Spring Security结合JWT的方法教程
|
概述 众所周知使用 JWT 做权限验证,相比 Session 的优点是,Session 需要占用大量服务器内存,并且在多服务器时就会涉及到共享 Session 问题,在手机等移动端访问时比较麻烦 而 JWT 无需存储在服务器,不占用服务器资源(也就是无状态的),用户在登录后拿到 Token 后,访问需要权限的请求时附上 Token(一般设置在Http请求头),JWT 不存在多服务器共享的问题,也没有手机移动端访问问题,若为了提高安全,可将 Token 与用户的 IP 地址绑定起来 前端流程 用户通过 AJAX 进行登录得到一个 Token 之后访问需要权限请求时附上 Token 进行访问
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="application/javascript">
var header = "";
function login() {
$.post("http://localhost:8080/auth/login",{
username: $("#username").val(),password: $("#password").val()
},function (data) {
console.log(data);
header = data;
})
}
function toUserPageBtn() {
$.ajax({
type: "get",url: "http://localhost:8080/userpage",beforeSend: function (request) {
request.setRequestHeader("Authorization",header);
},success: function (data) {
console.log(data);
}
});
}
</script>
</head>
<body>
<fieldset>
<legend>Please Login</legend>
<label>UserName</label><input type="text" id="username">
<label>Password</label><input type="text" id="password">
<input type="button" onclick="login()" value="Login">
</fieldset>
<button id="toUserPageBtn" onclick="toUserPageBtn()">访问UserPage</button>
</body>
</html>
后端流程(Spring Boot + Spring Security + JJWT) 思路:
编写用户实体类,并插入一条数据 User(用户)实体类
@Data
@Entity
public class User {
@Id
@GeneratedValue
private int id;
private String name;
private String password;
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
@JoinTable(name = "user_role",joinColumns = {@JoinColumn(name = "uid",referencedColumnName = "id")},inverseJoinColumns = {@JoinColumn(name = "rid",referencedColumnName = "id")})
private List<Role> roles;
}
Role(权限)实体类
@Data
@Entity
public class Role {
@Id
@GeneratedValue
private int id;
private String name;
@ManyToMany(mappedBy = "roles")
private List<User> users;
}
插入数据 User 表
Role 表
User_ROLE 表
Dao 层接口,通过用户名获取数据,返回值为 Java8 的 Optional 对象
public interface UserRepository extends Repository<User,Integer> {
Optional<User> findByName(String name);
}
编写 LoginDTO,用于与前端之间数据传输
@Data
public class LoginDTO implements Serializable {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
编写 Token 生成工具,利用 JJWT 库创建,一共三个方法:生成 Token(返回String)、解析 Token(返回Authentication认证对象)、验证 Token(返回布尔值)
@Component
public class JWTTokenUtils {
private final Logger log = LoggerFactory.getLogger(JWTTokenUtils.class);
private static final String AUTHORITIES_KEY = "auth";
private String secretKey; //签名密钥
private long tokenValidityInMilliseconds; //失效日期
private long tokenValidityInMillisecondsForRememberMe; //(记住我)失效日期
@PostConstruct
public void init() {
this.secretKey = "Linyuanmima";
int secondIn1day = 1000 * 60 * 60 * 24;
this.tokenValidityInMilliseconds = secondIn1day * 2L; this.tokenValidityInMillisecondsForRememberMe = secondIn1day * 7L;
}
private final static long EXPIRATIONTIME = 432_000_000;
//创建Token
public String createToken(Authentication authentication,Boolean rememberMe){
String authorities = authentication.getAuthorities().stream() //获取用户的权限字符串,如 USER,ADMIN
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
long now = (new Date()).getTime(); //获取当前时间戳
Date validity; //存放过期时间
if (rememberMe){
validity = new Date(now + this.tokenValidityInMilliseconds);
}else {
validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
}
return Jwts.builder() //创建Token令牌
.setSubject(authentication.getName()) //设置面向用户
.claim(AUTHORITIES_KEY,authorities) //添加权限属性
.setExpiration(validity) //设置失效时间
.signWith(SignatureAlgorithm.HS512,secretKey) //生成签名
.compact();
}
//获取用户权限
public Authentication getAuthentication(String token){
System.out.println("token:"+token);
Claims claims = Jwts.parser() //解析Token的payload
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) //获取用户权限字符串
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList()); //将元素转换为GrantedAuthority接口集合
User principal = new User(claims.getSubject(),"",authorities);
return new UsernamePasswordAuthenticationToken(principal,authorities);
}
//验证Token是否正确
public boolean validateToken(String token){
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); //通过密钥验证Token
return true;
}catch (SignatureException e) { //签名异常
log.info("Invalid JWT signature.");
log.trace("Invalid JWT signature trace: {}",e);
} catch (MalformedJwtException e) { //JWT格式错误
log.info("Invalid JWT token.");
log.trace("Invalid JWT token trace: {}",e);
} catch (ExpiredJwtException e) { //JWT过期
log.info("Expired JWT token.");
log.trace("Expired JWT token trace: {}",e);
} catch (UnsupportedJwtException e) { //不支持该JWT
log.info("Unsupported JWT token.");
log.trace("Unsupported JWT token trace: {}",e);
} catch (IllegalArgumentException e) { //参数错误异常
log.info("JWT token compact of handler are invalid.");
log.trace("JWT token compact of handler are invalid trace: {}",e);
}
return false;
}
}
实现 UserDetails 接口,代表用户实体类,在我们的 User 对象上在进行包装,包含了权限等性质,可以供 Spring Security 使用
public class MyUserDetails implements UserDetails{
private User user;
public MyUserDetails(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<Role> roles = user.getRoles();
List<GrantedAuthority> authorities = new ArrayList<>();
StringBuilder sb = new StringBuilder();
if (roles.size()>=1){
for (Role role : roles){
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
return AuthorityUtils.commaSeparatedStringToAuthorityList("");
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
(编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
