Spring Boot 3.0+Spring Security JWT请求Access Denied问题排查
JWT认证通过但接口返回Access Denied排查方案
核心问题
注册、登录获取JWT Token流程正常,但携带Token请求带有@PreAuthorize("hasRole('ROLE_USER')")注解的接口时,返回Access Denied错误。
排查步骤及解决方法
1. 检查JWT Token角色格式与权限注入逻辑
Spring Security的hasRole()方法默认会自动为角色名称添加ROLE_前缀,需确保Token中的角色与注解规则匹配:
- 查看
JwtUtils生成Token时,存入的角色名称是USER还是ROLE_USER:- 若Token中存的是
USER,则注解应改为@PreAuthorize("hasRole('USER')"),或在注入权限时手动添加ROLE_前缀; - 若注解用
hasRole('ROLE_USER'),则生成Token和解析时需确保角色名称包含ROLE_前缀。
- 若Token中存的是
- 检查
AuthTokenFilter中解析Token后构建Authentication对象的权限赋值:// 正确示例:确保权限带有ROLE_前缀(适配hasRole('ROLE_USER')注解) List<GrantedAuthority> authorities = userRoles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, authorities); SecurityContextHolder.getContext().setAuthentication(authentication);
2. 验证Security配置类关键项
- 确认已开启方法级安全:在
WebSecurityConfig类上添加@EnableGlobalMethodSecurity(prePostEnabled = true),否则@PreAuthorize注解不会生效。 - 确认JWT过滤器位置正确:需将
AuthTokenFilter添加到UsernamePasswordAuthenticationFilter之前,确保请求先经过JWT解析:@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/auth/**").permitAll() // 放行登录注册接口 .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 注入JWT过滤器,位置必须在UsernamePasswordAuthenticationFilter之前 http.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class); }
3. 检查Token携带方式
- 确认Postman请求头格式正确:使用
Authorization: Bearer <你的Token>,注意Bearer后必须有一个空格,且Token无多余空格或换行。 - 若项目自定义了Token请求头(如
X-Auth-Token),需确保Postman使用的头名称与AuthTokenFilter中解析的头一致。
4. 验证Token有效性与解析逻辑
- 用jwt.io解析Token,检查:
exp字段是否未过期;- 自定义角色字段(如
roles)是否包含预期的角色值; - 签名是否有效(确保
JwtUtils生成和解析用的是同一密钥)。
- 调试
AuthTokenFilter,确认Token解析成功,且SecurityContextHolder中正确存入了带有对应权限的Authentication对象。
5. 调整权限匹配规则
- 若使用
hasRole(),避免重复添加ROLE_前缀(比如hasRole('ROLE_USER')是正确的,不要写成hasRole('ROLE_ROLE_USER')); - 可尝试用
hasAuthority('ROLE_USER')替代hasRole(),hasAuthority()会严格匹配权限字符串,无需自动添加前缀。
内容的提问来源于stack exchange,提问作者Smetana Po Aktsii
相关产品推荐
相关产品推荐

