Spring Security 自定义 AuthenticationProvider 验证方法调用了两次

Posted

技术标签:

【中文标题】Spring Security 自定义 AuthenticationProvider 验证方法调用了两次【英文标题】:Spring Security custom AuthenticationProvider authenticate method called twice 【发布时间】:2018-02-06 21:53:27 【问题描述】:

我正在开发一个使用 API 密钥进行身份验证的 Spring Boot。我创建了一个自定义身份验证提供程序,并且身份验证方法被调用了两次。谁能告诉我为什么它被调用了两次?

这是我的身份验证方法:

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException 
    ApiAuthenticationToken authenticationToken = (ApiAuthenticationToken) authentication;

    /**
     * Authenticate the token
     */
    ValidateApiKeyRequest request = new ValidateApiKeyRequest(authenticationToken.getApiKey());
    ValidateApiKeyResp resp = getValidateApiKeyCommand().execute(request);

    /**
     * Populate and return a new authenticaiton token
     */
    return createSuccessAuthentication(resp);

这是 createSuccessAuthentication 方法:

protected Authentication createSuccessAuthentication(final ValidateApiKeyResp resp) 
    List<GrantedAuthority> authorities = Lists.newArrayList();
    authorities.add(new SimpleGrantedAuthority("API_KEY"));
    return new ApiAuthenticationToken(resp.getApiKey(), authorities, true);

这是 ApiAuthenticationToken 构造函数:

public ApiAuthenticationToken(final ApiKey apiKey, Collection<? extends GrantedAuthority> authorities, boolean authenticated) 
    super(authorities);
    setAuthenticated(true);
    this.apiKey = apiKey;

这是我的安全配置:

protected void configure(HttpSecurity http) throws Exception 
    http.antMatcher(CONFIGURATION_MATCHER)
        .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint())
        .and()
        .addFilterBefore(apiKeyAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .csrf().disable()
        .authorizeRequests().antMatchers(CONFIGURATION_MATCHER).authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authenticationProvider(apiKeyAuthenticationProvider());

【问题讨论】:

你是否碰巧在某处定义了 configure(AuthenticationManagerBuilder)? 不,我没有。我在类似的帖子上看到过这个答案,所以我知道这个问题。 还有其他建议吗? 您有重现该问题的小型示例项目吗? 【参考方案1】:

以防万一其他人有这个问题:

问题与我的 spring 安全配置有关。我有几个用 @Bean 注释的方法 - 见下文

@Bean
public ApiKeyAuthenticationProvider apiKeyAuthenticationProvider() 
    return new ApiKeyAuthenticationProvider(getValidateApiKeyCommand());


@Bean
public RestAuthenticationEntryPoint restAuthenticationEntryPoint() 
    return new RestAuthenticationEntryPoint();


@Bean
public ApiKeyAuthenticationFilter apiKeyAuthenticationFilter() throws Exception 
    ApiKeyAuthenticationFilter apiKeyAuthenticationFilter = new ApiKeyAuthenticationFilter();
    apiKeyAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
    apiKeyAuthenticationFilter.setAuthenticationSuccessHandler(new ApiKeyAuthenticationSuccessHandler());
    apiKeyAuthenticationFilter.setAuthenticationFailureHandler(new ApiKeyAuthenticationFailureHandler());
    return apiKeyAuthenticationFilter;

但是这些 bean 在 configure(HttpSecurity http) 方法中再次注册。

protected void configure(HttpSecurity http) throws Exception 

    http.antMatcher(CONFIGURATION_MATCHER)
        .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint())
        .and()
        .addFilterBefore(apiKeyAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .csrf().disable()
        .authorizeRequests().antMatchers(CONFIGURATION_MATCHER).authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authenticationProvider(apiKeyAuthenticationProvider());

解决方法是删除 @Bean 注释。现在看起来很明显:)

【讨论】:

以上是关于Spring Security 自定义 AuthenticationProvider 验证方法调用了两次的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security入门(3-6)Spring Security 的鉴权 - 自定义权限前缀

Spring Security 之自定义UserDetails

Spring Boot + Spring Security自定义用户认证

Gateway 整合 Spring Security鉴权

Spring Security-用户密码自定义加密

Spring Security —— 自定义配置