Как сделать API-шлюз без состояний для процесса аутентификации / авторизации с использованием Oauth2? - PullRequest
1 голос
/ 13 марта 2020

В моем проекте у меня есть 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 для обоих серверов.

...