1. 程式人生 > 實用技巧 >Spring Boot學習04--使用JWT進行Token驗證

Spring Boot學習04--使用JWT進行Token驗證

JWT(Json Web Token)是一種認證協議,在前後端分離的專案中,客戶端向服務端傳送的請求存在“跨域”的問題。而在分散式架構體系下,一般會有多個伺服器提供服務。例如使用Nginx進行反向代理,使用輪詢或根據負載的策略對伺服器進行分配時,客戶端每次請求的可能是不同的伺服器。

這兩種情況下,傳統的Session儲存客戶端身份的方式就會有很多的不便。而JWT是解決方案之一。

1. Token的組成

JWT的Token由三部分組成:頭部(Header)、負載(Payload)、驗證簽名(Signature)。

這三部分之間,使用英文句號"."分割,舉例如下:

首先有一個固定的明文的字首:"Bearer "。

"token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTExOTE3OTksImV4cCI6MTYxMTI3ODE5OX0.dCZFvxwJ0TS_fB_yj_6msveURl6xMYrsaaWraRqOA7Q"

Header預設內容如下:

{
    'alg': "HS256",
    'typ': "JWT"
}

將其進行Base64編碼,得到的字串為:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9。頭部是緊跟在Bearer 後面的的內容。


Payload中儲存的是有效的資訊,也是一個json格式,例如:

{
    'id': 1,
    'username': "zhangsan",
    'gender': 0
}

對其進行Base64編碼,得到字串為:ewogICAgJ2lkJzogMSwKICAgICd1c2VybmFtZSc6ICJ6aGFuZ3NhbiIsCiAgICAnZ2VuZGVyJzogMAp9。

Signature是根據兩部分的資訊進行簽名得到的字串,其簽名規則為:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

注:這裡加密演算法HMACSHA256由頭部來指定,secret是儲存在服務端的祕鑰。

2.在Spring Boot專案中使用JWT進行Token驗證

首先在pom檔案中新增JWT的座標

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

編寫工具類,實現Token的生成和驗證邏輯:

 1 package com.example.demo.utils;
 2 
 3 import com.auth0.jwt.JWT;
 4 import com.auth0.jwt.JWTCreator;
 5 import com.auth0.jwt.JWTVerifier;
 6 import com.auth0.jwt.algorithms.Algorithm;
 7 import com.auth0.jwt.interfaces.DecodedJWT;
 8 
 9 import java.util.Calendar;
10 import java.util.Date;
11 import java.util.Map;
12 
13 public class JWTUtils {
14 
15     public static final String secret = "itcast";
16 
17     //生成token
18     public static String getToken(Map<String,String> map){
19         Calendar instance = Calendar.getInstance();
20         Date date = instance.getTime();
21         instance.add(Calendar.DATE,1);
22 
23         JWTCreator.Builder builder = JWT.create();
24         map.forEach( (k,v) -> {
25             builder.withClaim(k,v);
26         });
27 
28         String token = builder
29                 .withIssuedAt(date)
30                 .withExpiresAt(instance.getTime())
31                 .sign(Algorithm.HMAC256(secret));
32 
33         return token;
34     }
35 
36     public static DecodedJWT verify(String token){
37         JWTVerifier build = JWT.require(Algorithm.HMAC256(secret)).build();
38         DecodedJWT verify = build.verify(token);
39         return verify;
40     }
41 }

編寫攔截器,實現對HTTP請求中的Token自動驗證:

 1 package com.example.demo.interceptors;
 2 
 3 import com.auth0.jwt.interfaces.DecodedJWT;
 4 import com.example.demo.utils.JWTUtils;
 5 import com.fasterxml.jackson.databind.ObjectMapper;
 6 import org.springframework.web.servlet.HandlerInterceptor;
 7 
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 import java.util.HashMap;
11 import java.util.Map;
12 
13 public class JWTInterceptor implements HandlerInterceptor {
14     @Override
15     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
16         Map<String,Object> map = new HashMap<>();
17         String token = request.getHeader("Authorization");
18         try{
19             DecodedJWT verify = JWTUtils.verify(token);
20             return true;
21         }
22         catch (Exception e){
23             e.printStackTrace();
24             map.put("state",false);
25             map.put("msg","請求失敗");
26         }
27         String json = new ObjectMapper().writeValueAsString(map);
28         response.setContentType("application/json;charset=utf-8");
29         response.getWriter().println(json);
30         return false;
31     }
32 }

編寫Controller對Token接受請求,並處理:

 1 package com.example.demo.controller;
 2 
 3 import com.alibaba.fastjson.JSON;
 4 import com.alibaba.fastjson.JSONObject;
 5 import com.auth0.jwt.interfaces.Claim;
 6 import com.auth0.jwt.interfaces.DecodedJWT;
 7 import com.example.demo.model.dto.SPManager;
 8 import com.example.demo.model.out.LoginData;
 9 import com.example.demo.model.out.LoginResult;
10 import com.example.demo.model.out.Meta;
11 import com.example.demo.service.SPManagerService;
12 import com.example.demo.utils.JWTUtils;
13 
14 import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.web.bind.annotation.PostMapping;
16 import org.springframework.web.bind.annotation.RequestMapping;
17 import org.springframework.web.bind.annotation.RequestParam;
18 import org.springframework.web.bind.annotation.RestController;
19 
20 import javax.servlet.http.HttpServletRequest;
21 import java.util.HashMap;
22 
23 @RestController
24 public class ManagerController {
25 
26     @Autowired
27     SPManagerService spManagerService;
28 
29     @PostMapping("/login")
30     public JSONObject Login(@RequestParam String username, @RequestParam String password) {
31         System.out.println(username + "|" + password);
32         
33         LoginData lt = new LoginData();
34         Meta meta = new Meta();
35         SPManager model = spManagerService.getManagerByNameAndPwd(username, password);
36         System.out.println(model);
37         if (model != null) {
38             HashMap<String, String> map = new HashMap<>();
39             map.put("uid", model.getMgId().toString());
40             map.put("rid", model.getRoleId().toString());
41             String token = JWTUtils.getToken(map);
42 
43             lt.setId(model.getMgId());
44             lt.setRid(model.getRoleId());
45             lt.setUsername(model.getMgName());
46             lt.setEmail(model.getMgEmail());
47             lt.setMobile(model.getMgMobile());
48             lt.setToken(token);
49 
50             meta.setMsg("登入成功");
51             meta.setStatus(200);
52         }else{
53             lt = null;
54             meta.setMsg("登入失敗");
55             meta.setStatus(400);
56         }
57         LoginResult lr = new LoginResult();
58         lr.setData(lt);
59         lr.setMeta(meta);
60 
61         String json = JSON.toJSONString(lr);
62         JSONObject jsonObject = JSONObject.parseObject(json);
63         return jsonObject;
64     }
65 }

在攔截器JWTInterceptor中已經定義了對request頭[Authorization]中儲存的Token自動驗籤的處理的邏輯,請求只有通過了攔截器的驗證,才能進入到Controller中。這樣在具體的業務邏輯處理時就無需編寫重複的驗證邏輯了。