为啥这个基于令牌的 Spring Security 过滤器没有被调用?

Posted

技术标签:

【中文标题】为啥这个基于令牌的 Spring Security 过滤器没有被调用?【英文标题】:Why does this token based Spring Security filter not get called?为什么这个基于令牌的 Spring Security 过滤器没有被调用? 【发布时间】:2015-07-23 22:14:46 【问题描述】:

我目前正在从事一个使用 Angular JS 和 Spring REST 服务的项目。在过去的几天里,我一直在尝试为系统添加一些安全性 (see my previous post)。我正在实现基于令牌的安全性。

我得到了基本的东西,但是当请求完成时 XAuthTokenFilter 没有被调用。我不知道为什么,我认为这是我忽略的非常简单的事情。相关类:

XAuthTokenFilter(doFilter 在每个请求中都被调用)

public class XAuthTokenFilter extends GenericFilterBean 

    private final static String XAUTH_TOKEN_HEADER_NAME = "x-auth-token";

    private UserDetailsService detailsService;

    private TokenProvider tokenProvider;

    public XAuthTokenFilter(UserDetailsService detailsService, TokenProvider tokenProvider) 
        this.detailsService = detailsService;
        this.tokenProvider = tokenProvider;
    

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException 
        try 
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            String authToken = httpServletRequest.getHeader(XAUTH_TOKEN_HEADER_NAME);
            if (StringUtils.hasText(authToken)) 
                String username = this.tokenProvider.getUserNameFromToken(authToken);
                UserDetails details = this.detailsService.loadUserByUsername(username);
                if (this.tokenProvider.validateToken(authToken, details)) 
                    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(details, details.getPassword(), details.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(token);
                
            
            filterChain.doFilter(servletRequest, servletResponse);
         catch (Exception ex) 
            throw new RuntimeException(ex);
        
    

XAuthTokenConfigurer

public class XAuthTokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> 

    private TokenProvider tokenProvider;

    private UserDetailsService detailsService;

    public XAuthTokenConfigurer(UserDetailsService detailsService, TokenProvider tokenProvider) 
        this.detailsService = detailsService;
        this.tokenProvider = tokenProvider;
    

    @Override
    public void configure(HttpSecurity http) throws Exception 
        XAuthTokenFilter customFilter = new XAuthTokenFilter(detailsService, tokenProvider);
        http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
    

安全配置

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 
    @Inject
    private Http401UnauthorizedEntryPoint authenticationEntryPoint;

    @Inject
    private UserDetailsService userDetailsService;

    @Inject
    private TokenProvider tokenProvider;

    @Bean
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder();
    

    @Inject
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    

    @Override
    public void configure(WebSecurity web) throws Exception 
        web.ignoring()
                .antMatchers("/scripts/**/*.js,html");
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .csrf()
                .disable()
                .headers()
                .frameOptions()
                .disable()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/api/protected/**").authenticated()
                .antMatchers("/api/open/**").permitAll()
                .and()
                .apply(securityConfigurerAdapter());

    

    private XAuthTokenConfigurer securityConfigurerAdapter() 
        return new XAuthTokenConfigurer(userDetailsService, tokenProvider);
    

    /**
     * This allows SpEL support in Spring Data JPA @Query definitions.
     *
     * See https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions
     */
    @Bean
    EvaluationContextExtension securityExtension() 
        return new EvaluationContextExtensionSupport() 
            @Override
            public String getExtensionId() 
                return "security";
            

            @Override
            public SecurityExpressionRoot getRootObject() 
                return new SecurityExpressionRoot(SecurityContextHolder.getContext().getAuthentication()) ;
            
        ;
    

我真的不知道为什么它没有被调用,我的 url antMatcher() 语句有问题吗?

我的上下文可能会包含在内:

@Configuration
@EnableWebMvc
@Import(AppContext.class) // The context from my backend which is included as a dependency
@ComponentScan("com.example.springsecuritytest")
public class RestContext extends WebMvcConfigurerAdapter 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) 
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    

WebAppInitializer

public class WebAppInitializer implements WebApplicationInitializer 
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException 
        //RootContext
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(RestContext.class);

        // Add RootContext using ContextLoaderListener
        servletContext.addListener(new ContextLoaderListener(rootContext));

        // Registering and mapping dispatcher servlet
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    

【问题讨论】:

您收到的请求和响应是什么?即使您在请求中有一个有效的令牌,您是否收到了未经授权的异常? 不,它没有被调用,用断点尝试过,但没有到达。所有请求都成功,即使是没有 x-auth-token 标头的请求(我使用 Postman 浏览器插件尝试请求),所以令牌甚至没有得到验证,因为过滤器显然从未运行。 所有 url 的(/api/open 和 /api/protected)都通过而没有被阻止,也没有首先通过过滤器。 所以根本没有调用安全过滤器链?我有一个使用 xml 配置的自定义令牌身份验证过滤器的类似设置,我的 web.xml 中有以下内容:&lt;filter&gt; &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt; &lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt; &lt;/filter&gt; &lt;filter-mapping&gt; &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt; &lt;url-pattern&gt;/*&lt;/url-pattern&gt; &lt;/filter-mapping&gt; 是的,看起来是这样,但是这条线会将它添加到链中,对吗? http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); 真的很奇怪,虽然我从来没有指定过滤器映射到哪个 url,我应该在任何地方这样做吗?在我从 (JHipster) 获得的示例中,过滤器没有使用 @Filter 注释并且没有 URL 映射,您认为这是必要的吗? 【参考方案1】:

请查看此git repo。我已经准备了一个非常基本的设置,可以在有和没有tokenAuthenticationFilter 的情况下测试安全性。过滤器实现只是一个模拟,只要存在标头,就设置有效的身份验证,而不管其值如何。另请注意两个应用程序WebApplicationInitializers,它们符合Servlet 3.0 并以编程方式(而不是web.xml)配置Servlet 容器。

【讨论】:

感谢您的努力和示例!但这似乎是一种完全不同的工作方式,我目前并没有真正寻找,我想保留配置,就像现在一样,这应该根据 JHipster 工作。好像我错过了一个 JHipster 骨架文件,或者我忽略了其他东西。 我非常明确地再次感谢您编写这个示例,它肯定会派上用场!! 我已经尽量减少配置。如果你查看StatelessWebSecurityConfig,你应该注意到我做的只有几件事与你不同: 1. 保护所有 URI; 2. 使用内存授权与用户详细信息服务 3. 将tokenAuthFilter 放在basicAuthFilter 之前并调用httpBasic() 嗯,好吧,我会试着让我的和你的更相似,看看它是否能解决任何问题! 刚刚尝试放置 .anyRequest().authenticated() 而不是 antMatcher() 语句并在末尾添加 .and().httpBasic(),但所有请求仍然通过而不会以任何方式被捕获或阻止:/

以上是关于为啥这个基于令牌的 Spring Security 过滤器没有被调用?的主要内容,如果未能解决你的问题,请参考以下文章

spring boot spring security 基于自定义令牌的身份验证和自定义授权

Spring-Security-Oauth2 基于JDBC存储令牌和RBAC权限认证

为啥我收到无效的 csrf 令牌?

带有 JWT 令牌的 Spring Security 和 Websocket

spring security oauth2 禁用基于 jsessionid 的会话

在 Spring Security 中检查令牌的 NullPointerException