JWT在專案中的簡單應用
JWT在專案中的簡單應用
JWT介紹
JWT(JSON WEB TOKEN):JSON網路令牌,JWT是一個輕便的安全跨平臺傳輸格式,定義了一個緊湊的自包含的方式在不同實體之間安全傳輸資訊(JSON格式)。它是在Web環境下兩個實體之間傳輸資料的一項標準。實際上傳輸的就是一個字串。廣義上講JWT是一個標準的名稱;狹義上JWT指的就是用來傳遞的那個token字串。
JWT的專案應用
引入依賴:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
定義兩個自定義註解,LoginToken表示登陸,可以跳過token驗證。CheckToken需要做token驗證。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoginToken {
boolean required() default true;
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckToken {
boolean required() default true;
}
定義生成token的JWT工具類,包含了token的生成、校驗、重新整理等。
/**
* @ClassName: JwtUtil
*/
public class JwtUtil {
//有效時間,如果有效時間小於20分鐘,重新整理token
public final static Integer checkDate = 20*60*1000;
public final static Integer validDate = 30*60*1000;
public final static String TOKEN = "token";
/**
* 使用者登入成功後生成Jwt
* 使用Hs256演算法 私匙使用使用者密碼
*
* @param ttlMillis jwt過期時間
* @param user 登入成功的user物件
* @return
*/
public static String createJWT(long ttlMillis, User user) {
//指定簽名的時候使用的簽名演算法,也就是header那部分,jjwt已經將這部分內容封裝好了。
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
//生成JWT的時間
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//建立payload的私有宣告(根據特定的業務需要新增,如果要拿這個做驗證,一般是需要和jwt的接收方提前溝通好驗證方式的)
Map<String, Object> claims = new HashMap<String, Object>();
claims.put("id", user.getId());
claims.put("username", user.getUsername());
claims.put("password", user.getPassword());
//生成簽名的時候使用的祕鑰secret,這個方法本地封裝了的,一般可以從本地配置檔案中讀取,切記這個祕鑰不能外露哦。它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了。
String key = user.getPassword();
//生成簽發人
String subject = user.getUsername();
//下面就是在為payload新增各種標準宣告和私有宣告瞭
//這裡其實就是new一個JwtBuilder,設定jwt的body
JwtBuilder builder = Jwts.builder()
//如果有私有宣告,一定要先設定這個自己建立的私有的宣告,這個是給builder的claim賦值,一旦寫在標準的宣告賦值之後,就是覆蓋了那些標準的宣告的
.setClaims(claims)
//設定jti(JWT ID):是JWT的唯一標識,根據業務需要,這個可以設定為一個不重複的值,主要用來作為一次性token,從而回避重放攻擊。
.setId(UUID.randomUUID().toString())
//iat: jwt的簽發時間
.setIssuedAt(now)
//代表這個JWT的主體,即它的所有人,這個是一個json格式的字串,可以存放什麼userid,roldid之類的,作為什麼使用者的唯一標誌。
.setSubject(subject)
//設定簽名使用的簽名演算法和簽名使用的祕鑰
.signWith(signatureAlgorithm, key);
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
//設定過期時間
builder.setExpiration(exp);
}
return builder.compact();
}
/**
* Token的解密
* @param token 加密後的token
* @param user 使用者的物件
* @return
*/
public static Claims parseJWT(String token, User user) {
//簽名祕鑰,和生成的簽名的祕鑰一模一樣
String key = user.getPassword();
//得到DefaultJwtParser
Claims claims = Jwts.parser()
//設定簽名的祕鑰
.setSigningKey(key)
//設定需要解析的jwt
.parseClaimsJws(token).getBody();
return claims;
}
/**
* 校驗token
* 在這裡可以使用官方的校驗,我這裡校驗的是token中攜帶的密碼於資料庫一致的話就校驗通過
* @param token
* @param user
* @return
*/
public static String isVerify(String token, User user) {
try {
//簽名祕鑰,和生成的簽名的祕鑰一模一樣
String key = user.getPassword();
//得到DefaultJwtParser
Claims claims = Jwts.parser()
//設定簽名的祕鑰
.setSigningKey(key)
//設定需要解析的jwt
.parseClaimsJws(token).getBody();
if (claims == null) {
return null;
}
if (claims.get("password").equals(user.getPassword())) {
return refreshJwt(token,claims,user);
}
return null;
} catch (ExpiredJwtException e) {
e.printStackTrace();
} catch (UnsupportedJwtException e) {
e.printStackTrace();
} catch (MalformedJwtException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return null;
}
/***
* 重新整理token,區分不同功能的配置
* @param claims
* @return
*/
private static String refreshJwt(String jwt, Claims claims,User user) {
Date exp = claims.getExpiration();
// 當 過期時間-當前時間(分)<配置時間 時重新整理
if ( exp.getTime()-System.currentTimeMillis() <= checkDate) {
String newjwt = createJWT(validDate, user);
if (newjwt != null) {
return newjwt;
}
}
return jwt;
}
}
定義攔截器:
/**
* @ClassName: AuthenticationInterceptor
* @Description: 攔截器
*/
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
// 從 http 請求頭中取出 token
String token = httpServletRequest.getHeader("token");
// 如果不是對映到方法直接通過
if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//檢查是否有LoginToken註釋,有則跳過認證
if (method.isAnnotationPresent(LoginToken.class)) {
LoginToken loginToken = method.getAnnotation(LoginToken.class);
if (loginToken.required()) {
return true;
}
}
//檢查有沒有需要使用者許可權的註解
if (method.isAnnotationPresent(CheckToken.class)) {
CheckToken checkToken = method.getAnnotation(CheckToken.class);
if (checkToken.required()) {
// 執行認證
if (token == null) {
throw new RuntimeException("無token,請重新登入");
}
// 獲取 token 中的 user id
String userId;
try {
userId = JWT.decode(token).getClaim("id").asString();
} catch (JWTDecodeException j) {
throw new RuntimeException("訪問異常!");
}
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("使用者不存在,請重新登入");
}
String jwt = JwtUtil.isVerify(token, user);
if (StringUtils.isEmpty(jwt)) {
throw new RuntimeException("非法訪問!");
}
httpServletResponse.setHeader(JwtUtil.TOKEN,jwt);
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
攔截器的配置:
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**"); // 攔截所有請求,通過判斷是否有 @LoginRequired 註解 決定是否需要登入
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
測試方法
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
private UserService userService;
//登入
@PostMapping("/login")
@LoginToken
public Object login(@RequestBody @Valid User user) {
JSONObject jsonObject = new JSONObject();
User userForBase = userService.findByUsername(user);
if (userForBase == null) {
jsonObject.put("message", "登入失敗,使用者不存在");
return jsonObject;
} else {
if (!userForBase.getPassword().equals(user.getPassword())) {
jsonObject.put("message", "登入失敗,密碼錯誤");
return jsonObject;
} else {
String token = JwtUtil.createJWT(JwtUtil.validDate, userForBase);
jsonObject.put("token", token);
jsonObject.put("user", userForBase);
return jsonObject;
}
}
}
//檢視個人資訊
@CheckToken
@GetMapping("/getMessage")
public String getMessage() {
return "你已通過驗證";
}
}
測試結果
登陸:
校驗:
關注公眾號免費領取學習資料~
相關文章
- 在 SpringBoot 專案中簡單實現 JWT 驗證Spring BootJWT
- 26.RSA加密解密在Java專案中的簡單應用加密解密Java
- Jenkins在Java web專案CI/CD中的簡單應用JenkinsJavaWeb
- JWT 在專案中的實際使用JWT
- webpack在PC專案中的應用Web
- 【zz】WBS在專案控制中的應用
- UI2 在專案中的應用UI
- 專案管理在HIS專案實施中的應用(轉)專案管理
- MVP在Android專案中的簡單體現MVPAndroid
- PFMEA在專案風險管理中的應用
- 專案管理在企業中的應用(轉)專案管理
- drf中jwt應用JWT
- 軟體專案管理在小軟體專案中的應用專案管理
- Cookie、Session、JWT在koa中的應用及實現原理CookieSessionJWT
- 專案管理軟體在企業中的應用專案管理
- Redis 在 Web 專案中的應用與實踐RedisWeb
- Redis在Web專案中的應用與實踐RedisWeb
- 甘特圖在六西格瑪專案中的應用
- Jaeger鏈路追蹤在專案中的應用
- 關於 fontawesome 庫在 Spartacus 專案中的應用
- 風險管理在施工專案管理中的應用(轉)專案管理
- Zustand:狀態持久化在專案中的應用持久化
- 軟體專案管理的研究及在專案開發中的應用專案管理
- jQuary中ajax的簡單應用
- 深入解析:JWT Bearer 認證在 .NET Core 中的應用JWT
- FMEA技術在IT專案風險管理中的應用
- Docker在PHP專案開發環境中的應用DockerPHP開發環境
- 物流理論在專案成本控制中的應用(轉)
- 整合中的小應用--檔案轉換或簡單的UIUI
- 在Java 11中建立一個簡單的模組化應用教程Java
- Java代理以及在Android中的一些簡單應用JavaAndroid
- 關於 index.ts 在大型 Angular 專案中的應用IndexAngular
- 六西格瑪管理在北京IT專案中的應用探討
- 常見演算法在實際專案中的應用演算法
- 價值工程在施工專案成本控制中的應用(轉)
- PetaPoco在.net專案中的簡單使用(儲存過程篇)儲存過程
- JWT簡單瞭解JWT
- simple-jwt的簡單使用JWT