使用 jwt 在 springboot 中始终允许未经授权的请求

Posted

技术标签:

【中文标题】使用 jwt 在 springboot 中始终允许未经授权的请求【英文标题】:unauthorized request are always allowed in springboot using jwt 【发布时间】:2020-11-08 06:58:57 【问题描述】:

我有一个项目,我启用了 jwt 以对其进行授权。问题是,每当我在标头中发送空标头请求或过期的授权代码时,它不会向我发送未经授权的错误,它会在日志中显示令牌无效但允许请求继续工作。这是我的配置代码:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
    

    @Bean
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder(BCryptVersion.$2Y);
    

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
        httpSecurity.csrf().disable()

                .authorizeRequests().antMatchers("/authenticate","/user","/swagger-ui.html","/swagger-ui/**"
                ,"/v3/api-docs/**").permitAll().

                anyRequest().authenticated().and().

                exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()

                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        httpSecurity.logout().logoutSuccessUrl("/authenticate").logoutUrl("/logout").permitAll();
    

    @Override
    public void configure(WebSecurity web) throws Exception 
        web.ignoring().mvcMatchers(String.valueOf(HttpMethod.OPTIONS), "/**");
        // ignore swagger
        web.ignoring().mvcMatchers("/swagger-ui.html/**", "/configuration/**", "/swagger-resources/**", "/v2/api-docs");
    

这是我的 jwt 请求过滤器:

@Component
public class JwtRequestFilter extends OncePerRequestFilter 
    @Autowired
    private JwtUserDetailsService jwtUserDetailsService;

    private JwtTokenUtil jwtTokenUtil;

    public JwtRequestFilter(JwtTokenUtil jwtTokenUtil) 
        this.jwtTokenUtil = jwtTokenUtil;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException 
        final String requestTokenHeader = request.getHeader("Authorization");
        String username = null;
        String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) 

            jwtToken = requestTokenHeader.substring(7);

            try 

                username = jwtTokenUtil.getUsernameFromToken(jwtToken);

             catch (IllegalArgumentException e) 

                System.out.println("Unable to get JWT Token");

             catch (ExpiredJwtException e) 

                System.out.println("JWT Token has expired");

            

        
        else if (requestTokenHeader == null)

            logger.info("Does not provide Authorization Header");

        
        else if (!requestTokenHeader.startsWith("Bearer "))
            logger.warn("JWT Token does not begin with Bearer");
        

// Once we get the token validate it.
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) 
            UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) 
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            
        
        chain.doFilter(request, response);
    

最后是 JwtAuthenticationEntryPoint:

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable 
    private static final long serialVersionUID = -7858869558953243875L;
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException 
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    

这是显示我没有在标头中发送任何令牌的日志,但它允许请求:

知道我应该怎么做吗? 有关更多信息,我应该说这段代码正在工作,但一段时间后停止工作,我没有找到任何原因,因为几个月来我没有对这些文件进行任何更改。

【问题讨论】:

您能提供假定受保护的端点吗? localhost:8080/api/customer/register 这是端点,但所有其他端点都相同。 【参考方案1】:

您缺少 addFilterAfter 并更新您的代码,如下所示。

  @Override
protected void configure(HttpSecurity httpSecurity) throws Exception 
    httpSecurity.csrf().disable()

            .authorizeRequests().antMatchers("/authenticate","/user","/swagger-ui.html","/swagger-ui/**"
            ,"/v3/api-docs/**").permitAll().

            anyRequest().authenticated().and().

            exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()

            .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

   .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class).logout().logoutSuccessUrl("/authenticate").logoutUrl("/logout").permitAll();

请参考https://github.com/techiesantosh/taskmanager-service/blob/develop/src/main/java/com/web/taskmanager/config/TaskConfig.java

【讨论】:

很抱歉听到这个消息。但是 TaskConfig.java 和整个项目中的代码都经过了很好的测试并且可以正常工作。你能试着再调整一次,希望它可以工作。谢谢【参考方案2】:

问题在于这一行的错误配置

web.ignoring().mvcMatchers(String.valueOf(HttpMethod.OPTIONS), "/**");

应该是

web.ignoring().mvcMatchers(HttpMethod.OPTIONS, "/**");

您现在可能已经注意到,您的配置实际上忽略了来自 Spring Security 过滤器的所有请求路径。这就是现在允许所有未经授权的请求(您期望的)的原因。

【讨论】:

谢谢,我删除了这一行,因为它不允许我删除 string.valueof。

以上是关于使用 jwt 在 springboot 中始终允许未经授权的请求的主要内容,如果未能解决你的问题,请参考以下文章

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

在 Spring Boot 中使用 jwt 令牌的具有 http 安全性的 CrossOrigin

springboot集成jjwt

SpringBoot2.0+Shiro+JWT 整合

个人技术总结

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