Spring Cloud Gateway 实现Token校验

Posted 狂乱的贵公子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud Gateway 实现Token校验相关的知识,希望对你有一定的参考价值。

在我看来,在某些场景下,网关就像是一个公共方法,把项目中的都要用到的一些功能提出来,抽象成一个服务。比如,我们可以在业务网关上做日志收集、Token校验等等,当然这么理解很狭隘,因为网关的能力远不止如此,但是不妨碍我们更好地理解它。下面的例子演示了,如何在网关校验Token,并提取用户信息放到Header中传给下游业务系统。

1. 生成Token

用户登录成功以后,生成token,此后的所有请求都带着token。网关负责校验token,并将用户信息放入请求Header,以便下游系统可以方便的获取用户信息。

为了方便演示,本例中涉及三个工程

公共项目:cjs-commons-jwt

认证服务:cjs-auth-service

网关服务:cjs-gateway-example

1.1. Token生成与校验工具类

因为生成token在认证服务中,token校验在网关服务中,因此,我把这一部分写在了公共项目cjs-commons-jwt中

pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 
 3 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>com.cjs.example</groupId>
 8     <artifactId>cjs-commons-jwt</artifactId>
 9     <version>1.0-SNAPSHOT</version>
10 
11     <properties>
12         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13         <maven.compiler.source>1.8</maven.compiler.source>
14         <maven.compiler.target>1.8</maven.compiler.target>
15     </properties>
16 
17     <dependencies>
18         <dependency>
19             <groupId>com.auth0</groupId>
20             <artifactId>java-jwt</artifactId>
21             <version>3.10.0</version>
22         </dependency>
23         <dependency>
24             <groupId>org.apache.commons</groupId>
25             <artifactId>commons-lang3</artifactId>
26             <version>3.9</version>
27         </dependency>
28         <dependency>
29             <groupId>com.alibaba</groupId>
30             <artifactId>fastjson</artifactId>
31             <version>1.2.66</version>
32         </dependency>
33     </dependencies>
34 
35 </project>

JWTUtil.java

 1 package com.cjs.example.utils;
 2 
 3 import com.auth0.jwt.JWT;
 4 import com.auth0.jwt.JWTVerifier;
 5 import com.auth0.jwt.algorithms.Algorithm;
 6 import com.auth0.jwt.exceptions.JWTDecodeException;
 7 import com.auth0.jwt.exceptions.SignatureVerificationException;
 8 import com.auth0.jwt.exceptions.TokenExpiredException;
 9 import com.auth0.jwt.interfaces.DecodedJWT;
10 import com.cjs.example.enums.ResponseCodeEnum;
11 import com.cjs.example.exception.TokenAuthenticationException;
12 
13 import java.util.Date;
14 
15 /**
16  * @author ChengJianSheng
17  * @date 2020-03-08
18  */
19 public class JWTUtil {
20 
21     public static final long TOKEN_EXPIRE_TIME = 7200 * 1000;
22     private static final String ISSUER = "cheng";
23 
24     /**
25      * 生成Token
26      * @param username 用户标识(不一定是用户名,有可能是用户ID或者手机号什么的)
27      * @param secretKey
28      * @return
29      */
30     public static String generateToken(String username, String secretKey) {
31         Algorithm algorithm = Algorithm.HMAC256(secretKey);
32         Date now = new Date();
33         Date expireTime = new Date(now.getTime() + TOKEN_EXPIRE_TIME);
34 
35         String token = JWT.create()
36                 .withIssuer(ISSUER)
37                 .withIssuedAt(now)
38                 .withExpiresAt(expireTime)
39                 .withClaim("username", username)
40                 .sign(algorithm);
41 
42         return token;
43     }
44 
45     /**
46      * 校验Token
47      * @param token
48      * @param secretKey
49      * @return
50      */
51     public static void verifyToken(String token, String secretKey) {
52         try {
53             Algorithm algorithm = Algorithm.HMAC256(secretKey);
54             JWTVerifier jwtVerifier = JWT.require(algorithm).withIssuer(ISSUER).build();
55             jwtVerifier.verify(token);
56         } catch (JWTDecodeException jwtDecodeException) {
57             throw new TokenAuthenticationException(ResponseCodeEnum.TOKEN_INVALID.getCode(), ResponseCodeEnum.TOKEN_INVALID.getMessage());
58         } catch (SignatureVerificationException signatureVerificationException) {
59             throw new TokenAuthenticationException(ResponseCodeEnum.TOKEN_SIGNATURE_INVALID.getCode(), ResponseCodeEnum.TOKEN_SIGNATURE_INVALID.getMessage());
60         } catch (TokenExpiredException tokenExpiredException) {
61             throw new TokenAuthenticationException(ResponseCodeEnum.TOKEN_EXPIRED.getCode(), ResponseCodeEnum.TOKEN_INVALID.getMessage());
62         } catch (Exception ex) {
63             throw new TokenAuthenticationException(ResponseCodeEnum.UNKNOWN_ERROR.getCode(), ResponseCodeEnum.UNKNOWN_ERROR.getMessage());
64         }
65     }
66 
67     /**
68      * 从Token中提取用户信息
69      * @param token
70      * @return
71      */
72     public static String getUserInfo(String token) {
73         DecodedJWT decodedJWT = JWT.decode(token);
74         String username = decodedJWT.getClaim("username").asString();
75         return username;
76     }
77 
78 }

ResponseCodeEnum.java

 1 package com.cjs.example.enums;
 2 
 3 /**
 4  * @author ChengJianSheng
 5  * @date 2020-03-08
 6  */
 7 public enum ResponseCodeEnum {
 8 
 9     SUCCESS(0, "成功"),
10     FAIL(-1, "失败"),
11     LOGIN_ERROR(1000, "用户名或密码错误"),
12     UNKNOWN_ERROR(2000, "未知错误"),
13     PARAMETER_ILLEGAL(2001, "参数不合法"),
14     TOKEN_INVALID(2002, "无效的Token"),
15     TOKEN_SIGNATURE_INVALID(2003, "无效的签名"),
16     TOKEN_EXPIRED(2004, "token已过期"),
17     TOKEN_MISSION(2005, "token缺失"),
18     REFRESH_TOKEN_INVALID(2006, "刷新Token无效");
19 
20 
21     private int code;
22 
23     private String message;
24 
25     ResponseCodeEnum(int code, String message) {
26         this.code = code;
27         this.message = message;
28     }
29 
30     public int getCode() {
31         return code;
32     }
33 
34     public String getMessage() {
35         return message;
36     }
37 
38 }

ResponseResult.java

 1 package com.cjs.example;
 2 
 3 import com.cjs.example.enums.ResponseCodeEnum;
 4 
 5 /**
 6  * @author ChengJianSheng
 7  * @date 2020-03-08
 8  */
 9 public class ResponseResult<T> {
10 
11     private int code = 0;
12 
13     private String msg;
14 
15     private T data;
16 
17     public ResponseResult(int code, String msg) {
18         this.code = code;
19         this.msg = msg;
20     }
21 
22     public ResponseResult(int code, String msg, T data) {
23         this.code = code;
24         this.msg = msg;
25         this.data = data;
26     }
27 
28     public static ResponseResult success() {
29         return new ResponseResult(ResponseCodeEnum.SUCCESS.getCode(), ResponseCodeEnum.SUCCESS.getMessage());
30     }
31 
32     public static <T> ResponseResult<T> success(T data) {
33         return new ResponseResult(ResponseCodeEnum.SUCCESS.getCode(), ResponseCodeEnum.SUCCESS.getMessage(), data);
34     }
35 
36     public static ResponseResult error(int code, String msg) {
37         return new ResponseResult(code, msg);
38     }
39 
40     public static <T> ResponseResult<T> error(int code, String msg, T data) {
41         return new ResponseResult(code, msg, data);
42     }
43 
44     public boolean isSuccess() {
45         return code == 0;
46     }
47 
48     public int getCode() {
49         return code;
50     }
51 
52     public void setCode(int code) {
53         this.code = code;
54     }
55 
56     public String getMsg() {
57         return msg;
58     }
59 
60     public void setMsg(String msg) {
61         this.msg 最全面的改造Zuul网关为Spring Cloud Gateway(包含Zuul核心实现和Spring Cloud Gateway核心实现)

Spring Cloud Gateway:retry 与 fallback

spring-cloud-gateway之GatewayFilterFactory

Spring Cloud-Gateway网关

spring cloud gateway 如何工作

Spirng Cloud Gateway中通过Spring security + WebFlux 实现权限认证