You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring项目JWT未授权报错及WebSecurityConfig废弃问题排查

Spring Boot集成JWT认证报错修复方案

运行报错日志:
ERROR security.jwt.AuthEntryPointJwt - Unauthorized error: Full authentication is required to access this resource
ERROR security.jwt.AuthTokenFilter - Cannot set user authentication {}

报错核心原因分三类:

  1. Spring Security 5.7+版本已完全废弃WebSecurityConfigurerAdapter适配器,旧版继承该类的配置方式会导致AuthenticationManager无法正常注入、过滤器链加载失效
  2. 自定义JWT过滤器缺少Spring托管注解,内部依赖注入为null,执行时抛出空指针导致认证上下文无法设置
  3. 业务代码存在语法不闭合、角色赋值逻辑缺失问题,导致用户权限数据异常触发认证拦截

1. 重构安全配置类,移除废弃API

删除原有继承WebSecurityConfigurerAdapter的写法,改用Spring Security推荐的组件化Bean配置方式,同时替换已经废弃的@EnableGlobalMethodSecurity注解:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private AuthEntryPointJwt unauthorizedHandler;

    @Bean
    public AuthTokenFilter authenticationJwtTokenFilter() {
        return new AuthTokenFilter();
    }

    // 通过AuthenticationConfiguration直接获取AuthenticationManager实例,无需重写废弃方法
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

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

    // 定义Security过滤器链,替换旧的configure(HttpSecurity)重写逻辑
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors(Customizer.withDefaults())
                .csrf(csrf -> csrf.disable())
                .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/auth/**").permitAll()
                        .requestMatchers("/api/food/**").permitAll()
                        .requestMatchers("/api/cart/**").permitAll()
                        .requestMatchers("/api/order/**").permitAll()
                        .requestMatchers("/api/payment/**").permitAll()
                        .requestMatchers("/h2-console/**").permitAll()
                        .requestMatchers("/api/users/**").permitAll()
                        .anyRequest().authenticated()
                );
        // 兼容H2数据库控制台的frame嵌入配置
        http.headers(headers -> headers.frameOptions(frame -> frame.disable()));
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

2. 修复JWT认证过滤器逻辑

原有过滤器存在两个致命问题:一是类上没有加@Component注解,手动new出来的实例不归Spring管理,内部@Autowired标记的JwtUtils、UserDetailsService全为null,执行时直接抛空指针;二是认证详情构造方式在高版本已废弃,容易引发上下文异常。修复后完整代码如下:

@Component // 必须添加该注解,否则内部依赖无法注入
public class AuthTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private UserDetailsServiceImpl userDetailsServiceImpl;

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthTokenFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String jwt = parseJwt(request);
            if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
                String username = jwtUtils.getUsernameFromJwtToken(jwt);
                UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        } catch (Exception e) {
            LOGGER.error("Cannot set user authentication: {}", e.getMessage(), e);
        }
        filterChain.doFilter(request, response);
    }

    private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");
        // 增加长度校验,避免字符串截取越界
        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ") && headerAuth.length() > 7) {
            return headerAuth.substring(7);
        }
        return null;
    }
}

3. 修复注册接口的逻辑错误

原有注册接口存在代码块未闭合、角色赋值逻辑缺失、硬编码管理员权限的问题,会导致用户保存时角色集合为空,登录后无权限访问接口。修复后注册方法核心逻辑如下:

@PostMapping("/register")
public ResponseEntity<?> registerUser(@Valid @RequestBody SignupRequest signupRequest) {
    if (userRepo.existsByEmail(signupRequest.getEmail())) {
        return ResponseEntity
                .badRequest()
                .body(new MessageResponse("Error: Email is already in use!"));
    }

    User user = new User(signupRequest.getEmail(),
            signupRequest.getName(),
            passwordEncoder.encode(signupRequest.getPassword()),
            signupRequest.getAddress());
    
    Set<Role> roles = new HashSet<>();
    String strRoles = signupRequest.getRole();
    // 未指定角色默认分配普通用户权限,禁止默认给管理员权限
    if (strRoles == null) {
        Role userRole = roleRepo.findByRoleName("ROLE_USER")
                .orElseThrow(() -> new RuntimeException("Error: role not found"));
        roles.add(userRole);
    } else {
        switch (strRoles) {
            case "admin":
                Role adminRole = roleRepo.findByRoleName("ROLE_ADMIN")
                        .orElseThrow(() -> new RuntimeException("Error: role not found"));
                roles.add(adminRole);
                break;
            default:
                Role userRole = roleRepo.findByRoleName("ROLE_USER")
                        .orElseThrow(() -> new RuntimeException("Error: role not found"));
                roles.add(userRole);
                break;
        }
    }

    user.setRoles(roles);
    userRepo.save(user);
    emailService.sendTextEmail(user.getEmail());
    return ResponseEntity
            .status(201)
            .body(new MessageResponse("User created successfully"));
}

修复验证

重启项目后按顺序验证:

  • 调用注册接口创建测试用户,确认数据库中用户、角色关联数据正常写入
  • 调用登录接口,确认可以正常返回JWT令牌
  • 携带格式为Authorization: Bearer 你的JWT令牌的请求头访问受保护接口,确认不再返回401未授权错误

内容的提问来源于stack exchange,提问作者Nitin Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 21:03:19