springboot project front-end and back-end separately use shiro to solve different sessionid problems

Posted by showman on Mon, 12 Aug 2019 07:43:04 +0200

Problems encountered

When developing app s or projects with separate front and back ends, sessions are different each time when using ajax and other ways to access the background, so using Shiro verification is totally impossible to pass. Originally, we used the way of token, which can realize the problem of authority judgment by validating token by ourselves, but we still need to use shiro. Rewrite your own permission judgment. Then how to achieve session ID invariance through token in shiro?
We solve this problem by configuring shiro's session manager and storing shiro's sessions in redis. However, different sessions accessed each time still need to be solved by transferring token. In fact, the token passed by the client is stored as session ID.

Solution code

The following code:
First add the jar package to the pom file

  <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
            <scope>compile</scope>
        </dependency>
                <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>2.8.24</version>
        </dependency>
             <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
            <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
            <scope>compile</scope>
        </dependency>

Adding redis configuration to the project is my own redis configuration item used in the project, when using the @Value annotation

 spring:
  redis:
    shiro: #Configure redis cache usage
      host: localhost
      port: 6379
      timeout: 10000
      password: 123456

Add shiro configuration class and add the following to implement custom management session

 
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {


    @Value("${spring.redis.shiro.host}")
    private String host="127.0.0.1";
    @Value("${spring.redis.shiro.port}")
    private int port=6379;
    @Value("${spring.redis.shiro.timeout}")
    private int timeout=100000;
    @Value("${spring.redis.shiro.password}")
    private String password = "";

 
  
 

    //Custom Session Manager
    @Bean("sessionManager")
    public DefaultWebSessionManager sessionManager() {
        MySessionManager mySessionManager = new MySessionManager();
        mySessionManager.setSessionDAO(new RedisSessionDAO());
        return mySessionManager;
    }

    /**
     * Configure Shiro redis Manager
     * <p>
     * The shiro-redis open source plug-in is used
     *
     * @return
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setExpire(1800);// Configure cache expiration time
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }
   
    /**
     * Inject Security Manager
     */
    @Bean("SecurityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao Layer implementation through redis
     * <p>
     * The shiro-redis open source plug-in is used
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }
}

Add the configuration classes for the custom management session as follows:

import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

/**
 * Modify the default to get session ID
 */
@Configuration
public class MySessionManager extends DefaultWebSessionManager {

    private Logger logger = LoggerFactory.getLogger(MySessionManager.class);


    private static final String TOKEN = "token";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(TOKEN);
        //If there is token in the request header, the value is session Id
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //Otherwise, take session Id from cookie by default rule
            return super.getSessionId(request, response);
        }
    }
}

In the configuration, the token passed by the client is used as session Id, and if no token is passed, the session ID in reques is used. If the token is obtained in the request header, the token is set to the session Id of the current request.
The variable private static final String TOKEN = token; is the token variable name in the client request header. By this way, the session ID of the same user request session ID can be kept unchanged.

Topics: Shiro Session Redis Apache