即使在允许的 url 上也可以访问 Jwt 过滤器

Posted

技术标签:

【中文标题】即使在允许的 url 上也可以访问 Jwt 过滤器【英文标题】:Jwt filter accessed even on permitted urls 【发布时间】:2018-03-22 20:07:08 【问题描述】:

我正在开发一个 Spring Boot 应用程序,我在其中定义了要执行的过滤器以管理获取和验证令牌。所以在我的 Web 安全类配置中,我设法做到了:

@Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
        httpSecurity
                // we don't need CSRF because our token is invulnerable
                .csrf().disable()

                // don't create session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

                .authorizeRequests()
                // .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

                // allow anonymous resource requests
                .antMatchers(HttpMethod.GET, "/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
                .antMatchers("/auth/**").permitAll()
                .antMatchers("/places/public").permitAll()

                .anyRequest().authenticated();

        // Custom JWT based security filter
        httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

        // disable page caching
        httpSecurity.headers().cacheControl();
    

authenticationTokenFilterBean 是一个用@Bean 注解的方法,它返回我的过滤器的一个实例:

public class JwtFilter extends OncePerRequestFilter

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtService jwtService;

    @Value("$jwt.header")
    private String authorizationHeader;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException 

        String token = httpServletRequest.getHeader(authorizationHeader);

        String username = jwtService.getUsernameFromToken(token);

        if(username!=null && SecurityContextHolder.getContext().getAuthentication() ==null)
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            if(jwtService.isTokenValid(token, userDetails))
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            
        

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    


由于我正在扩展 OncePerRequestFilter,因此每个请求仅调用一次此过滤器,而 GenericFilter 不是这种情况,它需要由 servlet 执行一次,而 Spring 安全性需要执行另一次。

我遇到的问题是我的配置类中描述的蚂蚁匹配器也被过滤器拦截,即使我使用 permitAll() 方法允许它们,我试图从 WebSecurityConfigurerAdapter 覆盖 configure(WebSecurity web) 方法并忽略他们,但仍然没有工作。

如何配置 Spring 安全性以跳过这些请求的过滤器?我已经检查了这个问题Spring Security JWT Filter applies on all requests,但它没有解决方案。

谢谢

【问题讨论】:

对于某些 URL,您始终可以在不使用过滤器的情况下构建另一个/第二个配置。 【参考方案1】:

仍在过滤器内部处理,但方法更清洁。 您可以覆盖OncePerRequestFiltershouldNotFilter(HttpServletRequest request) 方法并根据URL 模式返回true 以跳过过滤。

private static List<String> skipFilterUrls = Arrays.asList("/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js");

@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException 

  return skipFilterUrls.stream().anyMatch(url -> new AntPathRequestMatcher(url).matches(request));

【讨论】:

一种非常有效的绕过过滤器的方法,无需更改安全结构。谢谢!【参考方案2】:

更改您的 WebSecurityConfiguration 类,它看起来像:

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception 
    httpSecurity
            // we don't need CSRF because our token is invulnerable
            .csrf().disable()

            // don't create session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

            .authorizeRequests()                

            .anyRequest().authenticated();

    // Custom JWT based security filter
    httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);

    // disable page caching
    httpSecurity.headers().cacheControl();


 @Override
public void configure(WebSecurity web) throws Exception 
    // Ignore spring security in these paths
    web.ignoring().antMatchers("/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js","/auth/**","/places/public");

您必须从 HttpSecurity 配置方法中删除 permitAll 匹配器并将其放入 WebSecurity 配置方法中。

【讨论】:

【参考方案3】:

我是这样避免这个问题的,大家可以参考一下:

@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException 
    if("/login".equals(req.getServletPath()) && "POST".equalsIgnoreCase(req.getMethod()))
        // ......
    
    filterChain.doFilter(req, res);

【讨论】:

这是在过滤器内部处理的,这是我想要避免的。还是谢谢你

以上是关于即使在允许的 url 上也可以访问 Jwt 过滤器的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 自定义 JWT 过滤器不允许任何没有令牌的请求

即使我使用允许服务器上的所有来源,请求的资源上也没有“Access-Control-Allow-Origin”标头

如何将 jwt 令牌添加到 url?

设置带有过滤器和排序的 HTML 表格

这个 JWT 实现是不是可以防止 XSS 和 CSRF 攻击,同时仍然允许我访问有效负载?

在 Spring Boot 安全性中,无法跳过登录 url 的 OncePerRequestFilter 过滤器(基本上是在初始登录期间获取 JWT 令牌)