shiro中session的常见的问题

解决shiro中前后端session共享问题

添加shiro的配置类。

DefaultWebSessionManager 获取请求头中JSESSION ID的值

通过RedisSessionDao 从Redis中查询该值对应的key 如果存在则认为当前用户登录

 @Bean
    public DefaultWebSecurityManager sexcurityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm());
        securityManager.setCacheManager(cacheManager());
        // 设置session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

// 设置缓存管理器
    @Bean
    public CacheManager cacheManager() {
        RedisCacheManager cacheManager = new RedisCacheManager();
        cacheManager.setRedisManager(redisManager());
        return cacheManager;
    }

    @Bean
    public SessionManager sessionManager() {
        /*DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();*/
        MyWebSessionManager sessionManager = new MyWebSessionManager();
        // SessionDao用于操作session对象,再容器中把对象session进行crud
        sessionManager.setSessionDAO(sessionDAO());
        return sessionManager;
    }

    @Bean
    public SessionDAO sessionDAO() {
        // 该类会把对象session进行crud操作
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }

解决前端不支持cookie的效果

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

解决原理:

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

把sessionId放入请求头

@RestController
/**
 * @RestController是Spring4.0之后新增的注解。
 * 相当于@Controller+@ResponseBody合在一起的作用。
 */
@Api(tags ="登录接口类")
public class LoginController {

    @RequestMapping("/login")
    @ResponseBody
    public Result login(@RequestBody LoginVo loginVo){
        Subject subject = SecurityUtils.getSubject();

        // UsernamePasswordToken 用户名密码令牌
        /**
         * 具体的身份验证逻辑和配置可能因应用程序和使用的身份验证框架
         */
        UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(), loginVo.getPasword());
        try{
            subject.login(token);
            return new Result(200,"登录成功",subject.getSession().getId());
        } catch (Exception e){
            e.printStackTrace();
            return new Result(500,"登录失败",null);
        }
    }

}

添加前端登录方法

<template>
  <div>
    <el-form label-width="80px" :model="loginForm">
      <el-form-item label="账号:">
        <el-input v-model="loginForm.username"></el-input>
      </el-form-item>
      <el-form-item label="密码:">
        <el-input v-model="loginForm.pasword"></el-input>
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="login">创建</el-button>
        <el-button @click="resetForm('ruleForm')">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: "Login",
  // 定义数据
  data(){
    return{
      loginForm:{},
    }
  },
  methods:{
    login(){
      this.$http.post("http://localhost:8080/login",this.loginForm).then(result=>{
                if(result.data.code==200){
                  this.$message.success("登录成功");
                  sessionStorage.setItem("token",result.data.data);
                  this.$router.push("/product")
                /*console.log("对对对");*/
                }else{
                  this.$message.error("账号或密码错误");
                }
      })
    }
  }
}
</script>

<style scoped>

</style>

添加main.js文件

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

重写DefaultWebSessionManager的方法

public class MyWebSessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "token";
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    @Override
    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);
        }
    }

}

修改shiro配置类

@Bean
    public SessionManager sessionManager() {
        /*DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();*/
        MyWebSessionManager sessionManager = new MyWebSessionManager();
        // SessionDao用于操作session对象,再容器中把对象session进行crud
        sessionManager.setSessionDAO(sessionDAO());
        return sessionManager;
    }

shiroFilter过滤器

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

OPTIONS请求:先头部队。

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

package com.aaa.filter;

import com.aaa.vo.Result;
import com.alibaba.fastjson.JSON;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;

/**
 * @ClassName: LoginFilter
 * @Description: TODO
 * @date: 2023/7/11 16:33
 * @author: zzj
 * @Version: 1.0
 */
public class LoginFilter extends FormAuthenticationFilter {

    //未登录访问资源时,会触发该方法。---默认内容是重定向到登录页面---重写改为返回json数据
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        response.setContentType("application/json;charset=utf-8"); //解决响应数据的中文乱码问题
        PrintWriter writer = response.getWriter();
        Result result=new Result(401,"请先登录2",null);
        //吧java对象转换为json字符串---JSON
        String jsonString = JSON.toJSONString(result);
        writer.print(jsonString);
        writer.flush();
        writer.close();
        return false;
    }

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

}

 如何设置前端前置路由守卫

在main.js文件中设置前端前置路由

前置路由守卫是在路由跳转前触发的钩子函数,主要用于登录验证

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

  • router.beforeEach((to,from,next) =>{
      var path = to.path;
      if (path == "/login"){
        return next();
      }
      // 判断是否登录过
      var token = sessionStorage.getItem("token");
      if (token){
        return next();
      }
      return next("/login");
    })

    如何防止恶意重复登录

获取当前登录用户的信息,账号退出

后端退出

 后端获取用户信息

 前端

<template>
    <div>
      这是商品的页面
    <el-button type="primary" @click="logout">退出</el-button>
    <el-button @click="info">获取用户信息</el-button>
      <span v-text="userinfo.username"></span>

  </div>

</template>

<script>
export default {
  name: "Product",
  data(){
    return{
      userinfo:{},
    }
  },

  created(){

  },
  methods:{
      info(){
        this.$http.get("http://localhost:8080/user/info").then(result=>{
          this.userinfo=result.data.data;
        })
      },
    logout(){
       this.$http.post("http://localhost:8080/logout").then(result=>{
         console.log(result)
        if (result.data.code==200){
          this.$message.success("退出成功");
          // 清空sessionStorage
          sessionStorage.clear();
          this.$router.push("/login")
        }
      })
    }
  }
}

</script>

<style scoped>

</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值