自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

讓JWT來(lái)保護(hù)你的接口服務(wù)

網(wǎng)絡(luò) 通信技術(shù)
以前寫(xiě)過(guò)一篇關(guān)于接口服務(wù)規(guī)范的文章,原文在此,里面關(guān)于安全性問(wèn)題重點(diǎn)講述了通過(guò)appid,appkey,timestamp,nonce以及sign來(lái)獲取token,使用token來(lái)保障接口服務(wù)的安全。今天我們來(lái)講述一種更加便捷的方式,使用jwt來(lái)生成token。

大家好,我是大堯。

以前寫(xiě)過(guò)一篇關(guān)于接口服務(wù)規(guī)范的文章,原文在此,里面關(guān)于安全性問(wèn)題重點(diǎn)講述了通過(guò)appid,appkey,timestamp,nonce以及sign來(lái)獲取token,使用token來(lái)保障接口服務(wù)的安全。今天我們來(lái)講述一種更加便捷的方式,使用jwt來(lái)生成token。

一、JWT是什么

JSON Web Token(JWT) 定義了一種緊湊且自包含的方式,用于在各方之間作為 JSON 對(duì)象安全地傳輸信息。該信息可以被驗(yàn)證和信任,因?yàn)樗墙?jīng)過(guò)數(shù)字簽名的。JWT可以設(shè)置有效期。

JWT是一個(gè)很長(zhǎng)的字符串,包含了Header,Playload和Signature三部分內(nèi)容,中間用.進(jìn)行分隔。

Headers

Headers部分描述的是JWT的基本信息,一般會(huì)包含簽名算法和令牌類型,數(shù)據(jù)如下:

  1.     "alg""RS256"
  2.     "typ""JWT" 

Playload

Playload就是存放有效信息的地方,JWT規(guī)定了以下7個(gè)字段,建議但不強(qiáng)制使用:

  1. iss: jwt簽發(fā)者 
  2. sub: jwt所面向的用戶 
  3. aud: 接收jwt的一方 
  4. exp: jwt的過(guò)期時(shí)間,這個(gè)過(guò)期時(shí)間必須要大于簽發(fā)時(shí)間 
  5. nbf: 定義在什么時(shí)間之前,該jwt都是不可用的 
  6. iat: jwt的簽發(fā)時(shí)間 
  7. jti: jwt的唯一身份標(biāo)識(shí),主要用來(lái)作為一次性token 

除此之外,我們還可以自定義內(nèi)容

  1.     "name":"Java旅途"
  2.     "age":18 

Signature

Signature是將JWT的前面兩部分進(jìn)行加密后的字符串,將Headers和Playload進(jìn)行base64編碼后使用Headers中規(guī)定的加密算法和密鑰進(jìn)行加密,得到JWT的第三部分。

二、JWT生成和解析token

在應(yīng)用服務(wù)中引入JWT的依賴

  1. <dependency> 
  2.     <groupId>io.jsonwebtoken</groupId> 
  3.     <artifactId>jjwt</artifactId> 
  4.     <version>0.9.0</version> 
  5. </dependency> 

 

根據(jù)JWT的定義生成一個(gè)使用RSA算法加密的,有效期為30分鐘的token

  1. public static String createToken(User user) throws Exception{ 
  2.  
  3.     return Jwts.builder() 
  4.         .claim("name",user.getName()) 
  5.         .claim("age",user.getAge()) 
  6.         // rsa加密 
  7.         .signWith(SignatureAlgorithm.RS256, RsaUtil.getPrivateKey(PRIVATE_KEY)) 
  8.         // 有效期30分鐘 
  9.         .setExpiration(DateTime.now().plusSeconds(30 * 60).toDate()) 
  10.         .compact(); 

登錄接口驗(yàn)證通過(guò)后,調(diào)用JWT生成帶有用戶標(biāo)識(shí)的token響應(yīng)給用戶,在接下來(lái)的請(qǐng)求中,頭部攜帶token進(jìn)行驗(yàn)簽,驗(yàn)簽通過(guò)后,正常訪問(wèn)應(yīng)用服務(wù)。

  1. public static Claims parseToken(String token) throws Exception{ 
  2.     return Jwts 
  3.         .parser() 
  4.         .setSigningKey(RsaUtil.getPublicKey(PUBLIC_KEY)) 
  5.         .parseClaimsJws(token) 
  6.         .getBody(); 

三、token續(xù)簽問(wèn)題

上面講述了關(guān)于JWT驗(yàn)證的過(guò)程,現(xiàn)在我們考慮這樣一個(gè)問(wèn)題,客戶端攜帶token訪問(wèn)下單接口,token驗(yàn)簽通過(guò),客戶端下單成功,返回下單結(jié)果,然后客戶端帶著token調(diào)用支付接口進(jìn)行支付,驗(yàn)簽的時(shí)候發(fā)現(xiàn)token失效了,這時(shí)候應(yīng)該怎么辦?只能告訴用戶token失效,然后讓用戶重新登錄獲取token?這種體驗(yàn)是非常不好的,oauth2在這方面做的比較好,除了簽發(fā)token,還會(huì)簽發(fā)refresh_token,當(dāng)token過(guò)期后,會(huì)去調(diào)用refresh_token重新獲取token,如果refresh_token也過(guò)期了,那么再提示用戶去登錄。現(xiàn)在我們模擬oauth2的實(shí)現(xiàn)方式來(lái)完成JWT的refresh_token。

思路大概就是用戶登錄成功后,簽發(fā)token的同時(shí),生成一個(gè)加密串作為refresh_token,refresh_token存放在redis中,設(shè)置合理的過(guò)期時(shí)間(一般會(huì)將refresh_token的過(guò)期時(shí)間設(shè)置的比較久一點(diǎn))。然后將token和refresh_token響應(yīng)給客戶端。偽代碼如下:

  1. @PostMapping("getToken"
  2. public ResultBean getToken(@RequestBody LoingUser user){ 
  3.  
  4.     ResultBean resultBean = new ResultBean(); 
  5.     // 用戶信息校驗(yàn)失敗,響應(yīng)錯(cuò)誤 
  6.     if(!user){ 
  7.         resultBean.fillCode(401,"賬戶密碼不正確"); 
  8.         return resultBean; 
  9.     } 
  10.     String token = null
  11.     String refresh_token = null
  12.     try { 
  13.         // jwt 生成的token 
  14.         token = JwtUtil.createToken(user); 
  15.         // 刷新token 
  16.         refresh_token = Md5Utils.hash(System.currentTimeMillis()+""); 
  17.         // refresh_token過(guò)期時(shí)間為24小時(shí) 
  18.         redisUtils.set("refresh_token:"+refresh_token,token,30*24*60*60); 
  19.     } catch (Exception e) { 
  20.         e.printStackTrace(); 
  21.     } 
  22.  
  23.     Map<String,Object> map = new HashMap<>(); 
  24.     map.put("access_token",token); 
  25.     map.put("refresh_token",refresh_token); 
  26.     map.put("expires_in",2*60*60); 
  27.     resultBean.fillInfo(map); 
  28.     return resultBean; 

客戶端調(diào)用接口時(shí),在請(qǐng)求頭中攜帶token,在攔截器中攔截請(qǐng)求,驗(yàn)證token的有效性,如果驗(yàn)證token失敗,則去redis中判斷是否是refresh_token的請(qǐng)求,如果refresh_token驗(yàn)證也失敗,則給客戶端響應(yīng)鑒權(quán)異常,提示客戶端重新登錄,偽代碼如下:

  1. HttpHeaders headers = request.getHeaders(); 
  2. // 請(qǐng)求頭中獲取令牌 
  3. String token = headers.getFirst("Authorization"); 
  4. // 判斷請(qǐng)求頭中是否有令牌 
  5. if (StringUtils.isEmpty(token)) { 
  6.     resultBean.fillCode(401,"鑒權(quán)失敗,請(qǐng)攜帶有效token"); 
  7.     return resultBean; 
  8. if(!token.contains("Bearer")){ 
  9.     resultBean.fillCode(401,"鑒權(quán)失敗,請(qǐng)攜帶有效token"); 
  10.     return resultBean; 
  11.  
  12. token = token.replace("Bearer ",""); 
  13. // 如果請(qǐng)求頭中有令牌則解析令牌 
  14. try { 
  15.     Claims claims = TokenUtil.parseToken(token).getBody(); 
  16. } catch (Exception e) { 
  17.     e.printStackTrace(); 
  18.     String refreshToken = redisUtils.get("refresh_token:" + token)+""
  19.     if(StringUtils.isBlank(refreshToken) || "null".equals(refreshToken)){ 
  20.         resultBean.fillCode(403,"refresh_token已過(guò)期,請(qǐng)重新獲取token"); 
  21.         return resultbean; 
  22.     } 

refresh_token來(lái)?yè)Q取token的偽代碼如下:

  1. @PostMapping("refreshToken"
  2. public Result refreshToken(String token){ 
  3.  
  4.     ResultBean resultBean = new ResultBean(); 
  5.     String refreshToken = redisUtils.get(TokenConstants.REFRESHTOKEN + token)+""
  6.     String access_token = null
  7.     try { 
  8.         Claims claims = JwtUtil.parseToken(refreshToken); 
  9.         String username = claims.get("username")+""
  10.         String password = claims.get("password")+""
  11.         LoginUser loginUser = new LoginUser(); 
  12.         loginUser.setUsername(username); 
  13.         loginUser.setPassword(password); 
  14.         access_token = JwtUtil.createToken(loginUser); 
  15.     } catch (Exception e) { 
  16.         e.printStackTrace(); 
  17.     } 
  18.     Map<String,Object> map = new HashMap<>(); 
  19.     map.put("access_token",access_token); 
  20.     map.put("refresh_token",token); 
  21.     map.put("expires_in",30*60); 
  22.     resultBean.fillInfo(map); 
  23.     return resultBean; 

通過(guò)上面的分析,我們簡(jiǎn)單的實(shí)現(xiàn)了token的簽發(fā),驗(yàn)簽以及續(xù)簽問(wèn)題,JWT作為一個(gè)輕量級(jí)的鑒權(quán)框架,使用起來(lái)非常方便,但是也會(huì)存在一些問(wèn)題,

  • JWT的Playload部分只是經(jīng)過(guò)base64編碼,這樣我們的信息其實(shí)就完全暴露了,一般不要將敏感信息存放在JWT中。
  • JWT生成的token比較長(zhǎng),每次在請(qǐng)求頭中攜帶token,導(dǎo)致請(qǐng)求偷會(huì)比較大,有一定的性能問(wèn)題。
  • JWT生成后,服務(wù)端無(wú)法廢棄,只能等待JWT主動(dòng)過(guò)期。

下面這段是我網(wǎng)上看到的一段關(guān)于JWT比較適用的場(chǎng)景:

  • 有效期短
  • 只希望被使用一次

比如,用戶注冊(cè)后發(fā)一封郵件讓其激活賬戶,通常郵件中需要有一個(gè)鏈接,這個(gè)鏈接需要具備以下的特性:能夠標(biāo)識(shí)用戶,該鏈接具有時(shí)效性(通常只允許幾小時(shí)之內(nèi)激活),不能被篡改以激活其他可能的賬戶,一次性的。這種場(chǎng)景就適合使用JWT。

本文轉(zhuǎn)載自微信公眾號(hào)「Java旅途」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java旅途公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: Java旅途
相關(guān)推薦

2021-08-25 23:03:58

區(qū)塊鏈數(shù)據(jù)安全

2010-04-22 15:24:36

郵件安全網(wǎng)絡(luò)加密服務(wù)器

2011-03-18 13:41:50

2018-04-08 09:00:00

Let's Encry加密解密

2013-12-10 10:16:39

2020-07-06 11:32:50

HTTPHTTP Header開(kāi)發(fā)者

2023-12-22 09:03:31

2020-08-18 10:35:18

JWTredis認(rèn)證

2010-08-18 09:07:26

數(shù)據(jù)泄密防護(hù)DLP公司數(shù)據(jù)

2018-03-02 16:50:43

人工智能機(jī)器人

2022-03-05 18:25:51

SSLTLS協(xié)議

2011-03-07 09:44:09

赤裸的密碼密碼

2015-11-19 09:44:34

HTML5定位

2021-05-26 13:38:45

Google設(shè)定密碼用戶活動(dòng)

2016-08-15 10:39:36

2022-08-15 22:28:57

串口訪問(wèn)鴻蒙

2022-02-17 08:57:18

內(nèi)存設(shè)計(jì)進(jìn)程

2020-05-14 20:10:12

SSLTLSLinux

2012-05-11 14:39:07

2022-10-17 09:15:37

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)