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
相关产品推荐
相关产品推荐

