JJWT是一個提供端到端的JWT創建和驗證的Java庫
依賴
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>RELEASE</version>
</dependency>
token的創建
setIssuedAt用於設置簽發時間
signWith用於設置簽名秘鑰
JwtBuilder builder = Jwts.builder().setId("111")
.setSubject("小明")
.setIssuedAt(newDate())
.signWith(SignatureAlgorithm.HS256, "ld");
String token = builder.compact();
token的解析
String token = "~~~";
Claims claims = Jwts.parser().setSigningKey("ld").parseClaimsJws(token).getBody();
System.out.println("id:" + claims.getId());
System.out.println("subject:" + claims.getSubject());
System.out.println("IssuedAt:" + claims.getIssuedAt());
token過期校驗
long now = System.currentTimeMillis(); //當前時間
long exp = now + 1000 * 60; //過期時間為1分鍾
JwtBuilder builder = Jwts.builder().setId("111")
.setSubject("小明")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "ld")
.setExpiration(new Date(exp));
當未過期時可以正常讀取
當過期時會引發 io.jsonwebtoken.ExpiredJwtException 異常
自定義claims
long now = System.currentTimeMillis(); //當前時間
long exp = now + 1000 * 60; //過期時間為1分鍾
JwtBuilder builder = Jwts.builder().setId("111")
.setSubject("小明")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "ld")
.setExpiration(new Date(exp))
.claim("role", "admin");
獲取:
claims.get("role")
示例
JWT工具類
@Data
public class JwtUtil {
private String key; //密鑰加鹽
private long ttl; //過期時間
/**
* 生成JWT
*/
public String createJWT(String id, String subject, String role) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, key).claim("role", role);
if (ttl > 0) {
builder.setExpiration(new Date(nowMillis + ttl));
}
return builder.compact();
}
/**
* 解析JWT
*/
public Claims parseJWT(String jwtStr) {
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}
添加配置
jwt:
config:
key: littledonkey
ttl: 3600000
簽發token
//判斷是否密碼是否正確
Admin loginAdmin = adminService.login(admin);
if (loginAdmin == null) {
return new Result(false, StatusCode.LOGINERROR, "登陸失敗");
}
//簽發token
String token = jwtUtil.createJWT(admin.getId(), admin.getLoginname(), "admin");
HashMap<String, String> map = new HashMap<>();
map.put("token", token);
map.put("role", "admin");
return new Result(true, StatusCode.OK, "登陸成功", map);
使用攔截器方式實現token鑒權
org.springframework.web.servlet.handler.HandlerInterceptorAdapter
這個適配器,
繼承此類,可以非常方便的實現自己的攔截器。
三個方法分別實現預處理、后處理(調用了Service並返回ModelAndView,但未進行頁面渲染)、返回處理(已經渲染了頁面):
- 在preHandle中,可以進行編碼、安全控制等處理
- 在postHandle中,有機會修改ModelAndView
- 在afterCompletion中,可以根據ex是否為null判斷是否發生了異常,進行日志記錄
添加攔截器
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//獲取請求頭(如果有此請求頭,表示token已經簽發)
String header = request.getHeader("tokenHeader");
if (header != null || !"".equals(header)) {
//解析請求頭(防止偽造token,token內容以"token "作為開頭)
if (header.startsWith("token ")) {
try {
Claims claims = jwtUtil.parseJWT(header.substring(6));
String role = (String) claims.get("role");
//為具有相關權限的用戶添加權限到request域中
if ("admin".equals(role)) {
//拿到"admin_token"頭信息,表示當前角色是admin
request.setAttribute("admin_token", header.substring(6));
}
if ("user".equals(role)) {
//拿到"user_token"頭信息,表示當前角色是user
request.setAttribute("user_token", header.substring(6));
}
} catch (Exception e) {
throw new RuntimeException("令牌不正確");
}
}
}
//所有請求都通過,具體權限在service層判斷
return true;
}
}
注冊攔截器
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private TokenInterceptor tokenInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login/**");
}
}
service層驗證
public void deleteById(String id) {
String admin_token = (String) request.getAttribute("admin_token");
if(admin_token == null || "".equals(admin_token)){
throw new RuntimeException("權限不足");
}
adminDao.deleteById(id);
}