解决session共享问题&前端不支持cookie的效果

目录

1.如何解决session共享问题

2. 解决前端不支持cookie的效果

2.1 如何把sessionId放入请求头。

2.2 重写DefaultWebSessionManager的方法

 3.设置前端前置路由守卫

4.如何防止恶意重复登录

 5.退出登录接口

6.获取当前登录用户的信息

7.设定登录设备的个数


1.如何解决session共享问题

默认session存储再各自服务的内存中,可以让session统一存储再redis中。

疯狂的蛋糕的依赖。---提供了redis存储session的类。

<dependency>

        <groupId>org.crazycake</groupId>

        <artifactId>shiro-redis</artifactId>

        <version>3.3.1</version>

</dependency>

(1)修改shiro的配置类。  

 @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm());
        securityManager.setCacheManager(cacheManager());
        //设置session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    @Bean
   public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(setSessionDao());
        return sessionManager;
    }
    @Bean
    public SessionDAO setSessionDao(){
        //该类会对session进行插入到操作
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }
    @Bean
    public RedisManager redisManager(){
        RedisManager manager = new RedisManager();
        manager.setHost("192.168.214.129:6379");
        manager.setDatabase(1);
        return manager;
    }

 解决思路:

DefaulWrbSessionManager获取请求头中JSESSION的值,通过RedisSessionDao从redis中查询该值对应的key,如果存在则认为当前用户登录。

2. 解决前端不支持cookie的效果

当登陆成功,点击相应的操作会出现跨域问题。

 原因: 默认DefaultWebSessionManager它只接受Cookie中存储的JsessionId. 查询发现再redis中不存在对应的key.

 如何解决:

 客户发送请求时,再请求头中携带sessionId, 然后重写DefaultWebSessionManager中getSessionId()的方法。

思考:1. 如何把sessionId放入请求头。

        2. 重写getSessionId方法如何获取请求头的sessionID。

2.1 如何把sessionId放入请求头。

(1)修改登录的接口

@Controller
//@CrossOrigin
public class LoginController {
    @PostMapping("/login")
    @ResponseBody
    public Result login(@RequestBody LoginVo loginVo){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(),loginVo.getPassword());
        try {
            subject.login(token);
            //subject.getSession().getId()希望带当前会话id
            return new Result(200,"登陆成功",subject.getSession().getId());
        }catch (Exception e){
            e.printStackTrace();
            return new Result(500,"账号或密码错误",null);
        }
    }
}

注意:在登陆的时候会出现序列化报错!!!

 解决方法:在实体类实现Serializable接口


@Data
public class User implements Serializable {
    @TableId
    private Integer id;
    private String username;
    private String userpwd;
    private String sex;
    private String address;
    private String salt;
}
(2)修改前端登录方法 

 (3)修改main.js文件

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

2.2 重写DefaultWebSessionManager的方法

(1)自定义MyWebSessionManager配置类

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);
        }
    }
}

(2)修改shiro配置类

 (3)修改shiroFilter过滤器

我们发现跨域请求,会发送两个请求:第一个OPTIONS请求,第二个请求是真实的请求。

OPTIONS请求:先头部队。

所以我们对OPTIONS请求都要放行。

  @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest request1 = (HttpServletRequest) request;
        //获取请求方式
        String method = request1.getMethod();
        if ("OPTIONS".equals(method)){
            return true;
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }

 3.设置前端前置路由守卫

//设置前置路由守卫
router.beforeEach((to,from,next)=>{
  //to:到哪去  from:从哪来  next:下一站
  let path = to.path;//获取路由的路径
  if (path == "/login"){
    return next();
  }
  //判断是否定登陆过
  let token = sessionStorage.getItem("token");
  if (token){
    return next();
  }
  return next("/login");
})

4.如何防止恶意重复登录

 5.退出登录接口

(1)编辑退出接口

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

(2)编辑前端退出按钮

<el-button type="danger" @click="logout">退出登录</el-button>



logout(){
      this.$http.post("http://localhost:8080/logout").then(result=>{
        if (result.data.code == 200){
          this.$message.success("退出成功");
          //清空sessionStorage
          sessionStorage.clear();
          this.$router.push("/login")
        }else {
          this.$message.error("退出失败");
        }
      })

6.获取当前登录用户的信息

(1)编辑信息接口

 @GetMapping("/info")
    public Result info(){
        Subject subject = SecurityUtils.getSubject();
        Object principal = subject.getPrincipal();
        return new Result(200,"查询成功",principal);
    }

(2)编辑查看信息按钮

info(){
      this.$http.get("http://localhost:8080/info").then(result=>{
        this.userinfo = result.data.data;
        console.log(result.data.data.username)
      })
    }



    <el-button  type="info" @click="info()">获取用户信息</el-button>

7.设定登录设备的个数

(1)引入依赖

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

(2)配置 application.properties文件

#配置redis
spring.redis.host=192.168.214.129

(3)编写后端登录接口

 @Autowired
    private StringRedisTemplate redisTemplate;
    @PostMapping("/login")
    public Result login(@RequestBody LoginVo loginVo){
        Subject subject = SecurityUtils.getSubject();
        //判断当前用户是否登陆过
        if(subject.isAuthenticated()){
            return new Result(200,"登陆成功",subject.getSession().getId());
        }
        UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(),loginVo.getPassword());
        try {
            String key = "shiro:user:"+loginVo.getUsername();
            System.out.println(key+"===========================");
            ValueOperations<String, String> forValue = redisTemplate.opsForValue();
            int count = 0;
            String s = forValue.get(key);
            System.out.println(s+"------------------------------");
            if (s !=null){
                if (Integer.parseInt(s)>=1){
                    return new Result(400,"同时在线设备不能超过2台",subject.getSession().getId());
                }else {
                    count++;
                }
            }else {
                count = 0;
            }
            forValue.set(key,count+"");
            subject.login(token);

            //subject.getSession().getId()希望带当前会话id
            return new Result(200,"登陆成功",subject.getSession().getId());
        }catch (Exception e){
            e.printStackTrace();
            return new Result(500,"账号或密码错误",null);
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值