Session sharing problems and other common problems and solutions

Table of contents

1. The sharing problem of session in shiro+springboot

1.1 How to solve the session sharing problem

2. Solve the effect that the front end does not support cookies

2.1. How to put the sessionId into the request header

2.2. Override the method of DefaultWebSessionManager

3. Set the front-end pre-routing guard

4. How to prevent malicious repeated login

5. Exit

6. Get the information of the currently logged in user

7. Set the number of login devices



1. Springboot integrates session sharing in Shiro

Problem demo:

(1) Start the shiro-springboot cluster project

 

 two boots

(2) Modify the configuration of nginx

 (3) test

When accessing certain resources after successful login, a json prompt of not being logged in appears 

1.1 How to solve the session sharing problem

By default, the session is stored in the memory of each service, and the session can be stored in redis uniformly.

Crazy Cake Dependency. --- Provides a class for redis to store sessions.

Modify shiro's configuration class (ShiroConfig.java)

@Bean
    public DefaultWebSecurityManager securityManager() {
        // 创建默认的 Web 安全管理器
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置安全管理器使用的 Realm
        securityManager.setRealm(myRealm());
        securityManager.setCacheManager(redisCacheManager());
        //设置session管理器
        securityManager.setSessionManager(sessionManager());
        // 返回安全管理器
        return securityManager;
    }
    @Bean
    public SessionManager sessionManager(){  //session管理器
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //setSessionDAO用于操作session对象,在容器中对象session进行CRUD操作
        sessionManager.setSessionDAO(sessionDao());
        return sessionManager;
    }
    @Bean
    public SessionDAO sessionDao(){
        //该类会对对象session进行CRUD操作
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("192.168.75.129:6379");
        sessionDAO.setRedisManager(redisManager);
        return sessionDAO;
    }

Core: DefaultWebSessionManager gets the value of JSESSIONID in the request header,

          Query the key corresponding to this value from redis through RedisSessionDAO. If it exists, the current user is considered to be logged in

2. Solve the effect that the front end does not support cookies

Problem demo:

Background UserController added:

Front-end reference Vue scaffolding project

 Modification of front-end Login.vue  

 Front-end Product.vue modification  

 Test: login

 After logging in, click Permissions - Query, directly report an error - cross-domain

reason:

By default, the DefaultWebSessionManager only accepts the JsessionId stored in the Cookie. The query finds that there is no corresponding key in redis.

How to solve:

When the client sends a request, carry the sessionId in the request header, and then override the getSessionId() method in DefaultWebSessionManager.

Thinking: 1. How to put the sessionId into the request header.

        2. How to rewrite the getSessionId method to get the sessionID of the request header.

2.1. How to put the sessionId into the request header

Modify the login interface

Modify the front-end login method

Modify the main.js file  

//设置axios的请求拦截器
axios.interceptors.request.use(config=>{
  //从localStorage中获取token的值
  var item = localStorage.getItem("token");
  if (item){
    config.headers.token=item;
  }
  return config;
})

 Gray dependency deletion

2.2. Override the method of DefaultWebSessionManager

createMyWebSessionManager.java



import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;

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

public class MyWebSessionManager extends DefaultWebSessionManager {
    private static final String AUTHORIZATION = "token";
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //获取请求头中名称为token的内容
        String id = WebUtils.toHttp(request).getHeader("token");
        if (!StringUtils.isEmpty(id)) { //如果存在该token
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless request");
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //从cookie中获取sessionId.
            return super.getSessionId(request, response);
        }
    }
}

Modify shiro configuration class

Add in LoginFilter filter:

A cross-domain request is found here, and two requests will be sent: the first OPTIONS request, and the second request is a real request.

OPTIONS request: Vanguard.

So we have to release all OPTIONS requests

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest req = (HttpServletRequest) request;

        String method = req.getMethod();
        if("OPTIONS".equals(method)){
            return true;
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }

3. Set the front-end pre-routing guard

//设置前置路由守护-----登录才能访问里面的资源
router.beforeEach((to,from,next)=>{
  //to:到哪去  from:从哪来  next:下一站
  //获取路由的路径
  var  path = to.path;
  if (path == "/login"){
    return next();
  }
  //判断是否登录过
  var token = sessionStorage.getItem("token");
  if (token){
    return next();
  }
  return next("/login");
})

 Change localStorage to sessionStorage

 

4. How to prevent malicious repeated login

Add to:

5. Exit

Edit exit interface

 @PostMapping("/logout")
    public Result logout(){
        Subject subject = SecurityUtils.getSubject();
        //清空redis
        subject.logout();
        return new Result(200,"退出成功",null);
    }

Edit frontend exit button

6. Get the information of the currently logged in user

Edit query information interface

Edit frontend exit button

Click to get user name

7. Set the number of login devices

Demo: One account can only log in to two devices

increase dependence

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

configuration file

#配置redis
spring.redis.host=192.168.75.129
LoginController
@Controller
//默认*允许所有域都可以跨域访问该接口
/*@CrossOrigin*/
public class LoginController {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @PostMapping("/login")
    @ResponseBody
    public Result login(@RequestBody LoginVo loginVo) {
        // 获取当前主体对象
        Subject subject = SecurityUtils.getSubject();
        // 创建用户名密码令牌
        UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(), loginVo.getPassword());
        try {
            String key = "shiro:user:" + loginVo.getUsername();
            ValueOperations<String, String> foValue = redisTemplate.opsForValue();
            int count = 0;
            String c = foValue.get(key);
            if (c != null) {
                if (Integer.parseInt(c) >= 1) {
                    return new Result(400, "同时在线设备不能超过2台", subject.getSession().getId());
                } else {
                    count++;
                }
            } else {
                count = 0;
            }
            subject.login(token);
            foValue.set(key, count + "");
            return new Result(200, "登录成功~~~", subject.getSession().getId());
        } catch (Exception e) {
            e.printStackTrace();
            // 登录失败,重定向到登录页面
            return new Result(500, "登录失败", null);
        }
    }
}

Guess you like

Origin blog.csdn.net/WQGuang/article/details/131659400