目录
1.shiro+springboot中session的共享问题
1.1 如何解决session共享问题
2.1.如何把sessionId放入请求头
2.2.重写DefaultWebSessionManager的方法
3.设置前端前置路由守卫
4.如何防止恶意重复登录
5.退出
6.获取当前登录用户的信息
7.设定登录设备的个数
1.Springboot整合Shiro中session的共享问题
问题演示:
(1)启动shiro-springboot的集群项目
两个启动
(2)修改nginx的配置
(3)测试
登录成功后访问某些资源时,出现了未登录的json提示
1.1 如何解决session共享问题
默认session存储再各自服务的内存中,可以让session统一存储再redis中。
疯狂的蛋糕的依赖。---提供了redis存储session的类。
修改shiro的配置类(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; }
核心:DefaultWebSessionManager获取请求头中的JSESSIONID的值,
通过RedisSessionDAO从redis中查询该值对应的key,如果存在则认为当前用户登录
2. 解决前端不支持cookie的效果
问题演示:
后台UserController添加:
前端参考Vue脚手架工程
前端Login.vue修改
前端Product.vue修改
测试:登录
登录之后,点击权限-查询,直接报错--跨域
原因:
默认DefaultWebSessionManager它只接受Cookie中存储的JsessionId. 查询发现再redis中不存在对应的key.
如何解决:
客户发送请求时,在请求头中携带sessionId, 然后重写DefaultWebSessionManager中getSessionId()的方法。
思考:1. 如何把sessionId放入请求头。
2. 重写getSessionId方法如何获取请求头的sessionID。
2.1.如何把sessionId放入请求头
修改登录的接口
修改前端登录方法
修改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的方法
创建MyWebSessionManager .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); } } }
修改shiro配置类
在LoginFilter过滤器添加:
这里发现跨域请求,会发送两个请求:第一个OPTIONS请求,第二个请求是真实的请求。
OPTIONS请求:先头部队。
所以我们对OPTIONS请求都要放行
@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.设置前端前置路由守卫
//设置前置路由守护-----登录才能访问里面的资源 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"); })
把localStorage改为sessionStorage
4.如何防止恶意重复登录
添加:
5.退出
编辑退出接口
@PostMapping("/logout") public Result logout(){ Subject subject = SecurityUtils.getSubject(); //清空redis subject.logout(); return new Result(200,"退出成功",null); }
编辑前端退出按钮
6.获取当前登录用户的信息
编辑查询信息接口
编辑前端退出按钮
点击获取用户名字
7.设定登录设备的个数
演示:一个账号只能登录两个设备
加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
配置文件
#配置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); } } }