带有 permitAll() 和过期身份验证令牌的 URL 的 Spring Security

Posted

技术标签:

【中文标题】带有 permitAll() 和过期身份验证令牌的 URL 的 Spring Security【英文标题】:Spring Security for URL with permitAll() and expired Auth Token 【发布时间】:2015-06-12 18:42:43 【问题描述】:

我正在使用带有 Spring Security、自定义 GenericFilterBean 和 AuthenticationProvider 实现的 Spring 4。除了用于创建新会话的 URL 之外,我主要保护 URL:/v2/session(例如,基于用户名和密码登录并返回要使用的 Auth Token在后续需要认证的请求中)配置如下:

@Configuration
@ComponentScan(basePackages="com.api.security")
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

@Autowired
private ApiAuthenticationProvider apiAuthenticationProvider;

@Autowired
private AuthTokenHeaderAuthenticationFilter authTokenHeaderAuthenticationFilter;

@Autowired
private AuthenticationEntryPoint apiAuthenticationEntryPoint;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) 
    auth.authenticationProvider(apiAuthenticationProvider);


@Override
protected void configure(HttpSecurity http) throws Exception 
    http
        .addFilterBefore(authTokenHeaderAuthenticationFilter, BasicAuthenticationFilter.class) // Main auth filter
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.authorizeRequests()
        .antMatchers(HttpMethod.POST, "/v2/session").permitAll()
        .anyRequest().authenticated();

    http.exceptionHandling()
        .authenticationEntryPoint(apiAuthenticationEntryPoint);


authTokenHeaderAuthenticationFilter 在每个请求上运行并从请求标头中获取 Token:

/**
 * Main Auth Filter. Always sets Security Context if the Auth token Header is not empty
 */
@Component
public class AuthTokenHeaderAuthenticationFilter extends GenericFilterBean 

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 
    final String token = ((HttpServletRequest) request).getHeader(RequestHeaders.AUTH_TOKEN_HEADER);
    if (StringUtils.isEmpty(token)) 
        chain.doFilter(request, response);
        return;
    

    try 
            AuthenticationToken authRequest = new AuthenticationToken(token);

              SecurityContextHolder.getContext().setAuthentication(authRequest);
        
     catch (AuthenticationException failed) 
        SecurityContextHolder.clearContext();

        return;
    

    chain.doFilter(request, response); // continue down the chain

自定义 apiAuthenticationProvider 将尝试根据标头中提供的令牌对所有请求进行身份验证,如果身份验证不成功 - 抛出 AccessException 并且客户端将收到 HTTP 401 响应:

@Component
public class ApiAuthenticationProvider implements AuthenticationProvider 

@Autowired
private remoteAuthService remoteAuthService;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException 

    AuthenticationToken authRequest = (AuthenticationToken) authentication;
    String identity = null;

    try 
        identity = remoteAuthService.getUserIdentityFromToken(authRequest.getToken());
     catch (AccessException e) 
        throw new InvalidAuthTokenException("Cannot get user identity from the token", e);
    

    return new AuthenticationToken(identity, authRequest.getToken(), getGrantedAuthorites());
    
    

这对于需要身份验证的请求非常有效。这适用于没有身份验证标头的 /v2/session 请求。但是,对于 /v2/session 请求,在标头(或 cookie 中 - 未在代码示例中显示)具有过期的 Auth 令牌;这有时可能会发生,如果客户端未清除标头或继续发送带有请求的 cookie)安全上下文将被初始化,apiAuthenticationProvider 将抛出异常并以 HTTP 401 响应客户端。

由于 /v2/session 已配置为

http.authorizeRequests()
        .antMatchers(HttpMethod.POST, "/v2/session").permitAll()

我希望 Spring Security 在调用 ApiAuthenticationProvider.authenticate() 之前确定这一点。对于配置为 permitAll() 的 URL,过滤器或身份验证提供程序应该如何忽略/不抛出异常?

【问题讨论】:

【参考方案1】:

在执行请求授权检查之前触发 Spring 安全过滤器。为了使授权检查起作用,假设请求已经通过过滤器并且已经设置了 Spring 安全上下文(或没有,取决于是否传入了身份验证凭据)。

在您的过滤器中,如果令牌不存在,您会检查是否继续过滤器链处理。不幸的是,如果是,那么它将被传递给您的提供商进行身份验证,这会引发异常,因为令牌已过期,因此您将获得 401。

对于您认为公开的 URL,最好的办法是绕过过滤器执行。您可以在过滤器本身或配置类中执行此操作。将以下方法添加到您的 SecurityConfig 类中:

@Override
public void configure(WebSecurity webSecurity) 
  webSecurity.ignoring().antMatchers(HttpMethod.POST, "/v2/session");

这样做的目的是完全绕过您的AuthTokenHeaderAuthenticationFilter 获取POST /v2/sessions URL。

【讨论】:

看看这个帖子,它也回答了你的问题:***.com/questions/19917671/… @grigori 如何确保我的请求仍然通过安全过滤器链的其余部分,例如 csrf 保护等。然后我需要在 permitAll() 中有那个 URL。正确的?综上所述,我将如何配置需要其余 Spring Security 过滤器链服务的公共请求? 3 年了,仍然没有更好的解决方案,这让我有点担心!

以上是关于带有 permitAll() 和过期身份验证令牌的 URL 的 Spring Security的主要内容,如果未能解决你的问题,请参考以下文章

尝试进行身份验证时 JWT 令牌过期?

Firebase Android 身份验证失败:expired_token(身份验证令牌已过期)

如何使我的身份验证令牌不会过期?

使用访问令牌和刷新令牌保持身份验证

为啥我的 JWT 不记名身份验证在令牌说 5 分钟后将令牌识别为过期?

单次使用后如何使两因素身份验证令牌过期