Shiro + Redis custom session management

Keywords: Session Redis Shiro Java

Original blog address

This picture is very important!!! Town building

0x001 override SessionManager

shiro provides three default implementations:

  • DefaultSessionManager: for java se environment
  • ServletContainerSessionManager: implementation used by default, Servlet container management
  • DefaultWebSessionManager: self maintenance

Overriding SessionManager requires inheriting the DefaultWebSessionManager class

Purpose: get token from Authorization in header

public class CustomSessionManager extends DefaultWebSessionManager {

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader("Authorization");
        if (StringUtils.isEmpty(id)) {
            // Get sessionId, id can be customized
            return super.getSessionId(request, response);
        } else {
            //Return sessionId; fixed routine
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        }
    }
}

0x002 rewrite SessionDao

Overriding SessionDAO requires implementing the SessionDAO interface

You can also inherit the following subclasses:

Inherits AbstractSessionDAO and implements the basic implementation

ps:

Attention!!!!!!!

Be sure to convert the session to binary and save it in redis with redistemplate. If you directly use the object serializer of redistemplate, there will be an error!!!!!

@Component
public class SelfSessionDao extends AbstractSessionDAO {

    @Resource(name = "SessionRedisTemplate")
    private RedisTemplate<Object, byte[]> redisTemplate;


    // Create session and save to redis
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = generateSessionId(session);
        assignSessionId(session, sessionId);
        redisTemplate.boundValueOps(AppConstants.SHIRO_SESSION_REDIS_PREFIX + sessionId)
                .set(sessionToByte(session), AppConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);
        return sessionId;
    }


    @Override
    protected Session doReadSession(Serializable sessionId) {
        byte[] bytes = redisTemplate.boundValueOps(AppConstants.SHIRO_SESSION_REDIS_PREFIX + sessionId).get();
        return byteToSession(bytes);
    }

    /**
     * To update
     * @param session
     * @throws UnknownSessionException
     */
    @Override
    public void update(Session session) throws UnknownSessionException {
        if (session == null || session.getId() == null) {
            throw new UnknownSessionException("session perhaps sessionId Empty");
        }

        redisTemplate.boundValueOps(AppConstants.SHIRO_SESSION_REDIS_PREFIX + session.getId())
                .set(sessionToByte(session), AppConstants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);
    }

    /**
     * Delete session
     * @param session
     */
    @Override
    public void delete(Session session) {
        redisTemplate.delete(AppConstants.SHIRO_SESSION_REDIS_PREFIX + session.getId());

    }

    @Override
    public Collection<Session> getActiveSessions() {
        Set<Object> keys = redisTemplate.keys(AppConstants.SHIRO_SESSION_REDIS_PREFIX + "*");
        if (keys != null) {
            return keys.stream().map(key -> {
                byte[] bytes = redisTemplate.boundValueOps(key).get();
                return byteToSession(bytes);
            }).collect(Collectors.toList());
        } else {
            return null;
        }
    }

    // Convert session object to byte and save it in redis
    public byte[] sessionToByte(Session session){
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        byte[] bytes = null;
        try {
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(session);
            bytes = bo.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }

    // Restore byte to session
    public Session byteToSession(byte[] bytes){
        ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
        ObjectInputStream in;
        SimpleSession session = null;
        try {
            in = new ObjectInputStream(bi);
            session = (SimpleSession) in.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }

        return session;
    }
}

0x003 custom id generator

public class CustomSessionIdGenerator implements SessionIdGenerator {

    @Override
    public Serializable generateId(Session session) {
        //... generate id logic
        return id;
    }
}

0x004 configuration class

@Bean
    public SecurityManager getSecurityManager(CustomRealm realm,DefaultWebSessionManager defaultWebSessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);

        //Register a custom session manager with the Security Manager
        securityManager.setSessionManager(defaultWebSessionManager);
        //Register the customized redis cache manager to the Security Manager
        securityManager.setCacheManager(selfCacheManager());

        return securityManager;
    }
//Session manager
@Bean
    public DefaultWebSessionManager sessionManager(SelfSessionDao selfSessionDao) {
        CustomSessionManager sessionManager = new CustomSessionManager();
        // Custom sessionDAO
        sessionManager.setSessionDAO(selfSessionDao);
        // Custom id generator
		selfSessionDao.setSessionIdGenerator(new CustomSessionIdGenerator());
        return sessionManager;
    }
Published 5 original articles, praised 0, visited 40
Private letter follow

Posted by mhoward on Wed, 29 Jan 2020 09:19:02 -0800