Вход в Oauth2 Client через Cloud Gateway дает 400 Неверный идентификатор регистрации клиента - PullRequest
0 голосов
/ 20 октября 2019

У меня есть сервер Spring Authorization / Resource, управляющий авторизацией клиента Webflux Oauth2. Сам по себе это работает хорошо. Когда я добавляю Spring Cloud Gateway и получаю доступ к Webflux через шлюз, я вижу, что сервер авторизации проверяет пользователя, а Webflux возвращает 302, но на шлюзе я получаю 400 «Неверный идентификатор регистрации клиента».

Авторизация/ Сервер ресурсов:

    <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>

Starter;

@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;
    }
}

Запуск http://localhost:8083/test3 через Chrome перенесет меня на страницу входа на сервере авторизации, после чего я введу учетные данные, а затем приведенный выше 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, похоже, никак не повлияет. Я никогда не вижу, чтобы этот маршрут попадал.

Так как мне заставить шлюз перенаправить на myAppTwo на порту 8083?

Обновление Когда я использую KeyCloak вместомой собственный AuthorizationServer / ResourceServer Я получаю ту же проблему.

...