目录
2.4、重写DefaultWebSessionManager的方法
1、如何实现session共享
session默认存储再各自服务的内存中,我们可以采用让session统一存储再redis中来解决不共享问题。
疯狂的蛋糕的依赖。---提供了redis存储session的类。
1.1、修改shiro的配置类
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(myRealm());
//设置缓存管理器
securityManager.setCacheManager(redisCacheManager());
//session管理
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public SessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(sessionDAO());//sessionDao用于操作session对象,在容器中对对象session进行CRUD操作
return sessionManager;
}
@Bean
public SessionDAO sessionDAO(){
//该类会对对象session进行CRUD操作
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
return sessionDAO;
}
//redis缓存管理器
@Bean
public RedisCacheManager redisCacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
//redisCacheManager.setPrincipalIdFieldName("userId");
return redisCacheManager;
}
//redis管理器
@Bean
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
redisManager.setHost("192.168.232.166"+":6379");
redisManager.setDatabase(1);
return redisManager;
}
@Bean
public MyRealm myRealm(){
MyRealm myRealm=new MyRealm();
//设置密码加密器
myRealm.setCredentialsMatcher(credentialsMatcher());
return myRealm;
}
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5");
credentialsMatcher.setHashIterations(5);
return credentialsMatcher;
}
2、解决前端不支持cookie的效果
原因: 默认DefaultWebSessionManager它只接受Cookie中存储的JsessionId. 查询发现再redis中不存在对应的key.
客户发送请求时,再请求头中携带sessionId, 然后重写DefaultWebSessionManager中getSessionId()的方法。
解决:
1. 把sessionId放入请求头。
2. 重写getSessionId方法获取请求头中的sessionID
2.1、修改登录的接口
package com.lx.controller;
import com.lx.vo.LoginVo;
import com.lx.vo.Result;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @program: springboot-shiro
* @description:前后端分离
* @author:
* @create: 2023-07-10 16:51
**/
@Controller
/*@CrossOrigin
origins:允许哪些跨域访问该接口 allowedHeaders:允许携带哪些头信息的请求访问 methods:允许哪些请求方式跨域请求接口*/
public class LoginPlusController {
@PostMapping("/loginPlus")
@ResponseBody
public Result login(@RequestBody LoginVo loginVo) {
System.out.println(loginVo);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(), loginVo.getPassword());
try {
subject.login(token);
return new Result(200,"登录成功",subject.getSession().getId());//携带当前会话的id
} catch (Exception e) {
e.printStackTrace();
return new Result(500,"账号或密码错误",null);
}
}
}
2.2、修改前端登录办法
methods:{
submitForm(){
console.log(this.form)
this.$http.post("http://localhost:8080/loginPlus",this.loginForm).then(result=>{
if (result.data.code==200){
this.$message.success("登录成功")
sessionStorage.setItem("token",result.data.data())//存在sessionStorage中,浏览器关闭token就销毁了
this.$router.push("/product")//路由跳转
}else {
this.$message.error("账号或密码错误")
}
console.log(result)
})
}
2.3、修改前端main.js文件
//设置axios的请求拦截器
axios.interceptors.request.use(config=>{
//从sessionStorage中获取token值
var item = sessionStorage.getItem("token");
if (item){
config.headers.token=item;
}
return config;
})
2.4、重写DefaultWebSessionManager的方法
package com.lx.realm;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
/**
* @program: springboot-shiro
* @description:
* @author:
* @create: 2023-07-11 15:08
**/
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.5、修改shiro配置类
@Bean
public SessionManager sessionManager(){
//DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
MyWebSessionManager sessionManager = new MyWebSessionManager();//修改为我们自己定义的SessionManager类
sessionManager.setSessionDAO(sessionDAO());//sessionDao用于操作session对象,在容器中对对象session进行CRUD操作
return sessionManager;
}
2.6、修改shiroFilter过滤器
我们发现跨域请求,会发送两个请求:第一个OPTIONS请求,第二个请求是真实的请求。
OPTIONS请求:先头部队。
所以我们对OPTIONS请求都要放行
package com.lx.filter;
import com.alibaba.fastjson.JSON;
import com.lx.vo.Result;
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;
/**
* @program: springboot-shiro
* @description:
* @author:
* @create: 2023-07-11 16:48
**/
public class LoginFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
response.setContentType("appliation/json;charset=utf-8");
PrintWriter writer = response.getWriter();
Result result = new Result(401, "未登录", null);
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 request1 = (HttpServletRequest) request;
//获取请求方式
String method = request1.getMethod();
if ("OPTIONS".equals(method)){
return true;
}
return super.isAccessAllowed(request, response, mappedValue);
}
}