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

Spring Boot+MySQL集成JWT配置hasAuthority出现401未授权问题

问题分析与解决步骤

1. 修复Spring Security权限匹配顺序问题

你的权限配置中第一个规则是.antMatchers("/**", ...).hasAuthority(UserRole.ADMIN.name()),"/**"会匹配所有请求,导致后续针对SUPPORTEXECUTIVE、FIELDEXECUTIVE的规则永远不会被执行。Spring Security的规则是从上到下匹配,匹配到第一个符合的规则就停止,必须把更具体的路径放在前面,最宽泛的路径放在最后。

修改后的配置示例:

protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .cors().disable()
        .authorizeRequests()
        // 先配置各角色的具体路径规则
        .antMatchers("/appointment/", "/document/").hasAuthority(UserRole.SUPPORTEXECUTIVE.name())
        .antMatchers("/appointment/", "/document/").hasAuthority(UserRole.FIELDEXECUTIVE.name())
        // 配置ADMIN的规则,避免用/**覆盖所有请求
        .antMatchers("/user/**", "/document/**", "/appointment/**", "/activity/**").hasAuthority(UserRole.ADMIN.name())
        // 最后添加默认规则,所有未匹配的请求需认证
        .anyRequest().authenticated()
        .and()
        .exceptionHandling().authenticationEntryPoint(invaildUserAuthEntryPoint)
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class);
}

2. 确保UserDetailsService正确加载用户权限

过滤器中通过userDetailsService.loadUserByUsername(username)获取UserDetails对象,必须保证该对象的getAuthorities()方法正确返回用户对应的权限集合。示例实现:

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userRepository.findByUserEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("用户不存在:" + email));
        
        // 将用户角色转换为GrantedAuthority集合
        Collection<? extends GrantedAuthority> authorities = Collections.singletonList(
                new SimpleGrantedAuthority(user.getRole().name())
        );
        
        return new org.springframework.security.core.userdetails.User(
                user.getUserEmail(),
                user.getPassword(),
                authorities
        );
    }
}

3. 修复Token生成与验证逻辑

当前genrateToken仅传入username,建议将用户权限信息存入Token,既提升验证效率,也避免依赖数据库查询权限。同时要处理Token的前缀格式:

生成Token时加入权限

public String genrateToken(String username) {
    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
    Map<String, Object> claims = new HashMap<>();
    // 将权限写入Token的claims字段
    claims.put("authorities", userDetails.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.toList()));
    
    return Jwts.builder()
            .setClaims(claims)
            .setSubject(username)
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时有效期
            .signWith(SignatureAlgorithm.HS512, "your-secret-key") // 替换为你的密钥
            .compact();
}

优化过滤器的Token处理

处理Authorization头的Bearer前缀,并直接从Token解析权限:

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    String token = request.getHeader("Authorization");
    // 处理Bearer前缀
    if (token != null && token.startsWith("Bearer ")) {
        token = token.substring(7);
        String username = util.getUsername(token);
        
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            if (util.validateToken(token, username)) {
                // 从Token解析权限
                List<String> authorityStrings = util.getAuthoritiesFromToken(token);
                Collection<GrantedAuthority> authorities = authorityStrings.stream()
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());
                
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        username, null, authorities);
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
    }
    filterChain.doFilter(request, response);
}

4. 检查请求Token格式

确保前端请求时的Authorization头格式为Bearer <token>,如果缺少前缀,过滤器会无法正确解析Token,导致认证失败触发401。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 21:50:23