{"id":1952,"date":"2023-04-01T10:16:01","date_gmt":"2023-04-01T02:16:01","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=1952"},"modified":"2023-04-22T08:39:44","modified_gmt":"2023-04-22T00:39:44","slug":"micro-service-gateway-spring-cloud-gateway-advanced","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/04\/01\/micro-service-gateway-spring-cloud-gateway-advanced\/","title":{"rendered":"\u5fae\u670d\u52a1\u7f51\u5173 Spring Cloud Gateway \u8fdb\u9636"},"content":{"rendered":"<h2>\u5173\u4e8e\u7f51\u5173<\/h2>\n<h3>\u7f51\u5173\u662f\u600e\u4e48\u6f14\u5316\u6765\u7684<\/h3>\n<p>\u5355\u4f53\u5e94\u7528\u62c6\u5206\u6210\u591a\u4e2a\u670d\u52a1\u540e\uff0c\u5bf9\u5916\u9700\u8981\u4e00\u4e2a\u7edf\u4e00\u5165\u53e3\uff0c\u89e3\u8026\u5ba2\u6237\u7aef\u4e0e\u5185\u90e8\u670d\u52a1<\/p>\n<p><!-- more --><\/p>\n<h3>\u7f51\u5173\u7684\u57fa\u672c\u529f\u80fd<\/h3>\n<ul>\n<li>\u7f51\u5173\u6838\u5fc3\u529f\u80fd\u662f\u8def\u7531\u8f6c\u53d1\uff0c\u56e0\u6b64\u4e0d\u8981\u6709\u8017\u65f6\u64cd\u4f5c\u5728\u7f51\u5173\u4e0a\u5904\u7406\uff0c\u8ba9\u8bf7\u6c42\u5feb\u901f\u8f6c\u53d1\u5230\u540e\u7aef\u670d\u52a1\u4e0a<\/li>\n<li>\u7f51\u5173\u8fd8\u80fd\u505a\u7edf\u4e00\u7684\u7194\u65ad\u3001\u9650\u6d41\u3001\u8ba4\u8bc1\u3001\u65e5\u5fd7\u76d1\u63a7\u7b49<\/li>\n<\/ul>\n<h2>\u5173\u4e8eSpring Cloud Gateway<\/h2>\n<p>Spring Cloud Gateway\u662f\u7531Spring\u5b98\u65b9\u57fa\u4e8eSpring5.0\u3001Spring Boot2.0\u3001Project Reactor\u7b49\u6280\u672f\u5f00\u53d1\u7684\u7f51\u5173\uff0c\u4f7f\u7528\u975e\u963b\u585eAPI\uff0cWebsockets\u5f97\u5230\u652f\u6301\uff0c\u76ee\u7684\u662f\u4ee3\u66ff\u539f\u5148\u7248\u672c\u4e2d\u7684Spring Cloud Netfilx Zuul\uff0c\u76ee\u524dNetfilx\u5df2\u7ecf\u5f00\u6e90\u4e86Zuul2.0\uff0c\u4f46Spring \u6ca1\u6709\u8003\u8651\u96c6\u6210\uff0c\u800c\u662f\u63a8\u51fa\u4e86\u81ea\u5df1\u5f00\u53d1\u7684Spring Cloud GateWay\u3002<strong>\u8fd9\u91cc\u9700\u8981\u6ce8\u610f\u4e00\u4e0bgateway\u4f7f\u7528\u7684netty+webflux\u5b9e\u73b0\uff0c\u4e0d\u8981\u52a0\u5165web\u4f9d\u8d56\uff08\u4e0d\u8981\u5f15\u7528webmvc\uff09\uff0c\u5426\u5219\u521d\u59cb\u5316\u4f1a\u62a5\u9519\uff0c\u9700\u8981\u52a0\u5165webflux\u4f9d\u8d56<\/strong>\u3002<\/p>\n<blockquote>\n<p>gateway\u4e0ezuul\u7684\u7b80\u5355\u6bd4\u8f83\uff1agateway\u4f7f\u7528\u7684\u662f\u5f02\u6b65\u8bf7\u6c42\uff0czuul\u662f\u540c\u6b65\u8bf7\u6c42\uff0cgateway\u7684\u6570\u636e\u5c01\u88c5\u5728ServerWebExchange\u91cc\uff0czuul\u5c01\u88c5\u5728RequestContext\u91cc\uff0c\u540c\u6b65\u65b9\u4fbf\u8c03\u5f0f\uff0c\u53ef\u4ee5\u628a\u6570\u636e\u5c01\u88c5\u5728ThreadLocal\u4e2d\u4f20\u9012\u3002<\/p>\n<\/blockquote>\n<p>Spring Cloud Gateway\u6709\u4e09\u4e2a\u6838\u5fc3\u6982\u5ff5\uff1a\u8def\u7531\u3001\u65ad\u8a00\u3001\u8fc7\u6ee4\u5668<\/p>\n<ul>\n<li>\u8fc7\u6ee4\u5668\uff1agateway\u6709\u4e24\u79cdfilter\uff1aGlobalFilter\u3001GatewayFilter\uff0c\u5168\u5c40\u8fc7\u6ee4\u5668\u9ed8\u8ba4\u5bf9\u6240\u6709\u8def\u7531\u6709\u6548\u3002<\/li>\n<li>\u6587\u6863\u5730\u5740\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/cloud.spring.io\/spring-cloud-static\/Finchley.SR2\/multi\/multi_spring-cloud.html\">https:\/\/cloud.spring.io\/spring-cloud-static\/Finchley.SR2\/multi\/multi_spring-cloud.html<\/a><\/li>\n<\/ul>\n<p>\u7f51\u5173\u4f5c\u4e3a\u6240\u6709\u8bf7\u6c42\u6d41\u91cf\u7684\u5165\u53e3\uff0c\u5728\u5b9e\u9645\u751f\u4ea7\u73af\u5883\u4e2d\u4e3a\u4e86\u4fdd\u8bc1\u9ad8\u53ef\u9760\u548c\u9ad8\u53ef\u7528\uff0c\u5c3d\u91cf\u907f\u514d\u91cd\u542f\uff0c\u9700\u8981\u7528\u5230\u52a8\u6001\u8def\u7531\u914d\u7f6e\uff0c\u5728\u7f51\u5173\u8fd0\u884c\u8fc7\u7a0b\u4e2d\u66f4\u6539\u8def\u7531\u914d\u7f6e<\/p>\n<h2>\u4ee3\u7801\u5b9e\u8df5<\/h2>\n<p>\u9700\u8981\u7528\u52303\u4e2a\u9879\u76ee\uff1aeureka-server\u3001gateway\u3001consumer-service<\/p>\n<h3>eureka-server<\/h3>\n<p>eureka-server \u670d\u52a1\u53d1\u73b0\u6ce8\u518c\uff0c\u4f9bgateway\u8f6c\u53d1\u8bf7\u6c42\u65f6\u83b7\u53d6\u670d\u52a1\u5b9e\u4f8b ip+port<\/p>\n<h3>gateway<\/h3>\n<p>\u65b0\u5efa gateway \u7f51\u5173\u9879\u76ee\uff0c\u9879\u76ee\u5f15\u7528\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-xml\">&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-cloud-starter-gateway&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-actuator&lt;\/artifactId&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n<p>\u5728\u4e3b\u7c7b\u4e0a\u542f\u7528\u670d\u52a1\u53d1\u73b0\u6ce8\u518c\u6ce8\u89e3<code>@EnableDiscoveryClient<\/code><\/p>\n<p>\u914d\u7f6e\u6587\u4ef6\u5185\u5bb9\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-yml\">server:\n  port: 9999\nspring:\n  profiles:\n    active: dev\n  application:\n    name: gateway-service\n  cloud:\n    gateway:\n      discovery:\n        locator:\n          enabled: true\n          # \u670d\u52a1\u540d\u5c0f\u5199\n          lower-case-service-id: true\n      routes:\n      - id: consumer-service\n        # lb\u4ee3\u8868\u4ece\u6ce8\u518c\u4e2d\u5fc3\u83b7\u53d6\u670d\u52a1\uff0c\u4e14\u5df2\u8d1f\u8f7d\u5747\u8861\u65b9\u5f0f\u8f6c\u53d1\n        uri: lb:\/\/consumer-service\n        predicates:\n        - Path=\/consumer\/**\n        # \u52a0\u4e0aStripPrefix=1\uff0c\u5426\u5219\u8f6c\u53d1\u5230\u540e\u7aef\u670d\u52a1\u65f6\u4f1a\u5e26\u4e0aconsumer\u524d\u7f00\n        filters:\n        - StripPrefix=1\n\n# \u6ce8\u518c\u4e2d\u5fc3\neureka:\n  instance:\n    prefer-ip-address: true\n  client:\n    service-url:\n      defaultZone: http:\/\/zy:zy123@localhost:10025\/eureka\/\n\n# \u66b4\u9732\u76d1\u63a7\u7aef\u70b9\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: &#039;*&#039;\n  endpoint:\n    health:\n      show-details: always<\/code><\/pre>\n<h3>consumer-service<\/h3>\n<p>consumer-service \u6d88\u8d39\u8005\u670d\u52a1\uff0c\u901a\u8fc7\u7f51\u5173\u8def\u7531\u8f6c\u53d1\u5230\u6d88\u8d39\u8005\u670d\u52a1\uff0c\u5e76\u8fd4\u56de\u4fe1\u606f\u56de\u53bb<\/p>\n<p>\u9879\u76ee\u5f15\u7528\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-xml\">&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n    &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;\/artifactId&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n<p>\u5728\u4e3b\u7c7b\u4e0a\u542f\u7528\u670d\u52a1\u53d1\u73b0\u6ce8\u518c\u6ce8\u89e3<code>@EnableDiscoveryClient<\/code><\/p>\n<p>\u5728\u914d\u7f6e\u6587\u4ef6\u4e2d\u6dfb\u52a0\u914d\u7f6e\uff1a<\/p>\n<pre><code>server.port=9700\nspring.application.name=consumer-service\neureka.instance.prefer-ip-address=true\n# \u914d\u7f6eeureka-server security\u7684\u8d26\u6237\u4fe1\u606f\neureka.client.serviceUrl.defaultZone=http:\/\/zy:zy123@localhost:10025\/eureka\/<\/code><\/pre>\n<p>\u65b0\u5efa IndexController\uff0c\u6dfb\u52a0\u4e00\u4e2a hello \u65b9\u6cd5\uff0c\u4f20\u5165name\u53c2\u6570\uff0c\u8bbf\u95ee\u540e\u8fd4\u56de hi + name \u5b57\u7b26\u4e32<\/p>\n<pre><code class=\"language-java\">@RestController\npublic class IndexController {\n\n    @RequestMapping(&quot;\/hello&quot;)\n    public String hello(String name){\n        return &quot;hi &quot; + name;\n    }\n}<\/code><\/pre>\n<h3>\u8fd0\u884c\u5e76\u6d4b\u8bd5<\/h3>\n<p>\u5206\u522b\u542f\u52a83\u4e2a\u9879\u76ee\uff0c\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"http:\/\/localhost:10025\">http:\/\/localhost:10025<\/a> \u770beureka\u4e0agateway\u4e0econsumer-service\u5b9e\u4f8b\u662f\u5426\u6ce8\u518c\u6210\u529f\uff0c\u5206\u522b\u57289700\u30019999\u7aef\u53e3<\/p>\n<p>\u901a\u8fc7\u7f51\u5173\u8bbf\u95eeconsumer-service\u7684hello\u65b9\u6cd5\uff0c<a target=\"_blank\" rel=\"noopener\" href=\"http:\/\/localhost:9999\/consumer\/hello?name=joe\">http:\/\/localhost:9999\/consumer\/hello?name=joe<\/a> \uff0c\u5982\u679c\u6210\u529f\u8fd4\u56de\uff0c\u8bf4\u660e\u8bf7\u6c42\u5df2\u7ecf\u8f6c\u53d1\u5230consumer-service\u670d\u52a1\u4e0a\u4e86<\/p>\n<p>\u4ee5\u4e0a\u5b8c\u6210\u4e86\u7f51\u5173\u7684\u57fa\u672c\u4ee3\u7801\uff0c\u4e0b\u9762\u7ee7\u7eed\u4ecb\u7ecd\u4e00\u4e9b\u5e38\u7528\u7684\u8fc7\u6ee4\u5668\uff0c\u901a\u8fc7\u8fc7\u6ee4\u5668\u5b9e\u73b0\u7edf\u4e00\u8ba4\u8bc1\u9274\u6743\u3001\u65e5\u5fd7\u3001\u5b89\u5168\u7b49\u68c0\u9a8c<\/p>\n<h3>GlobalFilter \u5168\u5c40\u8fc7\u6ee4\u5668<\/h3>\n<p>\u5728\u7f51\u5173\u9879\u76ee\u4e2d\u6dfb\u52a0 GlobalFilter \u5168\u5c40\u8fc7\u6ee4\u5668\uff0c\u6253\u5370\u6bcf\u6b21\u8bf7\u6c42\u7684url\uff0c\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-java\">\/**\n * \u5168\u5c40\u8fc7\u6ee4\u5668\n * \u6240\u6709\u8bf7\u6c42\u90fd\u4f1a\u6267\u884c\n * \u53ef\u62e6\u622aget\u3001post\u7b49\u8bf7\u6c42\u505a\u903b\u8f91\u5904\u7406\n *\/\n@Component\npublic class RequestGlobalFilter implements GlobalFilter, Ordered {\n\n    \/\/\u6267\u884c\u903b\u8f91\n    @Override\n    public Mono&lt;Void&gt; filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        ServerHttpRequest serverHttpRequest= exchange.getRequest();\n        String uri = serverHttpRequest.getURI().toString();\n        System.out.println(&quot; uri : &quot; + uri);  \/\/\u6253\u5370\u6bcf\u6b21\u8bf7\u6c42\u7684url\n        String method = serverHttpRequest.getMethodValue();\n        if (&quot;POST&quot;.equals(method)) {\n            \/\/\u4ece\u8bf7\u6c42\u91cc\u83b7\u53d6Post\u8bf7\u6c42\u4f53\n            String bodyStr = resolveBodyFromRequest(serverHttpRequest);\n            \/\/TODO \u5f97\u5230Post\u8bf7\u6c42\u7684\u8bf7\u6c42\u53c2\u6570\u540e\uff0c\u505a\u4f60\u60f3\u505a\u7684\u4e8b\n\n            \/\/\u4e0b\u9762\u7684\u5c06\u8bf7\u6c42\u4f53\u518d\u6b21\u5c01\u88c5\u5199\u56de\u5230request\u91cc\uff0c\u4f20\u5230\u4e0b\u4e00\u7ea7\uff0c\u5426\u5219\uff0c\u7531\u4e8e\u8bf7\u6c42\u4f53\u5df2\u88ab\u6d88\u8d39\uff0c\u540e\u7eed\u7684\u670d\u52a1\u5c06\u53d6\u4e0d\u5230\u503c\n            URI uri = serverHttpRequest.getURI();\n            ServerHttpRequest request = serverHttpRequest.mutate().uri(uri).build();\n            DataBuffer bodyDataBuffer = stringBuffer(bodyStr);\n            Flux&lt;DataBuffer&gt; bodyFlux = Flux.just(bodyDataBuffer);\n\n            request = new ServerHttpRequestDecorator(request) {\n                @Override\n                public Flux&lt;DataBuffer&gt; getBody() {\n                    return bodyFlux;\n                }\n            };\n            \/\/\u5c01\u88c5request\uff0c\u4f20\u7ed9\u4e0b\u4e00\u7ea7\n            return chain.filter(exchange.mutate().request(request).build());\n        } else if (&quot;GET&quot;.equals(method)) {\n            Map requestQueryParams = serverHttpRequest.getQueryParams();\n            \/\/TODO \u5f97\u5230Get\u8bf7\u6c42\u7684\u8bf7\u6c42\u53c2\u6570\u540e\uff0c\u505a\u4f60\u60f3\u505a\u7684\u4e8b\n\n            return chain.filter(exchange);\n        }\n        return chain.filter(exchange);\n    }\n    \/**\n     * \u4eceFlux&lt;DataBuffer&gt;\u4e2d\u83b7\u53d6\u5b57\u7b26\u4e32\u7684\u65b9\u6cd5\n     * @return \u8bf7\u6c42\u4f53\n     *\/\n    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {\n        \/\/\u83b7\u53d6\u8bf7\u6c42\u4f53\n        Flux&lt;DataBuffer&gt; body = serverHttpRequest.getBody();\n\n        AtomicReference&lt;String&gt; bodyRef = new AtomicReference&lt;&gt;();\n        body.subscribe(buffer -&gt; {\n            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());\n            DataBufferUtils.release(buffer);\n            bodyRef.set(charBuffer.toString());\n        });\n        \/\/\u83b7\u53d6request body\n        return bodyRef.get();\n    }\n\n    private DataBuffer stringBuffer(String value) {\n        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);\n\n        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);\n        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);\n        buffer.write(bytes);\n        return buffer;\n    }\n\n    \/\/\u6267\u884c\u987a\u5e8f\n    @Override\n    public int getOrder() {\n        return 1;\n    }\n}<\/code><\/pre>\n<p>\u91cd\u65b0\u8fd0\u884c\u7f51\u5173\u9879\u76ee\uff0c\u5e76\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"http:\/\/localhost:9999\/consumer\/hello?name=joe\">http:\/\/localhost:9999\/consumer\/hello?name=joe<\/a> \uff0c\u67e5\u770b\u63a7\u5236\u53f0\uff0c\u53ef\u770b\u5230 uri \u65e5\u5fd7\u88ab\u6253\u5370\u51fa\u6765\u4e86<\/p>\n<h3>GatewayFilter \u8fc7\u6ee4\u5668<\/h3>\n<p>\u5728\u7f51\u5173\u9879\u76ee\u4e2d\u6dfb\u52a0 GatewayFilter \u8fc7\u6ee4\u5668\uff0c\u6211\u4eec\u7ed9consumer-service \u6dfb\u52a0 token \u8ba4\u8bc1\u8fc7\u6ee4\u5668\uff0c\u548c\u5168\u5c40\u8fc7\u6ee4\u5668\u4e0d\u540c\u7684\u662f\uff0cGatewayFilter\u9700\u8981\u5728\u914d\u7f6e\u6587\u4ef6\u4e2d\u6307\u5b9a\u90a3\u4e2a\u670d\u52a1\u4f7f\u7528\u6b64\u8fc7\u6ee4\u5668\uff0c\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-java\">\/**\n * \u53ef\u5bf9\u5ba2\u6237\u7aefheader \u4e2d\u7684 Authorization \u4fe1\u606f\u8fdb\u884c\u8ba4\u8bc1\n *\/\n@Component\npublic class TokenAuthenticationFilter extends AbstractGatewayFilterFactory {\n\n    private static final String Bearer_ = &quot;Bearer &quot;;\n\n    @Override\n    public GatewayFilter apply(Object config) {\n        return (exchange, chain) -&gt; {\n            ServerHttpRequest request = exchange.getRequest();\n            ServerHttpRequest.Builder mutate = request.mutate();\n            ServerHttpResponse response = exchange.getResponse();\n            try {\n                \/\/String token = exchange.getRequest().getQueryParams().getFirst(&quot;authToken&quot;);\n                \/\/1.\u83b7\u53d6header\u4e2d\u7684Authorization\n                String header = request.getHeaders().getFirst(&quot;Authorization&quot;);\n                if (header == null || !header.startsWith(Bearer_)) {\n                    throw new RuntimeException(&quot;\u8bf7\u6c42\u5934\u4e2dAuthorization\u4fe1\u606f\u4e3a\u7a7a&quot;);\n                }\n                \/\/2.\u622a\u53d6Authorization Bearer\n                String token = header.substring(7);\n                \/\/\u53ef\u628atoken\u5b58\u5230redis\u4e2d\uff0c\u6b64\u65f6\u76f4\u63a5\u5728redis\u4e2d\u5224\u65ad\u662f\u5426\u6709\u6b64key\uff0c\u6709\u5219\u6821\u9a8c\u901a\u8fc7\uff0c\u5426\u5219\u6821\u9a8c\u5931\u8d25\n                if (!StringUtils.isEmpty(token)) {\n                    System.out.println(&quot;\u9a8c\u8bc1\u901a\u8fc7&quot;);\n                    \/\/3.\u6709token\uff0c\u628atoken\u8bbe\u7f6e\u5230header\u4e2d\uff0c\u4f20\u9012\u7ed9\u540e\u7aef\u670d\u52a1\n                    mutate.header(&quot;userDetails&quot;, token).build();\n                } else {\n                    \/\/4.token\u65e0\u6548\n                    System.out.println(&quot;token\u65e0\u6548&quot;);\n                    DataBuffer bodyDataBuffer = responseErrorInfo(response, HttpStatus.UNAUTHORIZED.toString(), &quot;\u65e0\u6548\u7684\u8bf7\u6c42&quot;);\n                    return response.writeWith(Mono.just(bodyDataBuffer));\n                }\n            } catch (Exception e) {\n                \/\/\u6ca1\u6709token\n                DataBuffer bodyDataBuffer = responseErrorInfo(response, HttpStatus.UNAUTHORIZED.toString(), e.getMessage());\n                return response.writeWith(Mono.just(bodyDataBuffer));\n            }\n            ServerHttpRequest build = mutate.build();\n            return chain.filter(exchange.mutate().request(build).build());\n        };\n    }\n\n    \/**\n     * \u81ea\u5b9a\u4e49\u8fd4\u56de\u9519\u8bef\u4fe1\u606f\n     * @param response\n     * @param status\n     * @param message\n     * @return\n     *\/\n    public DataBuffer responseErrorInfo(ServerHttpResponse response, String status, String message) {\n        HttpHeaders httpHeaders = response.getHeaders();\n        httpHeaders.add(&quot;Content-Type&quot;, &quot;application\/json; charset=UTF-8&quot;);\n        httpHeaders.add(&quot;Cache-Control&quot;, &quot;no-store, no-cache, must-revalidate, max-age=0&quot;);\n\n        response.setStatusCode(HttpStatus.UNAUTHORIZED);\n        Map&lt;String,String&gt; map = new HashMap&lt;&gt;();\n        map.put(&quot;status&quot;, status);\n        map.put(&quot;message&quot;, message);\n        DataBuffer bodyDataBuffer = response.bufferFactory().wrap(map.toString().getBytes());\n        return bodyDataBuffer;\n    }\n}<\/code><\/pre>\n<p>\u5728\u914d\u7f6e\u6587\u4ef6\u4e2d\u6307\u5b9aconsumer-service\u670d\u52a1\u4f7f\u7528 TokenAuthenticationFilter \uff0c\u914d\u7f6e\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-yml\">routes:\n- id: consumer-service\n  uri: lb:\/\/consumer-service\n  predicates:\n  - Path=\/consumer\/**\n  filters:\n  # \u8fdb\u884ctoken\u8fc7\u6ee4\n  - TokenAuthenticationFilter\n  - StripPrefix=1<\/code><\/pre>\n<p>\u8fd0\u884c\u9879\u76ee\uff0c\u518d\u6b21\u8bbf\u95ee <a target=\"_blank\" rel=\"noopener\" href=\"http:\/\/localhost:9999\/consumer\/hello?name=joe\">http:\/\/localhost:9999\/consumer\/hello?name=joe<\/a><\/p>\n<pre><code>{message=\u8bf7\u6c42\u5934\u4e2dAuthorization\u4fe1\u606f\u4e3a\u7a7a, status=401}<\/code><\/pre>\n<h3>\u8de8\u57df\u95ee\u9898<\/h3>\n<p>\u524d\u540e\u7aef\u5206\u79bb\u9879\u76ee\u89e3\u51b3\u7f51\u5173\u8de8\u57df\u95ee\u9898\uff0c\u5728\u7f51\u5173\u4e3b\u7c7b\u4e2d\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<pre><code>@Bean\npublic WebFilter corsFilter() {\n    return (ServerWebExchange ctx, WebFilterChain chain) -&gt; {\n        ServerHttpRequest request = ctx.getRequest();\n        if (!CorsUtils.isCorsRequest(request)) {\n            return chain.filter(ctx);\n        }\n\n        HttpHeaders requestHeaders = request.getHeaders();\n        ServerHttpResponse response = ctx.getResponse();\n        HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();\n        HttpHeaders headers = response.getHeaders();\n        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());\n        headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());\n        if (requestMethod != null) {\n            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());\n        }\n        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, &quot;true&quot;);\n        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, &quot;all&quot;);\n        headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, &quot;3600&quot;);\n        if (request.getMethod() == HttpMethod.OPTIONS) {\n            response.setStatusCode(HttpStatus.OK);\n            return Mono.empty();\n        }\n        return chain.filter(ctx);\n    };\n}<\/code><\/pre>\n<p>\u53c2\u8003\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/gitee.com\/zhuyu1991\/spring-cloud\/tree\/master\/gateway\">https:\/\/gitee.com\/zhuyu1991\/spring-cloud\/tree\/master\/gateway<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5173\u4e8e\u7f51\u5173 \u7f51\u5173\u662f\u600e\u4e48\u6f14\u5316\u6765\u7684 \u5355\u4f53\u5e94\u7528\u62c6\u5206\u6210\u591a\u4e2a\u670d\u52a1\u540e\uff0c\u5bf9\u5916\u9700\u8981\u4e00\u4e2a\u7edf\u4e00\u5165\u53e3\uff0c\u89e3\u8026\u5ba2\u6237\u7aef\u4e0e\u5185\u90e8\u670d\u52a1 \u7f51\u5173\u7684\u57fa\u672c [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42],"tags":[439],"class_list":["post-1952","post","type-post","status-publish","format-standard","hentry","category-spring-cloud","tag-spring-cloud-gateway"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1952","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1952"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1952\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1952"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1952"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1952"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}