ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring Gateway 开发实例

2022-07-12 12:01:13  阅读:176  来源: 互联网

标签:return String Spring final token 实例 claims response Gateway


简介

基于spring cloud gateway,微服务网关,注册中心使用nacos, 具备微服务动态路由,jwt token鉴权功能CheckTokenFilter, 路径白名单配置。

架构图

image

网关作为所有服务的请求入口,鉴权token 用的jwt , 好处是jwt本身携带用户身份信息,网关校验通过后,可以直接把用户信息发在请求头传递给上游服务,不要额外存储。

交互时序图

image

实现

设计介绍完了就,开始实现

  1. 创建工程pom文件添加依赖
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
		</dependency>
      <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jwt.version}</version>
        </dependency>
  1. 实现GlobalFilter接口, jwt 鉴权的主要逻辑
@Component
@RequiredArgsConstructor
public class CheckTokenFilter implements GlobalFilter, Ordered {

    public static final String AUTHHEADER = "authorization";
    public static final String USER_ID_KEY = "userId";
    public static final String USER_NAME_KEY = "userName";
    public static final String TRACE_ID = "traceId";

    static final String BODY_401 = " {\n" +
            "  \"code\": 401,\n" +
            "  \"message\": \"Unauthorized\"\n" +
            "}";

    static final String BODY_403 = " {\n" +
            "  \"code\": 403,\n" +
            "  \"message\": \"token expired\"\n" +
            "}";

    private final CheckTokenUtil checkTokenUtil;
    private final TokenParse tokenParse;
    //布隆过滤器,对校验过的token过滤避免重复校验,提高性能
    private final CircleBloomFilter passedCircleBloomFilter;
    private final CircleBloomFilter stopedCircleBloomFilter;

    private final MyFilterConfiguration myFilterConfiguration;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        if (request.getMethod() == HttpMethod.OPTIONS){
            return chain.filter(exchange);
        }
        //请求路径白名单 判断
        if (checkWhitePath(request.getPath().value())){
            return chain.filter(exchange);
        }
        //token
        String token = request.getHeaders().getFirst(AUTHHEADER);
        if(token == null){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return getVoidMono(response, request, BODY_401);
        }

        if (stopedCircleBloomFilter.exists(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return getVoidMono(response, request, BODY_401);
        }
        Claims claims = null;
        if (passedCircleBloomFilter.exists(token)){
            claims = tokenParse.parseToken(token);
           setHeaders(claims, request.mutate());
        }else {
            try {
                //jwt token 校验
                claims = checkTokenUtil.check(token);
                passedCircleBloomFilter.put(token);
                //重写请求头,带上 userId
                setHeaders(claims, request.mutate());
            } catch (ExpiredJwtException e) {
                stopedCircleBloomFilter.put(token);
                response.setStatusCode(HttpStatus.FORBIDDEN);
                return getVoidMono(response, request, BODY_403);
            } catch (Exception e){
                stopedCircleBloomFilter.put(token);
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return getVoidMono(response, request, BODY_401);
            }
        }
        return chain.filter(exchange);
    }

    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, ServerHttpRequest httpRequest, String body) {
        HttpHeaders headers = serverHttpResponse.getHeaders();
        headers.add("Content-Type", "application/json;charset=UTF-8");
        
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(body.getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

    @Override
    public int getOrder() {
        return -100;
    }

    private boolean checkWhitePath(String reqPath){
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String white : myFilterConfiguration.getWhiteList()) {
            if (pathMatcher.match(white, reqPath)) {
                return true;
            }
        }

        return false;
    }

    private void setHeaders(Claims claims, ServerHttpRequest.Builder builder){
        String traceId = UUID.randomUUID().toString();
        builder.header(TRACE_ID, traceId);
        builder.header(USER_ID_KEY, claims.get("uid").toString());
        try {
            builder.header(USER_NAME_KEY, URLEncoder.encode(claims.getSubject(), "utf-8"));
        } catch (UnsupportedEncodingException e) {
            builder.header(USER_NAME_KEY, claims.getSubject());
        }
        builder.header(Claims.AUDIENCE, claims.getAudience());
        Object eid = claims.get("eid");
        builder.header("employeeId", eid != null ? String.valueOf(eid) : null);
        Object admin = claims.get("admin");
        builder.header("admin", admin != null ? String.valueOf(admin) : null);
    }

  
}

3.网关配置

spring:
  cloud:
    nacos:
      discovery:
        #nacos 地址
        server-addr: 127.0.0.1:8848
        namespace:
        ip:

    gateway:
      default-filters:
        - DedupeResponseHeader=Vary Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE
        - DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_FIRST
        - RemoveRequestHeader=Authorization
      discovery:
        locator:
          enabled: true
      globalcors:
        # 跨域配置
        cors-configurations:
          '[/**]':
            maxAge: 3600
            allowedOrigins: "*"
            allowedHeaders: "Origin, X-Requested-With, Content-Type, Accept, Authorization, *"
            allowCredentials: true
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTIONS

      httpclient:
        pool:
          max-connections: 2000
        connect-timeout: 3000
        response-timeout: 15000

      routes:
        - id: test
          uri: https://httpbin.org
          predicates:
            - Path=/test/{segment}
          filters:
            - SetPath=/{segment}



  application:
    name: gateway

server:
  port: 9000


management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always

工程源码: springGateway

标签:return,String,Spring,final,token,实例,claims,response,Gateway
来源: https://www.cnblogs.com/wenshun/p/16469569.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有