Настройка нескольких поставщиков аутентификации в сочетании с oauth2 + jwt - PullRequest
0 голосов
/ 08 июня 2019

Я пытаюсь настроить свой Spring Boot 2, OAuth2 с сервером авторизации JWT, который должен выполнить следующие действия:

  1. взять имя пользователя / пароль, создать объект CustomUserDetails на основе данных БД и данных Salesforce и вернуть маркер JWT, если аутентификация прошла (это работает)
  2. взять токен обновления и вернуть новый токен обновления JWT и доступа (это работает)
  3. (NEW) возьмите токен обновления, проверьте базу данных на наличие сохраненного идентификатора токена, прежде чем возвращать новый токен доступа + обновления JWT (это проблема). Суть в том, чтобы убедиться, что только одно устройство подключено к учетные данные пользователя за раз.

Чтобы выполнить # 3, я пытаюсь настроить PreAuthenticatedAuthenticationProvider, задав ему пользовательский UserDetailsService, и бин AuthenticationManagerBuilder должен быть передан как настроенному PreAuthenticatedAuthenticationProvider, так и DaoAuthenticationProvider.

Если предположить, что я иду в правильном направлении, вот мой код конфигурации:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    @Autowired
    PasswordEncoder passwordEncoder;

    //implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>
    @Autowired
    CustomPreauthenticatedUserDetailsService customPreauthenticatedUserDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/actuator/**").permitAll()
        .antMatchers("/swagger-ui**","/webjars/**","/swagger-resources/**", "/v2/**").permitAll()
        .antMatchers("/oauth/token/revokeById/**").permitAll()
        .antMatchers("/oauth/token/**").permitAll()
        .anyRequest().authenticated()
        .and().csrf().disable();
    }
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder authenticationManager) throws Exception {
        authenticationManager.authenticationProvider(preauthAuthProvider());
        authenticationManager.authenticationProvider(dbAuthProvider());
    }

    @Bean
    @Qualifier(value = "authenticationManagerBean")
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean(value="preAuthProvider")
    public PreAuthenticatedAuthenticationProvider preauthAuthProvider() {
        PreAuthenticatedAuthenticationProvider preauthAuthProvider = new PreAuthenticatedAuthenticationProvider();
        LOGGER.info("Setting customPreauthenticatedUserDetailsService");
        preauthAuthProvider.setPreAuthenticatedUserDetailsService(customPreauthenticatedUserDetailsService);
        return preauthAuthProvider;
    }

    @Bean(value="dbAuthProvider")
    public DaoAuthenticationProvider dbAuthProvider() {
        DaoAuthenticationProvider dbAuthProvider = new DaoAuthenticationProvider();
        dbAuthProvider.setUserDetailsService(userDetailsService);
        dbAuthProvider.setPasswordEncoder(passwordEncoder);
        return dbAuthProvider;
    }

}

На стороне AuthorizationServerConfig:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private static final Integer ACCESS_TOKEN_VALIDITY_SECONDS = 300;
    private static final Integer REFRESH_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 60;

    @Autowired
    public AuthorizationServerConfig(AuthenticationManager authenticationManagerBean, PasswordEncoder passwordEncoder, CustomTokenEnhancer customTokenEnhancer, TokenStore tokenStore, JwtAccessTokenConverter accessTokenConverter) {
        this.authenticationManagerBean = authenticationManagerBean;
        this.passwordEncoder = passwordEncoder;
        this.customTokenEnhancer = customTokenEnhancer;
        this.tokenStore = tokenStore;
        this.accessTokenConverter = accessTokenConverter;
    }

    private AuthenticationManager authenticationManagerBean;
    private PasswordEncoder passwordEncoder;
    private CustomTokenEnhancer customTokenEnhancer;
    private JwtAccessTokenConverter accessTokenConverter;
    private TokenStore tokenStore;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer authorizationServerSecurityConfigurer) {
        authorizationServerSecurityConfigurer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {

        clientDetailsServiceConfigurer.inMemory().withClient("someclient")
                .authorizedGrantTypes("password", "refresh_token")
                .scopes("read", "write").accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
                .refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS)
                .secret(this.passwordEncoder.encode("somepassword"));
    }

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer, accessTokenConverter));
        endpoints.tokenStore(tokenStore).tokenEnhancer(tokenEnhancerChain)
            .authenticationManager(this.authenticationManagerBean);
    }
}

После запуска приложения появляются первые признаки проблемы:

s.c.a.w.c.WebSecurityConfigurerAdapter$3 : No authenticationProviders and no parentAuthenticationManager defined. Returning null.

Как ни странно, это кажется ложью или говорит о чем-то другом, потому что, когда я пытаюсь войти в систему с именем пользователя и паролем - это работает, и я получаю и свои обновления, и токены доступа. Отладчик показывает, что ProviderManager имеет и мои собственные классы провайдера аутентификации, и использует их для аутентификации.

Однако, когда я пытаюсь получить новый токен с токеном обновления, отладчик показывает, что приложение, похоже, использует другой путь ProviderManager - этот ProviderManager имеет только PreAuthenticatedAuthenticationProvider в своем списке, и что провайдер не тот, который я настроил. PreAuthenticatedAuthenticationProvider пытается получить UserDetailsService из WebSecurityConfigurerAdapter$UserDetailsServiceDelegator, и в результате возникает ошибка:

2019-06-08 13:27:24.764 ERROR 8731 --- [nio-8080-exec-3] o.s.s.o.provider.endpoint.TokenEndpoint  : Handling error: IllegalStateException, UserDetailsService is required.

Итак, какой шаг настройки я пропустил? Почему вызов токена обновления происходит в другом месте? Это authenticationManagerBean, который я передаю в AuthorizationServerConfig?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...