Сервер авторизации Spring перенаправляется на страницу входа при запросе токена

В настоящее время у нас есть 2 веб-приложения, которые аутентифицируются в CAS. Связь между ними осуществляется через Basic Auth.

Из соображений безопасности мы хотим переключиться на OAuth2, чтобы избавиться от базовой аутентификации. В то же время мы хотим избавиться от CAS, потому что он не соответствует нашим требованиям.

Итак, цель примерно такая:

Я нахожусь в том состоянии, когда могу войти в систему управления пользователями и манипулировать данными. Второе приложение также корректно перенаправляет меня в 1-е приложение для аутентификации. Аутентификация тоже работает. И 1-е приложение правильно реагирует на 2-е приложение. Затем второе приложение выполняет ожидаемый POST для URL-адреса «oauth2/token». Но первое приложение отвечает 302 на "/login" вместо токена.

Какой пункт я пропустил или мне все еще нужно настроить?

Я ориентировался на следующую документацию: https://docs.spring.io/spring-authorization-server/docs/current/reference/html/getting-started.html.

Мой AuthorizationServerConfig выглядит так:

      @Configuration
public class AuthorizationServerConfig {

  private final SecurityApp app;

  public AuthorizationServerConfig(SecurityApp app) {
    this.app = app;
  }

  @Bean
  @Order(1)
  public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
      throws Exception {

    OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
        new OAuth2AuthorizationServerConfigurer();
    authorizationServerConfigurer
        .authorizationEndpoint(authorizationEndpoint ->
            authorizationEndpoint.consentPage("/oauth2/authorize"))
        .oidc(Customizer.withDefaults());   // Enable OpenID Connect 1.0

    RequestMatcher endpointsMatcher = authorizationServerConfigurer
        .getEndpointsMatcher();

    http
        .securityMatcher(endpointsMatcher)
        .authorizeHttpRequests(authorize ->
            authorize.anyRequest().authenticated()
        )
        .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
        .exceptionHandling(exceptions ->
            exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
        )
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
        .apply(authorizationServerConfigurer);
    return http.build();
  }

  @Bean
  @Order(2)
  public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
      throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated()
        )
        // Form login handles the redirect to the login page from the
        // authorization server filter chain
        .formLogin(Customizer.withDefaults());

    return http.build();
  }

  @Bean
  UserDetailsService users() {
    return app::findUserByLogin;
  }

  // OVERWATCH

  @Bean
  public RegisteredClientRepository registeredClientRepository() {

    return new RegisteredClientRepository() {
      @Override
      public void save(RegisteredClient registeredClient) {
        throw new NotImplementedException();
      }

      @Override
      public RegisteredClient findById(String id) {
        return app.findByClientId(id);
      }

      @Override
      public RegisteredClient findByClientId(String clientId) {
        return app.findByClientId(clientId);
      }
    };
  }

  @Bean
  public JWKSource<SecurityContext> jwkSource() {
    RSAPublicKey publicKey = app.getPublicKey();
    RSAPrivateKey privateKey = (RSAPrivateKey) app.getPrivateKey();
    RSAKey rsaKey = new RSAKey.Builder(publicKey)
        .privateKey(privateKey)
        .keyID(UUID.randomUUID().toString())
        .build();
    JWKSet jwkSet = new JWKSet(rsaKey);
    return new ImmutableJWKSet<>(jwkSet);
  }

  @Bean
  public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
    return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
  }

  @Bean
  public AuthorizationServerSettings authorizationServerSettings() {
    return AuthorizationServerSettings.builder().build();
  }


  // region Password Authenticator

  @Bean
  public PasswordEncoder passwordEncoder() {
    return app.passwordEncoder();
  }

  // endregion
}

И конфигурация из приложения 2 выглядит так:

      @Configuration
@EnableWebSecurity
public class AceSecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
        .antMatcher("/**")
        .authorizeRequests()
        .antMatchers("/oauth/authorize**", "/login**", "/error**")
        .permitAll()
        .and()
        .authorizeRequests()
        .anyRequest().authenticated()
        .and()
        .oauth2Login( oauth2Login -> oauth2Login.defaultSuccessUrl("/index.html") );
  }
}

application.properties из App2:

      spring.security.oauth2.client.registration.<provider-name>.client-id=${app.uuid}
spring.security.oauth2.client.registration.<provider-name>.client-secret=${app.secret}
spring.security.oauth2.client.registration.<provider-name>.scope=openid
spring.security.oauth2.client.registration.<provider-name>.redirect-uri=http://127.0.0.1:8088/login/oauth2/code/<provider-name>
spring.security.oauth2.client.registration.<provider-name>.client-name=${app.name}
spring.security.oauth2.client.registration.<provider-name>.provider=${provider.name}
spring.security.oauth2.client.registration.<provider-name>.client-authentication-method=code
spring.security.oauth2.client.registration.<provider-name>.authorization-grant type=authorization_code

spring.security.oauth2.client.provider.<provider-name>.authorization-uri=http://localhost:8086/oauth2/authorize
spring.security.oauth2.client.provider.<provider-name>.token-uri=http://localhost:8086/oauth2/token
spring.security.oauth2.client.provider.<provider-name>.user-info-uri=http://localhost:8086/oauth2/userinfo?schema=openid
spring.security.oauth2.client.provider.<provider-name>.user-name-attribute=name
spring.security.oauth2.client.provider.<provider-name>.user-info-authentication-method=header
spring.security.oauth2.client.provider.<provider-name>.jwk-set-uri=http://localhost:8086/jwks

Запрос от App2 к App 1, вызывающий перенаправление:

      POST http://localhost:8086/oauth2/token

Body:
grant_type=authorization_code, 
code=Zls0ppjnS_RXyMVPB8fg_eQQgoiUAxRguOMsdyVYQpgd8eDkUDzgz813L0ybovTL7sNj0TDRUHibPfek9NzwULND1mty5WPW2DOtQjTAaEROL3qP7RvyTWXTEzzYe-o,
redirect_uri=[http://127.0.0.1:8088/login/oauth2/code/<provider-name>],
client_id=<app.uuid>

Header:
Accept:"application/json;charset=UTF-8", 
Content-Type:"application/x-www-form-urlencoded;charset=UTF-8"

1 ответ

Для людей, столкнувшихся с такой же или похожей проблемой. Собственно проблема была:

      spring.security.oauth2.client.registration.<provider-name>.client-authentication-method=code.

В базе данных был объявлен другой метод аутентификации.

Еще одна проблема заключалась в объявлении файла PasswordEncoder. Пароли в базе данных хранились BCrypt в зашифрованном виде. Однако общий секрет хранился в незашифрованном виде в базе данных (чтобы иметь возможность отображать его во внешнем интерфейсе). Так что возникла проблема со сравнением этих двух секретов.

Другие вопросы по тегам