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

Spring Boot+JWT鉴权异常:Authorization头为空致401/403错误

Spring Security集成JWT遇到的403/401问题

问题详情

开发React+Spring Boot前后端分离应用,后端第一部分开发完成,集成Spring Security JWT时出现以下异常:

  • 访问localhost:8080返回403错误,已在SecurityConfiguration中将/加入permitAll()
  • Postman发送POST请求到/api/auth/login(用户已存在数据库)返回401,request.getHeader("Authorization")返回null
  • 堆栈追踪显示错误出现在JWTFilter.doFilterInternal的chain.doFilter处:访问localhost:8080时header值为Basic (字母数字串),但登录请求时header为null
  • 已添加Cors配置,确认getHeader参数为Authorization,问题仍未解决

相关代码

SecurityConfiguration.java

package com.keyoflife.keyoflife.config;

import com.keyoflife.keyoflife.services.UserDetailsLoader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@Configuration
@EnableWebSecurity
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityConfiguration {
    private final UserDetailsLoader usersLoader;
    private final JwtFilter jwtFilter;
    public SecurityConfiguration(UserDetailsLoader usersLoader, JwtFilter jwtFilter) {
        this.usersLoader = usersLoader;
        this.jwtFilter = jwtFilter;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        //TODO: remove this
        //get the hashed version of 'asdfasdf' to save in db
        //System.out.println(new BCryptPasswordEncoder().encode("asdfasdf"));
        return new BCryptPasswordEncoder();
    }
    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http, PasswordEncoder passwordEncoder)
            throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.userDetailsService(usersLoader).passwordEncoder(passwordEncoder);
        return authenticationManagerBuilder.build();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .cors(Customizer.withDefaults())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/user-dash").authenticated()
                        .requestMatchers(HttpMethod.OPTIONS,"/api/auth/**", "/login", "/").permitAll()
                )
                .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .httpBasic(Customizer.withDefaults())
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin((login) -> login
                        .loginPage("/login")
                        .failureUrl("/login")
                        .defaultSuccessUrl("/user-dash"))
                /* Logout configuration */
                .logout((logout) -> logout
                        .logoutSuccessUrl("/"))
                .build();
    }
    
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("http://127.0.0.1:8080");
        configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
        configuration.setAllowedHeaders(Arrays.asList("Content-Type", "Authorization"));
        configuration.setAllowCredentials(true); // for cookies. bc everyone likes cookies

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}

JWTFilter关键代码

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {

        // Get authorization header and validate
        final String header = request.getHeader("Authorization");
        System.out.println(header);
        //ensure header has text and starts with "Bearer "
        if (!StringUtils.hasText(header) || (StringUtils.hasText(header) && !header.startsWith("Bearer "))) {
            chain.doFilter(request, response); //Access Denied Exception thrown here because header is null
            return;
        }

//rest of method below

}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:13:15