发布于2021-05-29 21:24 阅读(455) 评论(0) 点赞(5) 收藏(3)
项目需要需要使用jwt token来完成用户状态管理以及使用spring security完成用户权限管理,所以需要将二者配合使用
基础思路:
1:spring security关闭自带的session管理,不需要服务器错处用户信息
2:登录时调用security的用户认证模块
3:security认证成功后通过jwt生成token返回给客户,并且在token中存储其他信息(例如权限信息,也可以存储在redis中)
4:用户操作时通过jwt确认用户是否登录状态(token验证)、获取token携带的权限信息通过security管理权限判断是否有权限做某些事情
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(20) NOT NULL,
`user_pwd` varchar(60) DEFAULT NULL,
`enabled` TINYINT(1) DEFAULT '1',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
CREATE TABLE `sys_role` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`description` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4
CREATE TABLE `sys_role_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned NOT NULL,
`sys_role_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4
CREATE TABLE `sys_permission` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`description` varchar(200) DEFAULT NULL,
`url` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4
CREATE TABLE `sys_permission_role` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) unsigned NOT NULL,
`permission_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4
三张表分别为用户、角色、权限表用于基础的操作
(这里看个人习惯)
1:编写User类
@Entity
@Data
@ToString
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
private boolean enabled;
}
2:编写MyUserDetail类用户security中用户
package com.mysecurity.liuyf.pojo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class MyUserDetails implements UserDetails {
private User user;
private Collection<GrantedAuthority> roles;
public MyUserDetails(User user, Collection<GrantedAuthority> roles) {
this.user = user;
this.roles = roles;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
这里isAccountNonExpired(判断用户是否被锁定)等我没有用到,所以直接写死了,可根据自己的项目动态改变值
3:编写UserDetailService用于处理用户登录
public class UserDetailService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
User u=userService.findUserByName(name);
if (u==null){
return null;
}
MyUserDetails MyUserDetails=new MyUserDetails(u,getGrantedAuthority(u));
return MyUserDetails;
}
public List<GrantedAuthority> getGrantedAuthority(User user){
List<GrantedAuthority> list = new ArrayList<>();
List<String> roles=userService.findUserRoleByUserId(user.getId());
for (String role : roles) {
list.add(new SimpleGrantedAuthority("ROLE_" + role));
}
//加载权限表的东西进来 null
List<String> permission=userService.findUserPermissionByUserId(user.getId());
for (String per : permission) {
list.add(new SimpleGrantedAuthority(per));
}
return list;
}
}
这里就是普通的使用loadUserByUsername加用用户,通过security的认证管理器完成认证用于登录
这样security需要准备的基础就完成了(就是登录位置的准备),下边开始准备jwt的东西
1:准备jwt的工具类:
package com.mysecurity.liuyf.util.jwt;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.*;
@Component
@Data
public class JwtUtil {
// 密钥
private String secret="自己编一点随机的字符串";
//超时时间 用于相加过期时间 s为单位 1天(单位:s)=5184000
private long expire=3600;//3600
private String header="token";
// 签发者
private String issuer="自己的公司名字";
/*
* 生成 Token
*/
public String generateToken (String subject){
//Create the Signature SecretKey
final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
final byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(Base64.getEncoder().encodeToString(getSecret().getBytes()));
final Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
Map<String,Object> headerMap=new HashMap<String,Object>();
headerMap.put("alg", "HS256");
headerMap.put("typ", "JWT");
Date nowDate = new Date();
//过期时间
Date expireDate = new Date(nowDate.getTime() + expire * 1000);
return Jwts.builder()
.setHeaderParams(headerMap)
.setSubject(subject)
.setIssuedAt(nowDate)
//设置token过期时间
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS256, signingKey)
//设置签发人信息
.setIssuer(getIssuer())
//设置签发时间
.setIssuedAt(nowDate)
.compact();
}
/**
* 解析token
*
* @param token token
* @return
*/
public Claims parseToken(String token) {
final byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(Base64.getEncoder().encodeToString(getSecret().getBytes()));
Claims claims =claims = Jwts.parser().setSigningKey(apiKeySecretBytes).parseClaimsJws(token).getBody();
return claims;
}
/**
* 从令牌中获取用户名
* @param token
* @return
*/
public String getUsernameFromToken(String token) {
String username;
try {
Claims claims = parseToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 判断令牌是否过期
* @param token
* @return
*/
public Boolean isTokenExpired(String token) {
try {
Claims claims = parseToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return false;
}
}
/**
* 刷新令牌
* @param token
* @return
*/
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = parseToken(token);
claims.put("created", new Date());
refreshedToken = generateToken(claims.getSubject());
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
* 验证令牌
* @param token
* @param userDetails
* @return
*/
public Boolean validateToken(String token, UserDetails userDetails) {
String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
secret:自己随便编一段字符串:类似于"aHR0cHM6Ly9teSaaaaaaaaLm5ldCbbbbbbE4Njg"主要做随机加密的
issuer:签发者,写自己的名字或者公司的名字就好了
这样工具类就写好了,用于token得产生以及验证等操作
下一篇开始做最终代码
原文链接:https://blog.csdn.net/liuyuncd/article/details/117320536
作者:我睡觉爱打呼噜
链接:http://www.javaheidong.com/blog/article/207327/65fb61b7a13d8185c4b6/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!