javascript
JSON Web Tokens(JWT)
現在API越來越流行,如何安全保護這些API? JSON Web Tokens(JWT)能提供基于JSON格式的安全認證。它有以下特點:
- JWT是跨不同語言的,JWT可以在?.NET, Python, Node.js, Java, PHP, Ruby, Go, JavaScript和Haskell中使用
- JWT是自我包涵的,它們包含了必要的所有信息,這就意味著JWT能夠傳遞關于它自己的基本信息,比如用戶信息和簽名等。
- JWT傳遞是容易的,因為JWT是自我包涵,它們能被完美用在HTTP頭部中,當需要授權API時,你只要通過URL一起傳送它既可。
JWT易于辨識,是三段由小數點組成的字符串:
aaaaaaaaaa.bbbbbbbbbbb.cccccccccccc
這三部分含義分別是header,payload, signature
Header
頭部包含了兩個方面:類型和使用的哈希算法(如HMAC SHA256):
{ "typ": "JWT", "alg": "HS256" }?
對這個JSON字符進行base64encode編碼,我們就有了首個JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
JWT的第二部分是payload,也稱為?JWT Claims,這里放置的是我們需要傳輸的信息,有多個項目如注冊的claim名稱,公共claim名稱和私有claim名稱。
注冊claim名稱有下面幾個部分:
- iss: token的發行者
- sub: token的題目
- aud: token的客戶
- exp: 經常使用的,以數字時間定義失效期,也就是當前時間以后的某個時間本token失效。
- nbf: 定義在此時間之前,JWT不會接受處理。開始生效時間
- iat: JWT發布時間,能用于決定JWT年齡
- jti: JWT唯一標識. 能用于防止 JWT重復使用,一次只用一個token;如果簽發的時候這個claim的值是“1”,驗證的時候如果這個claim的值不是“1”就屬于驗證失敗
?
公共claim名稱用于定義我們自己創造的信息,比如用戶信息和其他重要信息。
私有claim名稱用于發布者和消費者都同意以私有的方式使用claim名稱。
下面是JWT的一個案例:
{ "iss": "scotch.io", "exp": 1300819380, "name": "Chris Sevilleja", "admin": true }?
簽名
JWT第三部分最后是簽名,簽名由以下組件組成:
- header
- payload
- 密鑰
下面是我們如何得到JWT的第三部分:
var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); HMACSHA256(encodedString, 'secret');
這里的secret是被服務器簽名,我們服務器能夠驗證存在的token并簽名新的token。
?
TWT支持的算法有:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
============================================================================================================
以上是官網的理論部分,下面會有提供一些實例:
?首先 導入 依賴:
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.2.0</version> </dependency>?1, 指定加密算法:
//HMAC Algorithm algorithmHS = Algorithm.HMAC256("secret");------------------------------------------------------------------------- //RSA
Map<String,Object> keys=RSAUtils.getKeys(); RSAPublicKey publicKey = (RSAPublicKey)keys.get("public"); //Get the key instance
RSAPrivateKey privateKey = (RSAPrivateKey)keys.get("private");//Get the key instance Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);
?2 , 生成token
? 用HS256生成token
try {Algorithm algorithm = Algorithm.HMAC256("secret");String token = JWT.create().withIssuer("auth0").sign(algorithm); } catch (UnsupportedEncodingException exception){//UTF-8 encoding not supported } catch (JWTCreationException exception){//Invalid Signing configuration / Couldn't convert Claims. }? 用RS256生成token
Map<String,Object> keys=RSAUtils.getKeys();RSAPublicKey publicKey = (RSAPublicKey)keys.get("public"); //Get the key instanceRSAPrivateKey privateKey = (RSAPrivateKey)keys.get("private");//Get the key instance try {Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);String token = JWT.create().withIssuer("auth0").sign(algorithm); } catch (JWTCreationException exception){//Invalid Signing configuration / Couldn't convert Claims. }? 3, 驗證token
? ? 1)普通驗證
? ? ? ?用HS256驗證token
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; try {Algorithm algorithm = Algorithm.HMAC256("secret");JWTVerifier verifier = JWT.require(algorithm).withIssuer("auth0").build(); //Reusable verifier instanceDecodedJWT jwt = verifier.verify(token); } catch (UnsupportedEncodingException exception){//UTF-8 encoding not supported } catch (JWTVerificationException exception){//Invalid signature/claims }? ? ?用RS256驗證token
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; RSAPublicKey publicKey = //Get the key instance RSAPrivateKey privateKey = //Get the key instance try {Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);JWTVerifier verifier = JWT.require(algorithm).withIssuer("auth0").build(); //Reusable verifier instanceDecodedJWT jwt = verifier.verify(token); } catch (JWTVerificationException exception){//Invalid signature/claims }? 2)在payLoad 是可以自定義數據,用于驗證,包括時間等。
? ? ?在生成token的時候指定數據:
@Testpublic void gen1() throws IOException {String token ="";SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//日期轉字符串Calendar calendar = Calendar.getInstance();calendar.add(Calendar.SECOND,30 ); //特定時間的年后Date date = calendar.getTime();try {Algorithm algorithm = Algorithm.HMAC256("mysecrite");token = JWT.create().withIssuer("auth0").withSubject("xiaoming").withClaim("name", 123).withArrayClaim("array", new Integer[]{1, 2, 3}).withExpiresAt(date).sign(algorithm);System.out.println("loglogagel:"+token);} catch (UnsupportedEncodingException exception){//UTF-8 encoding not supported} catch (JWTCreationException exception){//Invalid Signing configuration / Couldn't convert Claims. }}? ? 驗證token是否過期,是否有制定的
@Testpublic void gen3(){String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhdXRoMCIsImV4cCI6MTQ5NzY4NTQwOX0.DHY-90JAA63_TvI-gRZ2oHCIItMajb45zB1tdCHQ_NQ";try {Algorithm algorithm = Algorithm.HMAC256("mysecrite");JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(algorithm).withIssuer("auth0").withSubject("xiaomong");Clock clock = new Clock() {@Overridepublic Date getToday() {return new Date();}};//Must implement Clock interfaceJWTVerifier verifier = verification.build(clock);DecodedJWT jwt = verifier.verify(token);System.out.println(jwt.getAlgorithm());System.out.println(jwt.getType());System.out.println(jwt.getIssuer());System.out.println(jwt.getExpiresAt());} catch (UnsupportedEncodingException exception){//UTF-8 encoding not supported exception.printStackTrace();} catch (JWTVerificationException exception){//Invalid signature/claims exception.printStackTrace();} }如果 subject驗證的不一致,就會報如下錯誤:
??
如果時間超過 30 秒,會報如下錯誤:
?對驗證的方法稍加修改:
@Testpublic void gen3(){String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4aWFvbWluZyIsImFycmF5IjpbMSwyLDNdLCJpc3MiOiJhdXRoMCIsIm5hbWUiOiJJYW0gcmlnaHQgZnJvbSBjbGFpbSIsImV4cCI6MTQ5NzY4OTQ4NX0.6lsXISVAgi8B2wAvaZq4tj-h9Pgd6GGaOYZLz_gPFMU";try {Algorithm algorithm = Algorithm.HMAC256("mysecrite");JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(algorithm).withIssuer("auth0").withSubject("xiaoming");Clock clock = new Clock() {@Overridepublic Date getToday() {return new Date();}};//Must implement Clock interfaceJWTVerifier verifier = verification.build(clock);DecodedJWT jwt = verifier.verify(token);Map<String, Claim> claims = jwt.getClaims(); //Key is the Claim nameClaim claim = claims.get("name");System.out.println(claim.asString()); //打印出claim的值 System.out.println(jwt.getAlgorithm());System.out.println(jwt.getType());System.out.println(jwt.getIssuer());System.out.println(jwt.getExpiresAt());} catch (UnsupportedEncodingException exception){//UTF-8 encoding not supported exception.printStackTrace();} catch (JWTVerificationException exception){//Invalid signature/claims exception.printStackTrace();}驗證后的最后結果:
?
? ? ??
4,claim的添加,獲取
? ? 1) 內置的payload主要有以下幾個,如果沒有就返回null
? ? ?Issuer ("iss") :發布者
String issuer = jwt.getIssuer();Subject ("sub")
String subject = jwt.getSubject();Audience ("aud")
List<String> audience = jwt.getAudience();Expiration Time ("exp")
Date expiresAt = jwt.getExpiresAt();Not Before ("nbf")
Date notBefore = jwt.getNotBefore();Issued At ("iat")
Date issuedAt = jwt.getIssuedAt();JWT ID ("jti")
String id = jwt.getId();?
?2)定義私有的claim
? ? 添加:
String token = JWT.create().withClaim("name", 123).withArrayClaim("array", new Integer[]{1, 2, 3}).sign(algorithm);? ? 獲取:
JWTVerifier verifier = JWT.require(algorithm).withClaim("name", 123).withArrayClaim("array", 1, 2, 3).build(); DecodedJWT jwt = verifier.verify("my.jwt.token");?目前,官方支持claim的類型的有:Boolean, Integer, Double, String, Date , String[] 和 Integer.
?
? 5, ?Header Claims
? ? ?1)header claims 是定義header部分的內容,基本都是默認定義,不需要自己去設置的,內置的有:
Algorithm ("alg")
String algorithm = jwt.getAlgorithm();Type ("typ")
String type = jwt.getType();Content Type ("cty")
String contentType = jwt.getContentType();Key Id ("kid")
String keyId = jwt.getKeyId();?
? ? ? 2)添加:
Map<String, Object> headerClaims = new HashMap(); headerClaims.put("owner", "auth0"); String token = JWT.create().withHeader(headerClaims).sign(algorithm);? ? ? 3)獲取:
Claim claim = jwt.getHeaderClaim("owner");?
總結: 看了其他人的一些博客,發現他們的api都是相對老一點的版本,生成token是一步一步來,新的確實簡單方便很多。分享就這里,歡迎交流。
補充參考鏈接:
web 中使用jwt: ? ?https://github.com/jwtk/jjwt
?
參考地址:https://github.com/auth0/java-jwt
轉載于:https://www.cnblogs.com/minsons/p/7040753.html
總結
以上是生活随笔為你收集整理的JSON Web Tokens(JWT)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS开发 剖析网易新闻标签栏视图切换(
- 下一篇: 计算机浮点数运算误差与解决误差的算法