1.首先是jwt的相关maven依赖
io.jsonwebtoken
jjwt
0.9.1
org.springframework.boot
spring-boot-starter-security
2.2.4.RELEASE
2.JwtTokenUtil.java
package
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JwtToken生成的工具类
* JWT token的格式:header.payload.signature
* header的格式(算法、token的类型):
* {"alg": "HS512","typ": "JWT"}
* payload的格式(用户名、创建时间、生成时间):
* {"sub":"wang","created":1489079981393,"exp":1489684781}
* signature的生成算法:
* HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
*/
@Component
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
private static final String CLAIM_KEY_USERNAME = "sub";
// 用于获取token创建时间
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
/**
* 根据负责生成JWT的token
*/
private String generateToken(Map claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* 从token中获取JWT中的负载
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.info("JWT格式验证失败:{}", token);
}
return claims;
}
/**
* 生成token的过期时间
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 获取创建时间
*/
public Date getCreateDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return (Date) claims.get("created", Date.class);
}
/**
* 从token中获取登录用户名
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 验证token是否还有效
*
* @param token 客户端传入的token
* @param userDetails 从数据库中查询出来的用户信息
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* 判断token是否已经失效
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(DateUtil.localTime());
}
/**
* 从token中获取过期时间
*/
public Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails) {
Map claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, DateUtil.localTime());
return generateToken(claims);
}
/**
* 判断token是否可以被刷新
*/
public boolean canRefresh(String token) {
return !isTokenExpired(token);
}
/**
* 刷新token
*/
public String refreshToken(String token) {
Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, DateUtil.localTime());
return generateToken(claims);
}
}
3.涉及到的用户信息实体类-UserAuthDetails.java
package ;
import com.common.Constants;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* 用户权限管理
*/
public class UserAuthDetails implements UserDetails {
private Integer userId;
private String username;
private String password;
private Integer status;
public UserAuthDetails(Integer userId, String username, String password, Integer status) {
this.userId = userId;
this.username = username;
this.password = password;
this.status = status;
}
public Integer getUserId() {
return userId;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return status != Constants.USER_STATUS_DELETED;
}
@Override
public boolean isAccountNonLocked() {
return status != Constants.USER_STATUS_FORBIDDEN;
}
@Override
public boolean isCredentialsNonExpired() {
return status != Constants.USER_STATUS_INVALID;
}
@Override
public boolean isEnabled() {
return status == Constants.USER_STATUS_VALID;
}
}
4.JWT登录授权过滤器 JwtAuthenticationTokenFilter.java
package com.config;
import com.bo.UserAuthDetails;
import com.component.UserDataContextHolder;
import com.util.JwtTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* JWT登录授权过滤器
*/
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authToken;
String authHeader = request.getHeader(this.tokenHeader);
if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
// The part after "Bearer "
authToken = authHeader.substring(this.tokenHead.length());
} else {
authToken = request.getParameter("jwtToken");
}
//当token为空或格式错误时 直接放行
if (authToken == null) {
chain.doFilter(request, response);
return;
}
//获取用户信息
UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(authToken);
if(authenticationToken!=null){
UserAuthDetails userDetails = (UserAuthDetails) authenticationToken.getPrincipal();
UserDataContextHolder.setUserId(userDetails.getUserId());
UserDataContextHolder.setUsername(userDetails.getUsername());
}
//将authentication信息放到session里
//如果token有值但校验不通过,则需要清空上下文
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
/**
* token中获取用户信息
*/
private UsernamePasswordAuthenticationToken getAuthentication(String authToken) {
String username = jwtTokenUtil.getUserNameFromToken(authToken);
LOGGER.info("username authToken:{}", username);
if (username != null) {
UserAuthDetails userDetails = null;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
userDetails = (UserAuthDetails) this.userDetailsService.loadUserByUsername(username);
} else {
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();
userDetails = (UserAuthDetails) authentication.getPrincipal();
if (!username.equals(userDetails.getUsername())) {
userDetails = (UserAuthDetails) this.userDetailsService.loadUserByUsername(username);
}
}
//authToken是否过期,用户是否是同一个人
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}
}
return null;
}
}
5.保存存储用户session信息的实体类:UserDataContextHolder.java
package com.component;
/**
* 用于存储用户session信息
*/
public class UserDataContextHolder {
private static ThreadLocal userId = new ThreadLocal<>();
private static ThreadLocal username = new ThreadLocal<>();
private static ThreadLocal language = new ThreadLocal<>();
private static ThreadLocal timezone = new ThreadLocal<>();
private static ThreadLocal jwtToken = new ThreadLocal<>();
private static ThreadLocal basePath = new ThreadLocal<>();
private static ThreadLocal hostname = new ThreadLocal<>();
private static ThreadLocal device = new ThreadLocal<>();
public static Integer getUserId() {
return userId.get();
}
public static void setUserId(Integer userId) {
UserDataContextHolder.userId.set(userId);
}
public static String getUsername() {
return username.get();
}
public static void setUsername(String username) {
UserDataContextHolder.username.set(username);
}
public static String getLanguage() {
return language.get();
}
public static void setLanguage(String language) {
UserDataContextHolder.language.set(language);
}
public static String getTimezone() {
return timezone.get();
}
public static void setTimezone(String timezone) {
UserDataContextHolder.timezone.set(timezone);
}
public static String getJwtToken() {
return jwtToken.get();
}
public static void setJwtToken(String jwtToken) {
UserDataContextHolder.jwtToken.set(jwtToken);
}
public static String getBasePath() {
return basePath.get();
}
public static void setBasePath(String basePath) {
UserDataContextHolder.basePath.set(basePath);
}
public static String getHostname() {
return hostname.get();
}
public static void setHostname(String hostname) {
UserDataContextHolder.hostname.set(hostname);
}
public static String getDevice() {
return device.get();
}
public static void setDevice(String hostname) {
UserDataContextHolder.device.set(hostname);
}
}
6.jwt的yml文件配置
jwt:
tokenHeader: Authorization #JWT存储的请求头
secret: z26ER^SLE45G*SEFz$U!P4T #JWT加解密使用的密钥
expiration: 604800 #JWT的超期限时间(7*24*60*60)
tokenHead: Bearer #JWT负载中拿到开头
7.对应前端的request请求效果