Master Shiro Security: From Basics to CAS SSO Integration with SpringBoot

This comprehensive guide walks you through Shiro's core concepts, authentication and authorization mechanisms, various realm implementations, session management, distributed session handling with Redis, caching strategies, and step‑by‑step integration of CAS single sign‑on using SpringBoot and pac4j, complete with code samples.

Programmer Null's Self-Cultivation
Programmer Null's Self-Cultivation
Programmer Null's Self-Cultivation
Master Shiro Security: From Basics to CAS SSO Integration with SpringBoot

1. Existing Problems

Authentication (login) requires repetitive business‑code implementation; coarse‑grained authorization is easy but fine‑grained control is cumbersome; distributed session management needs manual Redis integration; single sign‑on (SSO) is not covered.

2. Shiro Framework Introduction

Shiro is a Java security library whose core features are authentication and authorization. Official site: http://shiro.apache.org.

Shiro architecture diagram
Shiro architecture diagram

3. Basic Usage

3.1 SimpleAccountRealm

Authentication flow:

Authentication flow
Authentication flow

Authorization flow:

Authorization flow
Authorization flow
@Test
public void authen() {
    // 1. Prepare Realm with in‑memory user
    SimpleAccountRealm realm = new SimpleAccountRealm();
    realm.addAccount("admin","admin","超级管理员","商家");

    // 2. Build SecurityManager
    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    securityManager.setRealm(realm);

    // 3. Bind SecurityManager
    SecurityUtils.setSecurityManager(securityManager);

    // 4. Obtain Subject
    Subject subject = SecurityUtils.getSubject();

    // 5. Perform authentication
    subject.login(new UsernamePasswordToken("admin","admin"));
    System.out.println(subject.isAuthenticated());

    // 6. Role and permission checks
    System.out.println("是否拥有超级管理员角色:" + subject.hasRole("超级管理员"));
    subject.checkRole("商家");
    System.out.println(subject.isPermitted("user:add"));
    subject.checkPermission("user:delete");
}

3.2 IniRealm

Configuration stored in an .ini file:

[users]
admin=admin,超级管理员,运营
[roles]
超级管理员=user:add,user:update,user:delete
@Test
public void authen() {
    IniRealm realm = new IniRealm("classpath:shiro.ini");
    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    securityManager.setRealm(realm);
    SecurityUtils.setSecurityManager(securityManager);
    Subject subject = SecurityUtils.getSubject();
    subject.login(new UsernamePasswordToken("admin","admin"));
    System.out.println(subject.hasRole("超级管理员"));
    subject.checkRole("运营");
    System.out.println(subject.isPermitted("user:update"));
    subject.checkPermission("user:delete");
}

3.3 JdbcRealm

Database tables for users, roles, permissions (five classic tables) and example SQL for table creation and data insertion.

@Test
public void authen() {
    JdbcRealm realm = new JdbcRealm();
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql:///shiro");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    realm.setDataSource(dataSource);
    realm.setPermissionsLookupEnabled(true);

    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    securityManager.setRealm(realm);
    SecurityUtils.setSecurityManager(securityManager);
    Subject subject = SecurityUtils.getSubject();
    subject.login(new UsernamePasswordToken("admin","admin"));
    System.out.println(subject.hasRole("超级管理员"));
    System.out.println(subject.isPermitted("user:add"));
}

4. Shiro Web Integration

4.1 SSM (Spring MVC) Approach

Configure Shiro filter, realm, and security manager in web.xml and Spring XML, define filter chain for login, anonymous resources, and authenticated paths.

4.2 SpringBoot Approach

@Configuration
public class ShiroConfig {
    @Bean
    public DefaultWebSecurityManager securityManager(ShiroRealm realm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(realm);
        return manager;
    }

    @Bean
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        Map<String,String> map = new LinkedHashMap<>();
        map.put("/login.html","anon");
        map.put("/user/**","anon");
        map.put("/**","authc");
        definition.addPathDefinitions(map);
        return definition;
    }
}

5. Authorization Methods

5.1 Filter Chain

filterChainDefinitionMap.put("/item/select","roles[超级管理员,运营]");
filterChainDefinitionMap.put("/item/delete","perms[item:delete,item:insert]");
Filter chain diagram
Filter chain diagram

5.2 Custom Filter (RolesOrAuthorizationFilter)

public class RolesOrAuthorizationFilter extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }
        for (String role : rolesArray) {
            if (subject.hasRole(role)) {
                return true;
            }
        }
        return false;
    }
}

5.3 Annotations

Use @RequiresRoles and @RequiresPermissions on controller methods; logical OR can be specified.

@GetMapping("/update")
@RequiresRoles({"超级管理员","运营"})
public String update() {
    return "item Update!!!";
}

6. Distributed Session Handling

6.1 Default Session Management

Shiro can use HttpSession or its own MemorySessionDAO.

6.2 Redis Session DAO

Implement RedisSessionDAO extending AbstractSessionDAO, store sessions in Redis with a "session:" prefix, and set expiration to 30 minutes.

@Component
public class RedisSessionDAO extends AbstractSessionDAO {
    @Resource
    private RedisTemplate redisTemplate;
    private final String SHIRO_SESSION = "session:";

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);
        assignSessionId(session, sessionId);
        redisTemplate.opsForValue().set(SHIRO_SESSION + sessionId, session, 30, TimeUnit.MINUTES);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        if (sessionId == null) return null;
        Session session = (Session) redisTemplate.opsForValue().get(SHIRO_SESSION + sessionId);
        if (session != null) {
            redisTemplate.expire(SHIRO_SESSION + sessionId, 30, TimeUnit.MINUTES);
        }
        return session;
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        if (session == null) return;
        redisTemplate.opsForValue().set(SHIRO_SESSION + session.getId(), session, 30, TimeUnit.MINUTES);
    }

    @Override
    public void delete(Session session) {
        if (session == null) return;
        redisTemplate.delete(SHIRO_SESSION + session.getId());
    }

    @Override
    public Collection<Session> getActiveSessions() {
        Set keys = redisTemplate.keys(SHIRO_SESSION + "*");
        Set<Session> sessionSet = new HashSet<>();
        for (Object key : keys) {
            Session s = (Session) redisTemplate.opsForValue().get(key);
            sessionSet.add(s);
        }
        return sessionSet;
    }
}

6.3 Optimized Session Retrieval

Extend DefaultWebSessionManager to first check the request attribute before hitting Redis, reducing I/O latency.

public class DefaultRedisWebSessionManager extends DefaultWebSessionManager {
    @Override
    protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
        Serializable sessionId = getSessionId(sessionKey);
        if (sessionKey instanceof WebSessionKey) {
            ServletRequest request = ((WebSessionKey) sessionKey).getServletRequest();
            Session session = (Session) request.getAttribute(sessionId + "");
            if (session != null) {
                return session;
            }
        }
        Session session = retrieveSessionFromDataSource(sessionId);
        if (session == null) {
            throw new UnknownSessionException("Could not find session with ID [" + sessionId + "]");
        }
        if (sessionKey instanceof WebSessionKey) {
            ((WebSessionKey) sessionKey).getServletRequest().setAttribute(sessionId + "", session);
        }
        return session;
    }
}

7. Authorization Cache

Implement RedisCache implementing org.apache.shiro.cache.Cache and RedisCacheManager to cache role and permission data for 15 minutes, reducing database load.

@Component
public class RedisCache<K,V> implements Cache<K,V> {
    @Autowired
    private RedisTemplate redisTemplate;
    private final String CACHE_PREFIX = "cache:";

    @Override
    public V get(K k) {
        V v = (V) redisTemplate.opsForValue().get(CACHE_PREFIX + k);
        if (v != null) {
            redisTemplate.expire(CACHE_PREFIX + k, 15, TimeUnit.MINUTES);
        }
        return v;
    }

    @Override
    public V put(K k, V v) {
        redisTemplate.opsForValue().set(CACHE_PREFIX + k, v, 15, TimeUnit.MINUTES);
        return v;
    }

    @Override
    public V remove(K k) {
        V v = (V) redisTemplate.opsForValue().get(CACHE_PREFIX + k);
        if (v != null) {
            redisTemplate.delete(CACHE_PREFIX + k);
        }
        return v;
    }

    @Override
    public void clear() {
        Set keys = redisTemplate.keys(CACHE_PREFIX + "*");
        redisTemplate.delete(keys);
    }
    // size, keys, values omitted for brevity
}
@Component
public class RedisCacheManager implements CacheManager {
    @Autowired
    private RedisCache redisCache;

    @Override
    public <K,V> Cache<K,V> getCache(String name) {
        return redisCache;
    }
}

8. CAS Single Sign‑On Integration

8.1 Overview

CAS provides centralized authentication (SSO). Two deployment models: centralized (CAS server) and decentralized (JWT). This guide uses the centralized model.

8.2 CAS Server Setup

Download CAS 4.1.10, configure HTTP support, modify Apereo‑10000002.json, HTTPSandIMAPS‑10000001.json, ticketGrantingTicketCookieGenerator.xml, warnCookieGenerator.xml, and deployerConfigContext.xml. Build with Maven war:war and deploy to Tomcat.

8.3 Shiro + pac4j + CAS

Use buji‑pac4j and pac4j‑cas libraries. Define a CasRealm extending Pac4jRealm, configure Pac4j Config with CasClient, set callback URL, and register Shiro filters: security, callback, and logout.

@Configuration
public class Pac4jConfig {
    @Value("${cas.server.url:http://localhost:8080/cas}")
    private String casServerUrl;
    @Value("${cas.project.url:http://localhost:81}")
    private String casProjectUrl;
    @Value("${cas.clientName:test}")
    private String clientName;

    @Bean
    public Config config(CasClient casClient) {
        return new Config(casClient);
    }

    @Bean
    public CasClient casClient(CasConfiguration casConfiguration) {
        CasClient client = new CasClient(casConfiguration);
        client.setCallbackUrl(casProjectUrl + "/callback?client_name=" + clientName);
        client.setName(clientName);
        return client;
    }

    @Bean
    public CasConfiguration casConfiguration() {
        CasConfiguration cfg = new CasConfiguration();
        cfg.setLoginUrl(casServerUrl + "/login");
        cfg.setProtocol(CasProtocol.CAS20);
        cfg.setPrefixUrl(casServerUrl + "/");
        cfg.setAcceptAnyProxy(true);
        return cfg;
    }
}

Shiro filter registration includes SecurityFilter, CallbackFilter, and LogoutFilter with appropriate URLs.

The guide provides end‑to‑end code, configuration, and diagrams to enable secure authentication, fine‑grained authorization, distributed session management, caching, and CAS SSO integration using Shiro.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaredisCASSpringBootShiro
Programmer Null's Self-Cultivation
Written by

Programmer Null's Self-Cultivation

Focused on backend development technologies and sharing knowledge from programming project experiences.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.