Spring/Boot - JWT 未在 GET 请求中获取

Posted

技术标签:

【中文标题】Spring/Boot - JWT 未在 GET 请求中获取【英文标题】:Spring/Boot - JWT not being picked up on GET request 【发布时间】:2020-12-09 16:24:19 【问题描述】:

我有一个 Spring/Boot REST API,它为会话提供 JWT 令牌。

我有一个带有 2 种方法的会话端点,一种用于验证用户身份,一种用于验证 Auth Token。我可以在端点上成功创建和验证令牌(这些令牌上没有过滤器)。一旦我尝试使用客户端应用程序访问端点,请求就会爆炸,但是我可以在 REST 客户端(如 insomnia)中正常使用它们。

通过 JWT 令牌运行的代码在这里:

@Component
public class JwtRequestFilter extends OncePerRequestFilter 


    private final UserService userService;
    private final JwtTokenUtility jwtTokenUtility;

    @Autowired
    public JwtRequestFilter(
            UserService userService,
            JwtTokenUtility jwtTokenUtility
    ) 
        this.userService = userService;
        this.jwtTokenUtility = jwtTokenUtility;
    

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 
        final String requestTokenHeader = request.getHeader("Authorization"); <-- null

        String username = null;
        String jwtToken = null;
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) 
            jwtToken = requestTokenHeader.substring(7);
            try 
                username = jwtTokenUtility.getUsernameFromToken(jwtToken);
             catch (IllegalArgumentException e) 
                System.out.println("Unable to get JWT Token");
             catch (ExpiredJwtException e) 
                System.out.println("JWT Token has expired");
            
         else 
            logger.warn("JWT Token does not begin with Bearer String"); <-- Therefore, this is called
        

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) 

            UserDetails userDetails = this.userService.loadUserByUsername(username);

            if (jwtTokenUtility.validateToken(jwtToken, userDetails)) 

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            
        
        filterChain.doFilter(request, response);
    


问题是代码读取授权请求头为空,但是当我检查请求头时,它显然在那里:

Provisional headers are shown
Accept: application/json, text/plain, */*
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJkdWRlY29yMyIsImV4cCI6MTU5NzkzNTQ0NywiaWF0IjoxNTk3OTE3NDQ3fQ.phMnbmkvOW6HKLCnWso-GaX844KMkukS5eADMBko56EEJA-VGgG1qpavwPwHT-tKjxbMuO9VZw3T0rESxQpIqQ
Referer: http://localhost:4200/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/84.0.4147.125 Safari/537.36

我的认证设置代码如下:


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final UserService userDetailsService;
    private final JwtRequestFilter jwtRequestFilter;

    @Autowired
    public WebSecurityConfig(
            JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
            UserService userDetailsService,
            JwtRequestFilter jwtRequestFilter
    ) 
        super();
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
        this.userDetailsService = userDetailsService;
        this.jwtRequestFilter = jwtRequestFilter;
    

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

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

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

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
        httpSecurity.csrf()
            .disable()
            .authorizeRequests()
            .antMatchers("/auth", "/auth/validate")
            .permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        httpSecurity.addFilterBefore(this.jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    


失败的客户端应用程序是 Angular 应用程序,从 auth 返回的 JWT 令牌存储为 cookie,然后拦截器检查 cookie,并通过拦截器将值(JWT 令牌)附加到请求标头。这种方法一直对我有用(如有必要,将提供代码)。下面的屏幕截图显示了完全相同的呼叫成功返回:

我感觉可能是在受保护的端点上不允许 OPTIONS 请求,但我没有证据表明是这种情况。是否有我可以使用的注释或实用程序允许我接受 OPTIONS 请求?这里有什么我做错了什么吗?我很茫然

【问题讨论】:

【参考方案1】:

已修复 - 我刚刚将我的 HTTP 配置更新为:


    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 
        httpSecurity.headers().frameOptions().disable().and().cors().and().csrf().disable();
        httpSecurity.addFilterBefore(this.jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    

我现在可以在服务器端接受 OPTIONS 请求。

【讨论】:

以上是关于Spring/Boot - JWT 未在 GET 请求中获取的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + Angular 2 + JWT

Spring boot中使用jwt

Spring boot中使用jwt

未在 Azure 中创建 Spring Boot 会话

CSS未在Spring Boot中加载

bootstrap.yml 未在 Spring Boot 2 中加载