我有一个Spring授权/资源服务器,用于管理Webflux Oauth2客户端的授权。单独运行就可以了。当我添加Spring Cloud Gateway并通过网关访问Webflux时,我看到授权服务器验证了用户,并且Webflux返回302,但是在网关上,我得到了400个“无效的客户端注册ID”。
授权/资源服务器为;
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
首发;
@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
属性;
server:
port: 8889
服务器配置;
@Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore());
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("myClientId2")
.secret(passwordEncoder.encode("mySecret2"))
.authorizedGrantTypes("authorization_code")
.scopes("all")
.autoApprove(true)
.redirectUris("http://localhost:8083/login/oauth2/code/myAppTwo")
.and()
.withClient("myGatewayId")
.secret(passwordEncoder.encode("myGatewaySecret"))
.authorizedGrantTypes("authorization_code")
.scopes("all")
.autoApprove(true)
.redirectUris("http://localhost:8888/login/oauth2/code/gateway")
;
}
}
安全性
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and().authorizeRequests().anyRequest().authenticated()
.and().formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("x").password(passwordEncoder().encode("123"))
.roles("USER");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
用户响应;
@RestController
public class UserController {
@GetMapping("/user")
public Principal user(Principal principal) {
System.err.println(principal);
return principal;
}
Webflux是;
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
</dependencies>
属性;
server:
port: 8083
spring:
security:
oauth2:
client:
registration:
myAppTwo:
client-name: Test8082
client-id: myClientId2
client-secret: mySecret2
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
myAppTwo:
authorization-uri: http://localhost:8889/oauth/authorize
token-uri: http://localhost:8889/oauth/token
user-info-uri: http://localhost:8889/user
user-name-attribute: name
Webflux端点;
@RestController
public class Controller {
@GetMapping(value = "/test3")
Mono<OAuth2User> getTest3(@AuthenticationPrincipal Mono<OAuth2User> ouser) {
System.err.println("o =" + ouser);
return ouser;
}
}
通过Chrome运行http://localhost:8083/test3
,将我带到授权服务器上的登录页面,然后输入凭据,然后上面的URL返回预期的响应。
当我添加网关时;
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
具有属性;
server:
port: 8888
spring:
autoconfigure:
# TODO: remove when fixed https://github.com/spring-projects/spring-security/issues/17949
# Without this line, Gateway tries to log in using it's own security, rather than deferring to OAuth2
exclude: org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration
cloud:
gateway:
routes:
- id: oauth
predicates:
- Path=/oauth2/**
uri: http://localhost:8889
filters:
- TokenRelay=
- RemoveRequestHeader=Cookie
- id: route2
predicates:
- Path=/2/**
uri: http://localhost:8083
filters:
- RewritePath=/2/(?<myPath>.*), /$\{myPath}
- TokenRelay=
- RemoveRequestHeader=Cookie
security:
oauth2:
client:
registration:
gateway:
client-id: myGatewayId
client-secret: myGatewaySecret
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider: gateway
provider:
gateway:
authorization-uri: http://localhost:8889/oauth/authorize
token-uri: http://localhost:8889/oauth/token
user-info-uri: http://localhost:8889/user
user-name-attribute: name
jwk-set-uri: http://localhost:8889/oauth/token_key
现在,如果我尝试使用http://localhost:8888/2/test3
,我仍将被重定向到登录名。授权服务器上的toString
记录凭据,Webflux仍然提供302,但是网关执行以下操作;
Route matched: route2
Mapping [Exchange: GET http://localhost:8888/2/test3] to Route{id='route2', uri=http://localhost:8083, order=0, predicate=Paths: [/2/**], match trailing slash: true, gatewayFilters=[[[RewritePath /2/(?<myPath>.*) = '/${myPath}'], order = 1], [org.springframework.cloud.security.oauth2.gateway.TokenRelayGatewayFilterFactory$$Lambda$666/1724736027@4b96b22, order = 2], [[RemoveRequestHeader name = 'Cookie'], order = 3]]}
[98bb6dad] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@6fa90b79
Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@1894593a}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@b835727}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@7fd26ad8}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@7cea0110}, order = 0], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.GatewayMetricsFilter@5527b211}, order = 0], [[RewritePath /2/(?<myPath>.*) = '/${myPath}'], order = 1], [org.springframework.cloud.security.oauth2.gateway.TokenRelayGatewayFilterFactory$$Lambda$666/1724736027@4b96b22, order = 2], [[RemoveRequestHeader name = 'Cookie'], order = 3], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@14b0e127}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration$NoLoadBalancerClientFilter@54cf7c6a}, order = 10100], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@468dda3e}, order = 2147483646], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@3d37203b}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@10823d72}, order = 2147483647]]
"GET /2/test3 HTTP/1.1" 302 0 8888 8 ms
Resolved [ResponseStatusException: 400 BAD_REQUEST "Invalid client registration id"] for HTTP GET /oauth2/authorization/myAppTwo
Writing "<html><body><h1>Whitelabel Error Page</h1><p>This application has no configured error view, so you (truncated)...
"GET /oauth2/authorization/myAppTwo HTTP/1.1" 400 312 8888 2 ms
"GET /favicon.ico HTTP/1.1" 302 0 8888 2 ms
"GET /oauth2/authorization/gateway HTTP/1.1" 302 0 8888 2 ms
Writing form fields [grant_type, code, redirect_uri] (content masked)
Decoded [{access_token=2c14f1e7-5d95-4fed-be95-2d8518d1fb48, token_type=bearer, expires_in=40912, scope=all}]
Decoded [{authorities=[{authority=ROLE_USER}], details={remoteAddress=127.0.0.1, sessionId=null, tokenValue=2 (truncated)...]
"GET /login/oauth2/code/gateway?code=fGdiyz&state=ZynriL0UtU37b4rMfDHM7JheNYYo0UnZQvgu4U2kWwQ%3D HTTP/1.1" 302 0 8888 82 ms
"GET / HTTP/1.1" 200 3348 8888 5 ms
通过相同浏览器会话,如果我尝试直接URL http://localhost:8083/test
,它将正确响应(即,我已登录并且8083具有我的凭据)。
我通过调试看到DefaultServerOAuth2AuthorizationRequestResolver.findByRegistrationId
首先检查gateway
的重定向并通过。但是,它尝试进行myAppTwo
重定向的第二遍尝试并失败,该重定向未定义网关的YML,我认为不应该这样做。删除路由oauth
似乎没有任何影响。我从没见过那条路被击中。
那么如何使网关重定向到端口8083上的myAppTwo
?
更新,当我使用KeyCloak而不是我自己的AuthorizationServer / ResourceServer时,会遇到相同的问题。