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是存储在服务端的秘钥。

例如,对字符串"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTExOTE3OTksImV4cCI6MTYxMTI3ODE5OX0",使用秘钥:"itcast",进行HMACSHA256进行加密,得到的结果为:"dCZFvxwJ0TS_fB_yj_6msveURl6xMYrsaaWraRqOA7Q"。这做为token的第三部分。

 

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 = "mysecret";
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中。这样在具体的业务逻辑处理时就无需编写重复的验证逻辑了。

posted on 2021-01-21 09:39  Sempron2800+  阅读(548)  评论(0编辑  收藏  举报