前言
记录 Spring Cloud Gateway
整合 Spring Security
及 Oauth2
时跨越问题相关解决过程
项目架构
为了不直接暴露 API
及保护服务器,所有访问都需要经过网关,由网关转发请求到服务器及返回服务器的响应
初遇跨域
跨域其实是很常见的问题,在 Spring
中可以简单的写个 @CrossOrigin
或者全局拦截器之类的解决掉,但在 Spring Cloud Gateway
中这行不通,写注解等方式在路由转发时还是会跨域
官方文档 给出相关跨域配置(实测不好用)
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET
不过你可以轻易的通过Google搜索到如下代码
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
@Bean
public CorsWebFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
简单的在启动类上或者其他什么地方加上后会发现跨域解决了
架构升级
在原系统中加个 Bean
就解决了跨域问题,现在为了进行 权限控制
我们需要给系统加上 Oauth2 认证
当用户访问敏感资源时需要用户登录并授权(类似第三方登录)
这时候 Gateway
既是网关也是 Oauth2
中的 Client
角色,用户授予 Gateway
某些权限可以访问用户相关资源,Gateway
在获得授权后从 UAA
中获得Token
。用户请求到达 Gateway
后每次转发都会带上 Token
,Resource Server
对 Token
进行验证并作出响应。
Gateway
项目相关依赖如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.12.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
再遇跨域
在引入 Oauth2
和 Security
相关依赖后发现又跨域了
可以发现 GET
请求正常,但 POST
等非简单请求遇到Preflight问题,这个问题的一般思路是拦截 OPTION
请求直接返回 200
以便后续真正的请求顺利发起
其实这个问题是花时间最多的,期间写过 Gateway
的拦截器、Security
的拦截器、全局拦截器、Webflux
配置和全局配置等等,没一个能打的
然后考虑到是引入 Oauth2
和 Security
相关依赖后才使原来的 Bean
失效,那可能是优先级的原因,请求在进入定义的 Filter
前就别拦截了。于是尝试提高 Bean
的优先级,但还是无果
最后在审查 Bean
代码时发现在创建 CorsWebFilter
时需要 ConfigurationSource
作为入参,那么拦截了请求的 Filter
会不会也需要相应的 ConfigurationSource
呢?于是将 ConfigurationSource
单独作为 Bean
创建,结果成功解决 Preflight
问题
@Bean
public UrlBasedCorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return source;
}
解决 Preflight
后又遇到 403
问题
查看响应
是CSRF相关,只要关掉就好了
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
ReactiveClientRegistrationRepository clientRegistrationRepository) {
http.oauth2Login();
http.authorizeExchange().anyExchange().authenticated();
http.csrf().disable();
return http.build();
}
至此,跨域问题完全解决