什麼是JWT?
Json web token (JWT), 是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準((RFC 7519).該token被設計爲緊湊且安全的, 特別適用於分佈式站點的單點登陸(SSO)場景。JWT的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息, 以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於認證,也可被加密。javascript
JWT是由三段信息構成的,將這三段信息文本用.連接一塊兒就構成了Jwt字符串。就像這樣:css
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLnlKjmiLciLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlzcyI6IuetvuWPkeiAhSIsImlkIjoic2xtMTIzIiwiZXhwIjoxNTU1MDQ5NzI1LCJ1c2VybmFtZSI6InNsbSJ9.x5ZoTW5NS4wBCIK61v4YCGi8bsveifBwnsMNBQz8s_s
JWT是由什麼構成的?
JWT共有三部分組成,第一部分稱之爲頭部(header),第二部分稱之爲荷載(payload),第三部分是簽名(signature)。html
header:java
{ 'typ': 'JWT',//聲明類型,這裏是jwt 'alg': 'HS256'}
typ:聲明瞭類型web
alg:聲明瞭加密方式算法
header通過Base64加密後:json
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Payload:安全
Payload 部分也是一個 JSON 對象,用來存放實際須要傳遞的數據。JWT 規定了7個官方字段,供選用服務器
iss (issuer):簽發人exp (expiration time):過時時間sub (subject):主題aud (audience):受衆nbf (Not Before):生效時間iat (Issued At):簽發時間jti (JWT ID):編號
除了官方字段,你還能夠在這個部分定義私有字段,下面就是一個例子。微信
{ "sub": "123456", "name": "admin", "admin": true}
Payload通過Base64加密後:
eyJzdWIiOiLnlKjmiLciLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlzcyI6IuetvuWPkeiAhSIsImlkIjoic2xtMTIzIiwiZXhwIjoxNTU1MDQ5NzI1LCJ1c2VybmFtZSI6InNsbSJ9
JWT 默認是不加密的,任何人均可以讀到,因此不要把祕密信息放在這個部分。
Signature是對前兩部分進行的簽名防止數據被篡改
首先,須要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。而後,使用 Header 裏面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出簽名之後,把 Header、Payload、Signature 三個部分拼成一個字符串,每一個部分之間用"點"(.)分隔,就能夠返回給用戶。
怎麼使用JWT?
通常是在請求頭裏加入Authorization,並加上Bearer標註:
headers: { 'Authorization': 'Bearer ' + token }
JWT如何應用在Springboot中?
首先引入pom依賴
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
實現WebMvcConfigurer接口配置攔截全部URl
/** * @Author: slm * @CreateTime: 2019-04-11 10:07 * @Description: 攔截設置 */public class InterceptorConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**"); } public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); }}
我在項目中使用了全局異常處理和加入了兩個自定義註解
LoginToken:表明須要對接口進行驗證
PassToken:表明不須要進行
建立AuthenticationInterceptor實現HandlerInterceptor接口對請求進行攔截
public class AuthenticationInterceptor implements HandlerInterceptor { private UserService userService;
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { String token = httpServletRequest.getHeader("token");// 從 http 請求頭中取出 token // 若是不是映射到方法直接經過 if (!(object instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); //檢查是否有passtoken註釋,有則跳過認證 if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } //檢查有沒有須要用戶權限的註解 if (method.isAnnotationPresent(LoginToken.class)) { LoginToken userLoginToken = method.getAnnotation(LoginToken.class); if (userLoginToken.required()) { // 執行認證 if (token == null) { throw new AppException("9999","無token,請從新登陸"); } // 獲取 token 中的 user id Boolean userId; try { userId = JWTUtil.verifyToken(token,"123456"); } catch (JWTDecodeException j) { throw new AppException("401","無權操做"); } if(userId){ return true; }else { throw new AppException("401","無權操做"); } } } return true; }
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { }}
配置完成之後簡單模擬一下用戶數據:
建立JWTUtil類對token進行生成和校驗
4jpublic class JWTUtil { private static final String EXP = "exp";
private static final String PAYLOAD = "payload";
/** * 加密生成token * * @param object 載體信息 * @param maxAge 有效時長 * @param secret 服務器私鑰 * @param <T> * @return */ public static String createToken(User object, long maxAge, String secret) { try { final Algorithm signer = Algorithm.HMAC256(secret);//生成簽名 String token = JWT.create() .withIssuer("簽發者") .withSubject("用戶")//主題,科目 .withClaim("username", object.getUsername()) .withClaim("id", object.getId()) .withClaim("password",object.getPassword()) .withExpiresAt(new Date(System.currentTimeMillis() + maxAge)) .sign(signer); System.out.println(token); return Base64.getEncoder().encodeToString(token.getBytes("utf-8")); } catch (Exception e) { log.error("生成token異常:", e); return null; } }
/** * 解析驗證token * * @param token 加密後的token字符串 * @param secret 服務器私鑰 * @return */ public static Boolean verifyToken(String token, String secret) { try { Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm).build(); DecodedJWT jwt = verifier.verify(new String(Base64.getDecoder().decode(token),"utf-8")); return true; } catch (IllegalArgumentException e) { throw new AppException("9999",e.getMessage()); } catch (JWTVerificationException e) { throw new AppException("9999",e.getMessage()); } catch (UnsupportedEncodingException e) { throw new AppException("9999",e.getMessage()); } }}
建立接口調用接口
注意:登陸接口加入了PassToken表示不進行校驗
最後啓動項目,調用登陸接口
調用getMsg進行驗證
登陸成功
若是你感受文章對你有幫忙就點點關注或者點一下在看
本文分享自微信公衆號 - 亂敲代碼(lqcoder)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。