我們接着上一章(五)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);
示例