前言
记录
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();
}
至此,跨域问题完全解决