当前栏目:java世界
shiro与token无状态单点登录

2023-02-12
269
shiro与token无状态单点登录
1.session 的运作通过一个session_id来进行。session_id通常是存放在客户端的 cookie 中,众所周知,当客户端请求成功,服务端写入session数据,向客户端浏览器返回sessionid,浏览器将sessionid保存在cookie中,当用户再次访问服务器时,会携带sessionid,服务器会拿着sessionid从服务器获取session数据,然后进行用户信息查询,查询到,就会将查询到的用户信息返回,从而实现状态保持。session是基于cookie进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
2.token :json web token ,由头部,载荷,签名三部分组成,无状态的,可以在多个服务间共享,安全性较高
步入正题:
1.shiroConfig
@Configuration
@Slf4j
public class ShiorConfig2 {
/**
* 注入自定义的 Realm
* @return MyRealm
*/
@Bean
public MyTokenRealm myAuthRealm() {
MyTokenRealm myRealm = new MyTokenRealm();
log.info("=============myRealm2===========================");
return myRealm;
}
@Bean
public SecurityManager securityManager(MyTokenRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
//关闭shiro自带的session
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// 强制使用cglib,防止重复代理和可能引起代理出错的问题
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
// 定义 shiroFactoryBean
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
// 设置自定义的 securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置默认登录的 URL,身份认证失败会访问该 URL
shiroFilterFactoryBean.setLoginUrl("/login");
// 设置成功之后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/success");
// 设置未授权界面,权限认证失败会访问该 URL
shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");
// LinkedHashMap 是有序的,进行顺序拦截器配置
Map<String,String> filterChainMap = new LinkedHashMap<>();
// 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行
filterChainMap.put("/css/**", "anon");
filterChainMap.put("/imgs/**", "anon");
filterChainMap.put("/js/**", "anon");
filterChainMap.put("/swagger-*/**", "anon");
filterChainMap.put("/swagger-ui.html/**", "anon");
// 登录 URL 放行
filterChainMap.put("/login", "anon");
// 以“/user/admin” 开头的用户需要身份认证,authc 表示要进行身份认证
filterChainMap.put("/user/admin*", "roles[admin]");
// “/user/student” 开头的用户需要角色认证,是“admin”才允许
filterChainMap.put("/user/student*/**", "roles[admin]");
// “/user/teacher” 开头的用户需要权限认证,是“user:create”才允许
filterChainMap.put("/user/teacher*/**", "perms[\"user:create\"]");
// 配置 logout 过滤器
filterChainMap.put("/logout", "logout");
// 设置 shiroFilterFactoryBean 的 FilterChainDefinitionMap
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
logger.info("====shiroFilterFactoryBean注册完成====");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
2: tokenUtil 工具
/**
* 设置过期时间 1分钟
*/
private static final long EXPIRE_TIME=60 * 1000;
private static final String TOKEN_SECRET="abcd";
/**
* 产生token
* @param useName
* @param userId
* @return
*/
public static String Sign(String useName , long userId){
try{
//这里将useName 和 userId 存入了Token,在下面的解析中,也会有解析的方法可以获取到Token里面的数据
//Token过期的时间
Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
System.out.println("date"+date);
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头部信息
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
//附带username和userid信息,存储到token中,生成签名
return JWT.create()
.withHeader(header)
//存储自己想要留存给客户端浏览器的内容
.withClaim("userName",useName)
.withClaim("userId",userId)
.withExpiresAt(date)
.sign(algorithm);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* token校验是否正确
* @param token
* @return
*/
public static boolean verify(String token){
try{
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(token);
return true;
}catch (Exception e){
System.out.println("Token超时,需要重新登录");
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
3.自定义Realm
/**
* Author:XuDing
* Date:2020/4/26
*/
public class MyTokenRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//doGetAuthenticationInfo() 方法:用来验证当前登录的用户,获取认证信息。
//doGetAuthorizationInfo() 方法:为当前登录成功的用户授予权限和分配角色。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String userName = JWTUtil.getUsername(principalCollection.toString());
// 获取用户名
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
// 给该用户设置角色,角色信息存在 t_role 表中取
Set<String> roles = userService.getRoles(userName);
System.err.println("roles========>"+roles);
authorizationInfo.setRoles(roles);
// 给该用户设置权限,权限信息存在 t_permission 表中取
authorizationInfo.setStringPermissions(userService.getPermissions(userName));
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 根据 Token 获取用户名
// UsernamePasswordUsertypeToken token = (UsernamePasswordUsertypeToken) authcToken;
String userName = (String) authenticationToken.getPrincipal();
// 根据用户名从数据库中查询该用户
User user = userService.getByUsername(userName);
if(user !=null){
// 把当前用户存到 Session 中
SecurityUtils.getSubject().getSession().setAttribute("user",user);
// 传入用户名和密码进行身份认证,并返回认证信息
// AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), "myRealm");
// return authcInfo;
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
}else {
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
登录接口
/**
* 用户登录接口
* @param user user
* @param request request
* @return string
*/
@GetMapping("/login")
public ResponseResult login(User user, HttpServletRequest request) {
// 根据用户名和密码创建 Token
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
System.err.println("token=========>"+token);
// 获取 subject 认证主体
Subject subject = SecurityUtils.getSubject();
try{
PrincipalCollection principals = subject.getPrincipals();
System.out.println(principals);
// 开始认证,这一步会跳到我们自定义的 Realm 中
subject.login(token);
// String token2 = JWTUtil.createToken(user.getUserName());
String token2 = TokenSign.Sign(user.getUserName(), 5);
// request.getSession().setAttribute("user", user);
return ResponseResult.success(token2);
}catch(Exception e){
e.printStackTrace();
request.getSession().setAttribute("user", user);
request.setAttribute("error", "用户名或密码错误!");
return ResponseResult.error("失败");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
拦截器
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//首先从请求头中获取jwt串,与页面约定好存放jwt值的请求头属性名为User-Token
String jwt = httpServletRequest.getHeader("User-Token");
log.info("[登录校验拦截器]-从header中获取的jwt为:{}", jwt);
//判断jwt是否有效
if(StringUtils.isNotBlank(jwt)){
boolean verify1 = TokenSign.verify(jwt);
if(verify1){
return true;
}
}
return false;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
退出:
————————————————
版权声明:本文为CSDN博主「南方的烈阳.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45528177/article/details/108563573