Spring自定义身份验证过滤器和提供者不调用控制器方法

Posted

技术标签:

【中文标题】Spring自定义身份验证过滤器和提供者不调用控制器方法【英文标题】:Spring custom authentication filter and provider not invoking controller method 【发布时间】:2016-12-05 11:05:47 【问题描述】:

我正在尝试使用最新版本的 Spring Boot、Web 和 Security 实现自定义身份验证逻辑,但我遇到了一些问题。我在类似的问题/教程中尝试了许多解决方案,但没有成功或不了解实际发生的情况。

我正在创建一个具有无状态身份验证的 REST 应用程序,即有一个 REST 端点 (/web/auth/login),它需要用户名和密码并返回一个字符串令牌,然后用于所有其他 REST 端点 ( /api/**) 来识别用户。我需要实现一个自定义解决方案,因为将来身份验证会变得更加复杂,我想了解 Spring Security 的基础知识。

为了实现令牌身份验证,我正在创建自定义过滤器和提供程序:

过滤器:

public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter 

public TokenAuthenticationFilter() 
    super(new AntPathRequestMatcher("/api/**", "GET"));


@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException 
    String token = request.getParameter("token");
    if (token == null || token.length() == 0) 
        throw new BadCredentialsException("Missing token");
    

    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(token, null);

    return getAuthenticationManager().authenticate(authenticationToken);


提供者:

@Component
public class TokenAuthenticationProvider implements AuthenticationProvider 
@Autowired
private AuthenticationTokenManager tokenManager;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException 
    String token = (String)authentication.getPrincipal();
    return tokenManager.getAuthenticationByToken(token);


@Override
public boolean supports(Class<?> authentication) 
    return UsernamePasswordAuthenticationToken.class.equals(authentication);


配置:

@EnableWebSecurity
@Order(1)
public class TokenAuthenticationSecurityConfig extends WebSecurityConfigurerAdapter 
@Autowired
private TokenAuthenticationProvider authProvider;

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.antMatcher("/api/**")
    .csrf().disable()
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and().addFilterBefore(authenticationFilter(), BasicAuthenticationFilter.class);


@Bean
public TokenAuthenticationFilter authenticationFilter() throws Exception 
    TokenAuthenticationFilter tokenProcessingFilter = new TokenAuthenticationFilter();
    tokenProcessingFilter.setAuthenticationManager(authenticationManager());
    return tokenProcessingFilter;


@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.authenticationProvider(authProvider);


在提供者(以及登录过程)中使用的AuthenticationTokenManager:

@Component
public class AuthenticationTokenManager 
private Map<String, AuthenticationToken> tokens;

public AuthenticationTokenManager() 
    tokens = new HashMap<>();


private String generateToken(AuthenticationToken authentication) 
    return UUID.randomUUID().toString();


public String addAuthentication(AuthenticationToken authentication) 
    String token = generateToken(authentication);
    tokens.put(token, authentication);
    return token;


public AuthenticationToken getAuthenticationByToken(String token) 
    return tokens.get(token);

会发生什么: 我在请求中附加了一个有效令牌到“/api/bla”(这是一个返回一些 Json 的 REST 控制器)。过滤器和提供者都被调用。 问题是,浏览器被重定向到“/”而不是调用 REST 控制器的请求方法。这似乎发生在 SavedRequestAwareAuthenticationSuccessHandler 中,但是为什么要使用这个处理程序呢?

我试过了

实现一个空的成功处理程序,导致 200 状态码并且仍然不调用控制器 在简单的 GenericFilterBean 中进行身份验证并通过 SecurityContextHolder.getContext().setAuthentication(authentication) 设置身份验证对象,这会导致“错误凭据”错误页面。

我想了解为什么在我对令牌进行身份验证后没有调用我的控制器。除此之外,是否有一种“Spring”方式来存储令牌而不是将其存储在 Map 中,例如 SecurityContextRepository 的自定义实现?

我真的很感激任何提示!

【问题讨论】:

有什么可行的解决方案吗?我们有同样的问题,任何提示或工作代码都会很好。 【参考方案1】:

在构造函数中使用 setContinueChainBeforeSuccessfulAuthentication(true)

【讨论】:

【参考方案2】:

可能有点晚了,但我遇到了同样的问题并添加:

@Override
protected void successfulAuthentication(
        final HttpServletRequest request, final HttpServletResponse response,
        final FilterChain chain, final Authentication authResult)
        throws IOException, ServletException 
    chain.doFilter(request, response);

我的 AbstractAuthenticationProcessingFilter 实现成功了。

【讨论】:

我建议不要替换实现,而是先调用 super

以上是关于Spring自定义身份验证过滤器和提供者不调用控制器方法的主要内容,如果未能解决你的问题,请参考以下文章

spring AOP 和自定义注解进行身份验证

Grails 3.2.4:未调用自定义身份验证过滤器

使用 Java Config 的 Spring Security 自定义身份验证过滤器

如何在 Spring 身份验证管理器之前执行自定义处理程序

甚至没有调用自定义 Spring Security 身份验证提供程序?

Spring-security/Grails 应用程序 - 未调用自定义 WebSecurity 配置