В моем проекте у меня есть API-шлюз (Spring Cloud API-шлюз), сервер авторизации (Oauth2) и сервер ресурсов (микросервис). (У меня также есть сервер CAS, но теперь его можно игнорировать для простоты)
Я просто хочу использовать шлюз API для перенаправления клиентских запросов.
- Если пользователь не аутентифицирован, запрос должен быть отправлен на сервер авторизации, и после завершения процесса аутентификации и авторизации сервер авторизации должен вернуть JSESSION и JWT в заголовке access_token. После этого шлюз API должен вернуть JSESSION AND JWT клиенту.
- Если пользователь аутентифицирован, запрос должен быть отправлен на сервер ресурсов.
Таким образом, шлюз API будет без состояния и используется только для перенаправления запросов. Но после успешного входа в систему устанавливаются файлы cookie JSESSION и SESSION, а JWT не отправляется клиенту. (Насколько можно судить из журналов, API-шлюз имеет JWT, но вместо того, чтобы просто отправлять его клиенту, сохраняет его с помощью ключа SESSION и отправляет ключ SESSION)
Как сделать шлюз API без состояния и отправить его JWT в заголовке access_token для клиента?
Вот мой код:
Сервер авторизации:
@EnableWebSecurity
@Order(1)
public class MySecurityConfigurationDev extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login/**", "/oauth/**", "/userinfo")
.permitAll()
.and()
.formLogin().permitAll().and()
.authorizeRequests().anyRequest().authenticated();
http.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("{noop}pass").roles("ADMIN");
}
}
@Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public KeyPair keyPair() {
return new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "mystorepass".toCharArray()).getKeyPair("mytestkey");
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair());
return converter;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").
checkTokenAccess("permitAll()").
allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("first-client").secret("{noop}noonewilleverguess")
.authorities("ADMIN")
.authorizedGrantTypes("client_credentials", "password", "authorization_code", "refresh_token")
.scopes("custom_mod")
.accessTokenValiditySeconds(60*30)
.refreshTokenValiditySeconds(60*60)
.autoApprove(true)
.redirectUris("http://localhost:8085/login/oauth2/code/login-client");
}
}
API-шлюз:
@EnableWebFluxSecurity
public class MySecurityConfiguration {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, ReactiveClientRegistrationRepository clientRegistrationRepository) {
http.authorizeExchange().anyExchange().authenticated().and().oauth2Login();
return http.build();
}
}
logging:
level:
root: TRACE
org.springframework.cloud.gateway: DEBUG
reactor.netty.http.client: DEBUG
com.netflix.discovery.DiscoveryClient: error
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator: error
com.netflix.discovery.shared.resolver.aws.ConfigClusterResolver: error
server:
port: 8085
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8010/eureka
spring:
application:
name: gateway-server
cloud:
gateway:
default-filters:
- RemoveRequestHeader=Cookie
- TokenRelay=
routes:
- id: user_route
predicates:
- Path=/gateway-user/**
filters:
- RewritePath=/gateway-user/(?<segment>.*), /user/$\{segment}
- RemoveRequestHeader=Cookie
- name: Hystrix
args:
name: fallbackCommand
fallbackUri: forward:/fallback/error
uri: "lb://USER-RESOURCE-SERVER"
- id: address_route
predicates:
- Path=/gateway-address/**
filters:
- RewritePath=/gateway-address/(?<segment>.*), /address/$\{segment}
uri: "lb://ADDRESS-RESOURCE-SERVER"
security:
oauth2:
client:
registration:
login-client:
provider: uaa
client-id: first-client
client-secret: noonewilleverguess
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
scope: custom_mod
provider:
uaa:
authorization-uri: http://localhost:8094/oauth/authorize
token-uri: http://localhost:8094/oauth/token
user-info-uri: http://localhost:8094/userinfo
user-name-attribute: user_name
user-info-authentication-method: form
** Версия Spring Boot 2.1.6-RELEASE для обоих серверов.