JWT(JSON Web Token)是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準。java
在這裏我的整理了一些資料,有須要的朋友能夠直接點擊領取。面試
22本Java架構師核心書籍spring
JWT具體長什麼樣呢?
JWT是由三段信息構成的,將這三段信息文本用.連接一塊兒就構成了JWT字符串。就像這樣:網絡
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
JWT的頭部承載兩部分信息:架構
完整的頭部就像下面這樣的JSON:app
{ 'typ': 'JWT', 'alg': 'HS256' }
使用base64加密,構成了第一部分。dom
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
載荷就是存放有效信息的地方,這些有效信息包含三個部分:
私有的聲明部分:私有聲明是提供者和消費者所共同定義的聲明,通常不建議存放敏感信息,由於base64是對稱解密的,意味着該部分信息能夠歸類爲明文信息。
定義一個payload:
{ "sub": "1234567890", "name": "John Doe", "admin": true }
而後將其進行base64加密,獲得Jwt的第二部分:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
jwt的第三部分是一個簽證信息,這個簽證信息由三部分組成:
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, '密鑰'); 加密以後,獲得signature簽名信息。 TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
jwt最終格式
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
secret用來進行jwt的簽發和jwt的驗證,因此,在任何場景都不該該流露出去。
<!--token--> <!-- jwt支持 --> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> </dependency>
注意靜態屬性的配置文件注入方式:
package com.neuq.common.util; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.TokenExpiredException; import com.neuq.common.exception.ApiException; import com.neuq.common.response.ResultInfo; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Date; /** * @Author: xiang * @Date: 2021/5/11 21:11 * <p> * JwtToken生成的工具類 * JWT token的格式:header.payload.signature * header的格式(算法、token的類型),默認:{"alg": "HS512","typ": "JWT"} * payload的格式 設置:(用戶信息、建立時間、生成時間) * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) */ @Component @ConfigurationProperties(prefix = "jwt") public class JWTUtils { //定義token返回頭部 public static String header; //token前綴 public static String tokenPrefix; //簽名密鑰 public static String secret; //有效期 public static long expireTime; //存進客戶端的token的key名 public static final String USER_LOGIN_TOKEN = "USER_LOGIN_TOKEN"; public void setHeader(String header) { JWTUtils.header = header; } public void setTokenPrefix(String tokenPrefix) { JWTUtils.tokenPrefix = tokenPrefix; } public void setSecret(String secret) { JWTUtils.secret = secret; } public void setExpireTime(int expireTimeInt) { JWTUtils.expireTime = expireTimeInt*1000L*60; } /** * 建立TOKEN * @param sub * @return */ public static String createToken(String sub){ return tokenPrefix + JWT.create() .withSubject(sub) .withExpiresAt(new Date(System.currentTimeMillis() + expireTime)) .sign(Algorithm.HMAC512(secret)); } /** * 驗證token * @param token */ public static String validateToken(String token){ try { return JWT.require(Algorithm.HMAC512(secret)) .build() .verify(token.replace(tokenPrefix, "")) .getSubject(); } catch (TokenExpiredException e){ throw new ApiException(ResultInfo.unauthorized("token已通過期")); } catch (Exception e){ throw new ApiException(ResultInfo.unauthorized("token驗證失敗")); } } /** * 檢查token是否須要更新 * @param token * @return */ public static boolean isNeedUpdate(String token){ //獲取token過時時間 Date expiresAt = null; try { expiresAt = JWT.require(Algorithm.HMAC512(secret)) .build() .verify(token.replace(tokenPrefix, "")) .getExpiresAt(); } catch (TokenExpiredException e){ return true; } catch (Exception e){ throw new ApiException(ResultInfo.unauthorized("token驗證失敗")); } //若是剩餘過時時間少於過時時常的通常時 須要更新 return (expiresAt.getTime()-System.currentTimeMillis()) < (expireTime>>1); } }
jwt: header: "Authorization" #token返回頭部 tokenPrefix: "Bearer " #token前綴 secret: "qwertyuiop7418520" #密鑰 expireTime: 1 #token有效時間 (分鐘) 建議一小時以上
@Override public Map<String,Object> login(User user) { //phone是除id外的惟一標誌 須要進行檢查 if (user.getPhone() == null || user.getPhone().equals("")) throw new ApiException("手機號不合法"); User selectUser = userDao.selectUserByPhone(user.getPhone()); if (selectUser == null) { //註冊用戶 int count = userDao.insertUser(user); if (count < 1) throw new ApiException(ResultInfo.serviceUnavailable("註冊異常")); } //將userId存入token中 String token = JWTUtils.createToken(selectUser.getUserId().toString()); Map<String,Object> map = new HashMap<>(); map.put("user",selectUser); map.put("token",token); return map; }
注意將token保存到Http 的 header
@GetMapping("/login") public ResultInfo login(User user, HttpServletResponse response) { Map<String, Object> map = userService.login(user); //將token存入Http的header中 response.setHeader(JWTUtils.USER_LOGIN_TOKEN, (String) map.get("token")); return ResultInfo.success((User)map.get("user")); }
/** * @Author: xiang * @Date: 2021/5/7 20:56 * <p> * 攔截器:驗證用戶是否登陸 */ public class UserLoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //http的header中得到token String token = request.getHeader(JWTUtils.USER_LOGIN_TOKEN); //token不存在 if (token == null || token.equals("")) throw new ApiException("請先登陸"); //驗證token String sub = JWTUtils.validateToken(token); if (sub == null || sub.equals("")) throw new ApiException(ResultInfo.unauthorized("token驗證失敗")); //更新token有效時間 (若是須要更新其實就是產生一個新的token) if (JWTUtils.isNeedUpdate(token)){ String newToken = JWTUtils.createToken(sub); response.setHeader(JWTUtils.USER_LOGIN_TOKEN,newToken); } return true; } }
@Configuration @ComponentScan(basePackages = "com.neuq.common") //全局異常處理類須要被掃描才能 public class WebMvcConfig implements WebMvcConfigurer { /** * 註冊自定義攔截器 * * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserLoginInterceptor()) .addPathPatterns("/user/**") .addPathPatterns("/userInfo/**") .excludePathPatterns("/user/login");//開放登陸路徑 } }
將token或者一個惟一標識UUID=UUID.randomUUID().toString()存進Cookie中(別存在Http的header中了),設置路徑爲整個項目根路徑/*;
每每以這個惟一標識爲key,用戶信息爲value緩存在服務器中!!!
感謝大佬能看到最後,以爲文章對你有幫助記得點個贊!