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

Spring Boot应用出现401 Unauthorized错误,求解决方案

解决Spring Boot Whitelabel Error Page 401未授权问题

你遇到的401未授权错误,通常是Spring Security的权限配置、JWT验证逻辑或者资源放行规则出了问题。结合你提供的代码,我帮你梳理几个关键排查点和解决方案:

一、核心问题排查方向

1. JWT过滤器是否正确集成?

你在WebSecurityConfig中使用了JwtConfigurer但未提供实现代码——如果这个配置类没有把JWT验证过滤器加入Spring Security的过滤器链,所有非放行请求都会被直接拦截返回401。

2. 静态资源放行规则是否覆盖全面?

你的Maven插件把前端dist目录复制到了target/classes/static/,意味着前端静态文件是根路径访问(比如/css/main.css),但你配置的ACCEPTED_FILES只放行根目录下的单个文件,前端子目录资源会被拦截。

3. 角色权限匹配是否错位?

hasRole("ADMIN")会自动给角色名添加ROLE_前缀,实际匹配的是ROLE_ADMIN权限。如果你的appUser中存储的权限是ADMIN(不带前缀),这个配置就会失效,导致管理员请求被拦截。

4. 前端请求是否正确携带Token?

登录接口/auth/sign-in放行后,前端需要将返回的token放在请求头(通常是Authorization: Bearer <token>)中,否则后续请求会被判定为未授权。

二、具体解决方案

1. 补全JWT配置类

先实现JwtConfigurer,将JWT过滤器注入Spring Security链:

package app.auth;

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    private JwtTokenProvider jwtTokenProvider;

    public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
        http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

再实现JwtTokenFilter处理Token验证:

package app.auth;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
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;

public class JwtTokenFilter extends OncePerRequestFilter {

    private JwtTokenProvider jwtTokenProvider;

    public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = jwtTokenProvider.resolveToken(request);
        if (token != null && jwtTokenProvider.validateToken(token)) {
            Authentication auth = jwtTokenProvider.getAuthentication(token);
            if (auth != null) {
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
        filterChain.doFilter(request, response);
    }
}

确保JwtTokenProvider能正确提取请求头中的Token:

public String resolveToken(HttpServletRequest request) {
    String bearerToken = request.getHeader("Authorization");
    if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
        return bearerToken.substring(7);
    }
    return null;
}

2. 调整静态资源放行规则

修改WebSecurityConfig,确保所有前端静态资源都被放行,同时优化权限配置的可读性:

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring()
        .antMatchers("/assets/**", "/home/**", "/css/**", "/js/**", "/images/**")
        .antMatchers(ACCEPTED_FILES);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and()
        .csrf().disable()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
            .antMatchers("/auth/sign-in").permitAll()
            .antMatchers(ACCEPTED_FILES).permitAll()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            // 如果数据库中角色是不带ROLE_前缀的ADMIN,改用hasAuthority
            .antMatchers("/auth/admin/**", "/object/admin/**").hasAuthority("ADMIN")
            .anyRequest().authenticated()
        .and()
        .exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint())
        .and()
        .apply(new JwtConfigurer(jwtTokenProvider))
        .and()
        .httpBasic().disable();
}

3. 验证UserDetailsService的权限返回

确保你的UserDetailsService实现中,返回的用户权限集合与配置匹配:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    appUser user = userRepository.findByUsername(username);
    if (user == null) {
        throw new UsernameNotFoundException("User not found");
    }
    Collection<GrantedAuthority> authorities = new ArrayList<>();
    if (user.hasRule(ParamsEnum.ROLE_ADMIN.getValue())) {
        // 对应hasAuthority("ADMIN")
        authorities.add(new SimpleGrantedAuthority("ADMIN"));
        // 如果用hasRole("ADMIN"),需要改成new SimpleGrantedAuthority("ROLE_ADMIN")
    } else {
        authorities.add(new SimpleGrantedAuthority("USER"));
    }
    return new org.springframework.security.core.userdetails.User(
        user.getUsername(),
        user.getPassword(),
        authorities
    );
}

4. 前端请求携带Token

确保前端登录成功后,将Token放入请求头:

// 登录成功后保存Token
localStorage.setItem('token', response.data.token);

// 全局请求拦截器携带Token
axios.interceptors.request.use(config => {
    const token = localStorage.getItem('token');
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
});

三、额外检查点

  • 确认JwtTokenProvider的createToken方法正确将用户权限写入Token,getAuthentication方法能正确解析出权限。
  • 检查数据库中用户的权限字段值,确保和hasAuthority/hasRole的配置对应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 21:07:40