我们接着上一章 (五)Spring Security基于数据库的权限授权,进行开发
一:创建JWT工具
- 导入依赖:
implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.0'
- 编写工具类
@Component public class JwtTokenUtil implements Serializable { private static final long serialVersionUID = -4397248422869805076L; @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private long expiration; @Value("${jwt.header}") private String header; /** * 获取token中的信息 * @param token 生成的token * @return 信息 */ public String getInfoFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } /** * 获取token的生成时间 * @param token 生成的token * @return token的生成时间 */ public Date getIssuedAtDateFromToken(String token) { return getClaimFromToken(token, Claims::getIssuedAt); } /** * 获取token的过期时间 * @param token 生成的token * @return token的过期时间 */ public Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } /** * 判断token是否过期 * @param token 生成的token * @return true:过期,false:失效 */ private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(Date.from(Instant.now())); } public <T> T getClaimFromToken(String token, Function<Claims,T> claimsResolver){ final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } private Claims getAllClaimsFromToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) { return (lastPasswordReset != null && created.before(lastPasswordReset)); } private Boolean ignoreTokenExpiration(String token) { // here you specify tokens, for that the expiration is ignored return false; } /** * 生成令牌 * @param userDetails * @return */ public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return doGenerateToken(claims, userDetails.getUsername()); } /** * 真正进行创建token的方法 * @param claims * @param subject * @return */ private String doGenerateToken(Map<String, Object> claims, String subject) { final Date createdDate = Date.from(Instant.now()); final Date expirationDate = calculateExpirationDate(createdDate); return Jwts.builder() .setClaims(claims) /* 自定义属性 */ .setSubject(subject) /* 该JWT所面向的用户 */ .setIssuedAt(createdDate) /* 设置发放的时间,类型为: Date*/ .setExpiration(expirationDate) /* 设置过期时间 类型为:Date */ .signWith(SignatureAlgorithm.HS512, secret) /* jwt签名算法和密钥 */ .compact(); /* 返回一个URL安全JWT字符串 */ } /* public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) { final Date created = getIssuedAtDateFromToken(token); return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) && (!isTokenExpired(token) || ignoreTokenExpiration(token)); } */ /** * 刷新token * @param token * @return */ public String refreshToken(String token) { final Date createdDate = Date.from(Instant.now()); final Date expirationDate = calculateExpirationDate(createdDate); final Claims claims = getAllClaimsFromToken(token); claims.setIssuedAt(createdDate); claims.setExpiration(expirationDate); return Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public Boolean validateToken(String token, UserDetails userDetails) { SecurityUser user = (SecurityUser) userDetails; final Date created = getIssuedAtDateFromToken(token); /* final Date expiration = getExpirationDateFromToken(token); 如果token存在,且token创建日期 > 最后修改密码的日期 则代表token有效*/ return (!isTokenExpired(token) /*&& !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate())*/ ); } /** * 生成过期时间 * @param createdDate 当前时间 * @return 返回到期时间 */ private Date calculateExpirationDate(Date createdDate) { return Date.from(Instant.ofEpochMilli(createdDate.toInstant().toEpochMilli()+expiration)); } }
二:登录成功生成一个token给客户端
@Component
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private static final Logger log = LoggerFactory.getLogger(LoginSuccessHandler.class);
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
// 会帮我们跳转到上一次请求的页面上
//super.onAuthenticationSuccess(request, response, authentication);
/* 生成token */
String token = jwtTokenUtil.generateToken((UserDetails) authentication.getPrincipal());
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
PrintWriter writer = response.getWriter();
writer.write(token);
writer.flush();
writer.close();
}
}
三:JWT 过滤器配置
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(JwtAuthorizationTokenFilter.class);
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.header}")
private String header;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String header = request.getHeader(this.header);
String authToken = null;
String username = null;
if(header != null && header.startsWith("Bearer")){ /* 检测字符串是否以指定的前缀开始 */
authToken = header.substring(6);
log.info("获取token:{}",authToken);
username = jwtTokenUtil.getInfoFromToken(authToken);
}
/* 如果token有效,就跳过过滤器验证 */
if(username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if(jwtTokenUtil.validateToken(authToken,userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); /* 增加额外数据 */
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
/* 没有token,就继续进行过滤器验证 */
filterChain.doFilter(request,response);
}
}
四:WebSecurityConfig配置
http.csrf().disable(); /* CSRF禁用,因为不使用session */
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); /* 禁用session */
/* 添加过滤器 */
http.addFilterBefore(jwtAuthorizationTokenFilter,UsernamePasswordAuthenticationFilter.class);
示例