JWT公司的主流Json Web Token 令牌 如何使用,取代session
JWT簡介:
JWT(JSON WEB TOKEN):JSON網路令牌,JWT是一個輕便的安全跨平臺傳輸格式,定義了一個緊湊的自包含的方式在不同實體之間安全傳輸資訊(JSON格式)。它是在Web環境下兩個實體之間傳輸資料的一項標準。實際上傳輸的就是一個字串。廣義上講JWT是一個標準的名稱;狹義上JWT指的就是用來傳遞的那個token字串
JWT公司官網:
https://jwt.io/
jwt的token結構:
JWT的資料結構以及簽發的過程
JWT由三部分構成:header(頭部)、payload(載荷)和signature(簽名)。
Header 頭部資訊:指定型別和演算法
Payload 荷載資訊:存放Claims宣告資訊
Signature 簽名:把前兩者對應的Json結構進行base64url編碼之後的字串拼接起來和金鑰放一起加密後的簽名
組成方式為: header.payload.signature
:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpc3MiOiLlvKDlvLoiLCJuYW1lIjoiNzg5IiwiZXhwIjoxNTI4MzY0MjU5LCJpYXQiOjE1MjgzNjMwNTl9.574koY-c9SqMNNzfvAWQuKEnimWeZAcoFQ5XudNWF3o
因為是拼接的所以base64很容易破解,所以你不要把密碼或敏感的資訊放入,這樣就算是被別人破解了你的荷載資訊他也只能看著,
你該如何去理解JWT幫你做些什麼?
結合業務場景,首先前端登入 後端通過使用者名稱與密碼驗證成功後,使用jwt生成 一個具備有效期的token令牌, 這個token裡面儲存著 token本身的結構,加上token的過期時間,和一個你自己定義的字串類似於加密 簽名所用的鹽,這個鹽只有你自己知道,這樣你的加密才是安全的,還可以包含一些自定義的使用者資訊放在荷載資訊裡,
本文需要你具備javaSE的基礎, 所涉及的jar包如下:
<!-- JWT javawebToken--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <!-- 阿里巴巴json --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.4</version> </dependency>
jwt 如何生成Token呢?
注意:這裡使用的阿里巴巴提供的json工具包,實現序列化,後續會貼上所用的jar
/** * token 工具類 * @author 郎俊楠 * */ public class JwtUtil { private static Logger logger = Logger.getLogger(JwtUtil.class); public static final String TOKEN_HEADER = "Authorization";//token的key 也是名 不要寫成token這樣,要按照規範來 public static final String TOKEN_PREFIX = "Bearer ";//token值的字首,這是一種規範 ok private static final String SECRET = "mrLang";//你自己定的字串 別讓別人知道,加密時候用 鹽 public static final String FUNCTS = "FUNCTS";//獲取使用者的功能使用的key public static final String USERINFO = "USER";//獲取使用者使用的key private static final long EXPIRATION = 1800L;// token的生命週期30分 /** * 建立token令牌 以下為引數都是自定義資訊 * @param loginName 一般我們放使用者的唯一標識登入名 * @param functs 當前使用者的功能集合, 本人的rbac許可權比較個性化且很負責,一般你們放role角色就可以了 * @param user 當前使用者 * @return */ public static String createToken(String loginName, List<Object> functs, Users user) { Map<String, Object> map = new HashMap<>(); //當前使用者擁有的功能 map.put(FUNCTS, JsonUtil.set(functs)); //當前使用者資訊 map.put(USERINFO, JsonUtil.set(user)); String token = Jwts.builder() .setSubject(loginName)//主題 主角是誰? 賦值登入名 .setClaims(map) .setIssuedAt(new Date())//設定釋出時間,也是生成時間 .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))//設定過期時間 .signWith(SignatureAlgorithm.HS256, SECRET)//設定HS256加密,並且把你的鹽 放裡,這裡推薦使用SH256證書加密 .compact();//建立完成 return token; }
jwt 如何驗證Token是否過期呢?
// token是否過期 public static boolean isExpiration(String token) { try { return getTokenBody(token).getExpiration().before(new Date()); } catch (Exception e) { return true; } }
jwt 如何獲取自定義的資訊呢?
// 獲取主角,登入名 public static String getUserName(String token) { return getTokenBody(token).getSubject(); } // 獲取token中儲存的功能 public static List<Object> getUserFuncts(String token) { String str = getTokenBody(token).get(FUNCTS).toString(); List<Object> list = JsonUtil.getArray(str); return list; } // 獲取token儲存的使用者 public static Object getUser(String token) { String str = getTokenBody(token).get(USERINFO).toString(); return JsonUtil.getObj(str); } // 公共獲取自定義資料 public static Claims getTokenBody(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); }
jwt 如何重新整理Token呢?
// 重新整理token public static String refreshToken(String token) { if (isExpiration(token)) { logger.info("token重新整理失敗!! 過期了!!"); return null; } // 獲取使用者 許可權資訊 String functs = getTokenBody(token).get(FUNCTS).toString(); String user = getTokenBody(token).get(USERINFO).toString(); String username = getTokenBody(token).getSubject(); Map<String, Object> map = new HashMap<>(); map.put(FUNCTS, JsonUtil.set(functs)); map.put(USERINFO, JsonUtil.set(user)); token = Jwts.builder().signWith(SignatureAlgorithm.HS256, SECRET).setClaims(map).setSubject(username) .setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000)) .compact(); return token; }
token 如何傳送給前端?.
傳入 當前使用者的 功能與使用者資訊,登入名 生成token,寫入response的返回頭中,前端獲取後儲存在前端的本地快取中,後續前端請求要把token放在 頭header裡
//登入成功之後 List<Object> functs=(List<Object>) authResult.getAuthorities();//當前功能列表 String loginName=authResult.getName();//登入名 Users obj=(Users)authResult.getPrincipal();//使用者資訊 String token=JwtUtil.createToken(loginName,functs,obj);//生成token //TOKEN_HEADER= Authorization TOKEN_PREFIX=Bearer token值 response.setHeader(JwtUtil.TOKEN_HEADER,JwtUtil.TOKEN_PREFIX+token); response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); //個人編寫的檢視物件 DTO dto=new DTO<>(); dto.setCode("000000"); dto.setMessage("認證通過"); PrintWriter pw=response.getWriter(); pw.write(JsonUtil.set(dto));//寫入json pw.flush();//強制重新整理 pw.close();//關閉流
使用者請求攜帶token如何驗證?
你可以自定義一個過濾器,或者使用某某框架,自定義攔截器,或者aop
String header = request.getHeader(JwtUtil.TOKEN_HEADER); if (null == header || !header.toLowerCase().startsWith(JwtUtil.TOKEN_PREFIX)) { // 如果頭部 Authorization 未設定或者不是 basic 認證頭部,則當前 // 請求不是該過濾器關注的物件,直接放行,繼續filter chain 的執行 chain.doFilter(request, response); return; } try { String token = header.replace(JwtUtil.TOKEN_PREFIX, ""); // 驗證token是否過期 if(JwtUtil.isExpiration(token)){ throw new javax.security.sasl.AuthenticationException("token 驗證不通過"); } //檢查token是否能解析 Users user = (Users) JwtUtil.getUser(token); if (null == user) { throw new javax.security.sasl.AuthenticationException("token 驗證不通過"); } //驗證成功
總結:
使用jwt生成 一個具備有效期的token令牌, 這個token裡面儲存著 token本身的結構,加上token的過期時間,和一個你自己定義的字串類似於加密 ,也就是簽名所用的鹽,這個鹽只有你自己知道,這樣你的加密才是安全的,還可以包含一些自定義的使用者資訊放在荷載資訊裡,本文所用的HS256+鹽 對token加密是沒有RS256安全的,
JWT簽名演算法中HS256和RS256有什麼區別?
JWT簽名演算法中,一般有兩個選擇,一個採用HS256,另外一個就是採用RS256。
簽名實際上是一個加密的過程,生成一段標識(也是JWT的一部分)作為接收方驗證資訊是否被篡改的依據。
RS256 (採用SHA-256 的 RSA 簽名) 是一種非對稱演算法, 它使用公共/私鑰對: 標識提供方採用私鑰生成簽名, JWT 的使用方獲取公鑰以驗證簽名。由於公鑰 (與私鑰相比) 不需要保護, 因此大多數標識提供方使其易於使用方獲取和使用 (通常通過一個元資料URL)。
另一方面, HS256 (帶有 SHA-256 的 HMAC 是一種對稱演算法, 雙方之間僅共享一個 金鑰。由於使用相同的金鑰生成簽名和驗證簽名, 因此必須注意確保金鑰不被洩密。
在開發應用的時候啟用JWT,使用RS256更加安全,你可以控制誰能使用什麼型別的金鑰。另外,如果你無法控制客戶端,無法做到金鑰的完全保密,RS256會是個更佳的選擇,JWT的使用方只需要知道公鑰。
建議:
為了雙重保險,建議您使用RS256,最好是生成一個證書 讀取使用證書裡面的公鑰加密,私鑰留著以後每次前端請求驗證token的合法性就可以了
jwt提供了方法可以驗證token的合法性,過期時間,還可以根據token讀取使用者資訊,這樣你把token儲存前端的localstorage中,
後端的快取都省了
這樣做安全麼?
你要知道 RS256非對稱加密是一套體系的,公鑰私鑰是一組,都存在的後端,別人不進你伺服器盜取你就是安全的,要是真能登入你伺服器那就直接刪你庫了,
本文作者為本人原創:如需轉載 請攜帶出處 謝謝!!!!