当前位置:网站首页>API Gateway/API 网关(四) - Kong的使用 - 集成Jwt和熔断插件

API Gateway/API 网关(四) - Kong的使用 - 集成Jwt和熔断插件

2022-04-23 14:11:00 anron

一、前言

使用Kong中的JWT插件需要先在Kong的Consumers中创建一个Consumer,然后在该Consumer的Credentials中设置JWT的key和secret。

 

假定在3个项目组中共用一个单点登录模块的这么一个场景,使用Kong网关来对JWT Token进行拦截。

  • A项目组负责单点登录

 /api/user   提供登录,登出的接口

  • B项目组负责APP1

/api/app1   应用1

/api/common/app1   不需要登录的查询页面

  • C项目组负责APP2

/api/app2   应用2

/api/common/app2   不需要登录的查询页面

 

Kong网关不对/api/user,/api/common进行JWT Token拦截,只对/api/app1,/api/app2进行JWT Token拦截

用户登录时调用/api/user/login接口验证用户名和密码,验证通过后系统将userid和mobile写入到JWT Token中,并以该Token为KEY放入redis中。

用户端登录后把JWT Token放入HTTP Header中,请求/api/app1或者/api/app2的应用接口,如果JWT Token无效或过期,在Kong网关就被拦截了,不会调用到后端的/api/app1或是/api/app2的应用接口。

app1或是app2接收到请求后,先读取出HTTP Header中的Authorization值(即:Bearer + 1个空格+Token),然后读取本项目中的redis,如果没找到数据就调用A项目组的远程接口(各个项目组有自己的DB,不可以互相访问其他项目的DB,只能通过远程接口来进行访问),出于安全性考虑,B和C项目组没有密钥,无法对Token进行解析,只有A项目组才能从Token中解析出UserID和Mobile。

Kong网关中没有找到这样的功能:自动解析Token中的UserID和Mobile,添加到Header后再调用后端的应用,不用后面再去解析。虽然Request Transformer插件可以remove/rename/replace/add/append对应的body/headers/querstring。

 

二、创建用户

2.1 添加Consumer

点击Consumers中的Create Consumer按钮,然后输入username,username=AllUsers,或者随便取个名称,只创建一个即可。

所有用户共用一个Consumer,即有优点也有缺点。

优点:不需要同步Kong中的Consumer和自己数据库的用户数据

缺点:限流等插件的使用只能按IP,不能按Consumer限制了。

2.2 创建Key和Secret

Credentials->JWT->Create JWT ,algorithm=HS256,其他默认空即可

三、添加JWT插件

设置claims to verify = exp,否则过期的token都可以请求,nbf可以不用设置

exp: 定义jwt的过期时间

nbf: 定义jwt的生效时间

设置了exp后,如果token过期了会提示"token expired"。

 

四、Java工程

单点登录工程下的部分模块,主要是JWT Token的颁发

4.1 修改POM文件

在pom.xml文件中添加jwt

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

4.2 JwtUtil类

JWT_KEY、JWT_SECRET要与Kong中的Customer里面的设置相同

package com.anron.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;

/**
 * @Author: Anron
 * @Date: 2020/4/13 14:07
 */
public class JwtUtil {
    private static final String JWT_KEY = "9vQeIbdszK6Pq6Lcf0y8khnQ6yr5779P";
    private static final String JWT_SECRET = "sZuEfoA9Vkwm8mhkM6Nr97eDX2YGdSu5";

    /**
     * 7天过期
     */
    public static final long JWT_EXPIRATION = (long) 7 * 24 * 60 * 60000;

    /**
     * 测试环境30分钟过期
     */
    public static final long JWT_EXPIRATION_TEST = (long) 30 * 60000;

    /**
     * iat 签发日期  withIssuedAt
     * nbf 生效时间  withNotBefore
     * exp 过期时间  withExpiresAt
     * sub 主题
     * iss 签发人
     * aud 接收方
     */
    public static String createJwtToken(Integer userId, String userNameOrMobile, long ttlMillis) {
        String token = null;
        try {
            token = JWT.create()
                    .withIssuer(JwtUtil.JWT_KEY)
                    .withIssuedAt(new Date())
                    .withNotBefore(new Date())
                    .withClaim("userid", userId)
                    .withClaim("username", userNameOrMobile)
                    .withClaim("sub", "anron company")
                    .withExpiresAt(new Date(System.currentTimeMillis() + ttlMillis))
                    .sign(Algorithm.HMAC256(JwtUtil.JWT_SECRET));
        } catch (JWTCreationException e){
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
        return token;
    }

    public static DecodedJWT decryptToken(final String token) {
        DecodedJWT jwt = null;
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(JwtUtil.JWT_SECRET))
                    .withIssuer(JwtUtil.JWT_KEY)
                    .build();
            jwt = verifier.verify(token);
        } catch (TokenExpiredException e) {
            e.printStackTrace();
        } catch (JWTDecodeException e){
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
        return jwt;
    }

}

4.3 Test类

package com.anron;

import com.anron.util.JwtUtil;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class AnronApplicationTests {

    @Test
    void contextLoads() {
        Integer userId  = 1001;
        String userName = "admin";
        String token = JwtUtil.createJwtToken(userId, userName, JwtUtil.JWT_EXPIRATION);
        System.out.println(token);
    }

    @Test
    void test1() {
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbnJvbiBjb21wYW55IiwibmJmIjoxNTkwMTI3MTY3LCJpc3MiOiI5dlFlSWJkc3pLNlBxNkxjZjB5OGtoblE2eXI1Nzc5UCIsImV4cCI6MTU5MDczMTk2NywiaWF0IjoxNTkwMTI3MTY3LCJ1c2VyaWQiOjEwMDEsInVzZXJuYW1lIjoiYWRtaW4ifQ.AeoDSUZRPsoQAUfkJUTZJuU5e-CI5SEmihrHlw39Cuc";
        DecodedJWT jwt = JwtUtil.decryptToken(token);

        if (jwt != null) {
            System.out.println("userid = " + jwt.getClaim("userid").asInt());
            System.out.println("username = " + jwt.getClaim("username").asString());
            System.out.println("sub = " + jwt.getClaim("sub").asString());
            System.out.println("签发时间 = " + jwt.getClaim("iat").asDate());
            System.out.println("生效时间 = " + jwt.getClaim("nbf").asDate());
            System.out.println("过期时间 = " + jwt.getClaim("exp").asDate());
        }
    }

}

五、熔断插件

在Kong中添加个Request Termination插件,配置用默认即可

版权声明
本文为[anron]所创,转载请带上原文链接,感谢
https://blog.csdn.net/anron/article/details/105736816