尝试访问资源时出现 Spring security jwt token 403 错误

Posted

技术标签:

【中文标题】尝试访问资源时出现 Spring security jwt token 403 错误【英文标题】:Spring security jwt token 403 error when trying to access a resource 【发布时间】:2021-10-12 13:46:02 【问题描述】:

JwtRequestFilter:

package com.rest.app.filters;


import com.rest.app.services.MyUserDetailsService;
import com.rest.app.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
public class JwtRequestFilter extends OncePerRequestFilter 

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

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

        System.out.println(authorizationHeader);

        String userName = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) 
            jwt = authorizationHeader.substring(7);
            userName = jwtUtil.extractUserName(jwt);
        


        if (userName != null && SecurityContextHolder.getContext().getAuthentication() != null) 
            UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
            if (jwtUtil.validateToken(jwt, userDetails)) 
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            
        
        filterChain.doFilter(request, response);
    

应用:

package com.rest.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class RestApplication 

    public static void main(String[] args) 
        SpringApplication.run(RestApplication.class, args);
    


    @Bean
    public WebMvcConfigurer corsConfigurer() 
        return new WebMvcConfigurer() 
            @Override
            public void addCorsMappings(CorsRegistry registry) 
                registry.addMapping("/**")
                        .allowedOrigins("*")
                        .allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS")
                        .allowedHeaders("*");
            
        ;
    

安全配置器:

package com.rest.app.configuration;


import com.rest.app.filters.JwtRequestFilter;
import com.rest.app.services.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter 

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(myUserDetailsService);
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.cors().and().csrf().disable()
                .authorizeRequests().antMatchers("/authenticate", "/user/create", "/user/login").permitAll()
                .anyRequest().authenticated()
                .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

    

    @Bean
    public PasswordEncoder passwordEncoder() 
        return NoOpPasswordEncoder.getInstance();
    


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







我仍然得到这个:

"timestamp":"2021-08-08T15:23:52.656+00:00","status":403,"error":"Forbidden","path":"/api/user/account/deposit"

响应标头:

Vary: origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 08 Aug 2021 15:37:25 GMT
Keep-Alive: timeout=60
Connection: keep-alive

请求标头:

Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Type: application/json
Date: Sun, 08 Aug 2021 15:23:52 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
Transfer-Encoding: chunked
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJtYXJlazEyMyIsImV4cCI6MTYyODQ3MjE5MiwiaWF0IjoxNjI4NDM2MTkyfQ.rxUQ3uzCTtriMUkrKCayBDCQ4Q4aXTHD_z-R7A8Oduc
Connection: keep-alive
Content-Length: 2
Content-Type: application/json;charset=UTF-8
Host: localhost:8080
Origin: http://localhost:3000
Referer: http://localhost:3000/
sec-ch-ua: "Opera";v="77", "Chromium";v="91", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/91.0.4472.164 Safari/537.36 OPR/77.0.4054.277

即使我在授权请求标头中发送 jwt 令牌,我也无法获取此资源。 OPTIONS 请求通过,状态码 200 没有问题。是关于我的 jwt 授权还是我的 CORS 配置?我正在使用弹簧安全。我已经尝试了所有我能找到的解决方案。

【问题讨论】:

你的调试日志在说什么? 另外编写自定义 jwt 过滤器是不好的做法,spring security 已经完全支持 jwt 3 年了,请阅读 spring security 参考文档中的章节。 【参考方案1】:

我在我的 JwtRequestFilter 中找到了原因,而不是这个:

SecurityContextHolder.getContext().getAuthentication() != null

应该是这样的:

SecurityContextHolder.getContext().getAuthentication() == null

【讨论】:

以上是关于尝试访问资源时出现 Spring security jwt token 403 错误的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 4. 指定访问决策管理器时出现异常

Spring Security 4. 指定访问决策管理器时出现异常

Spring Security 4. 指定访问决策管理器时出现异常

尝试在 Thymeleaf 和 Spring Security 项目中插入图像时出现 404 错误

在 Spring Security 中添加新用户时出现异常

使用 CorsFilter 和 spring security 时出现 Cors 错误