Spring Boot中跳过认证仅实现JWT角色授权方案咨询
实现Spring Boot REST接口的纯授权逻辑(跳过认证)
核心思路
因为认证由外部系统完成,我们只需要:
- 从JWT中提取角色信息,转换为Spring Security能识别的权限格式
- 将权限信息注入到SecurityContext中
- 通过
@PreAuthorize注解实现接口的角色访问控制 - 关闭Spring Security的默认认证流程,仅保留授权校验
步骤1:开启方法级安全注解
创建Spring Security配置类,开启@PreAuthorize等方法注解支持:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) // 开启方法级权限注解 public class SecurityConfig { private final JwtRoleExtractionFilter jwtRoleExtractionFilter; // 构造注入自定义过滤器 public SecurityConfig(JwtRoleExtractionFilter jwtRoleExtractionFilter) { this.jwtRoleExtractionFilter = jwtRoleExtractionFilter; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) // REST接口关闭CSRF防护 .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 无状态会话 .authorizeHttpRequests(auth -> auth .anyRequest().permitAll() // 允许所有请求进入,授权交给方法注解处理 ) .addFilterBefore(jwtRoleExtractionFilter, UsernamePasswordAuthenticationFilter.class); // 加入角色提取过滤器 return http.build(); } }
步骤2:自定义JWT角色提取过滤器
实现过滤器,从JWT中解析角色并注入SecurityContext:
import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; public class JwtRoleExtractionFilter extends OncePerRequestFilter { private static final String USER_PROFILE = "user_profile"; // 从请求头解析JWT并获取claims(替换为你实际的JWT解析逻辑) private Claims extractClaimsFromRequest(HttpServletRequest request) { String token = request.getHeader("Authorization").replace("Bearer ", ""); return Jwts.parser().setSigningKey("your-jwt-secret").parseClaimsJws(token).getBody(); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { Claims claims = extractClaimsFromRequest(request); Optional<Profile> profileOpt = Optional.ofNullable(claims.get(USER_PROFILE, Profile.class)); if (profileOpt.isPresent()) { Profile profile = profileOpt.get(); // 转换角色为Spring Security格式:必须加ROLE_前缀,因为hasRole会自动匹配前缀 Collection<GrantedAuthority> authorities = profile.getRoles().stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .collect(Collectors.toList()); // 创建Authentication对象(用AnonymousAuthenticationToken,因为不需要认证信息) Authentication authentication = new AnonymousAuthenticationToken( "security-key", "external-authenticated-user", authorities ); // 将权限注入SecurityContext SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { // JWT解析失败时清空上下文,后续接口会返回403 SecurityContextHolder.clearContext(); } filterChain.doFilter(request, response); } }
步骤3:Profile类示例
public class Profile { private List<String> roles; // getter/setter public List<String> getRoles() { return roles; } public void setRoles(List<String> roles) { this.roles = roles; } }
步骤4:控制器中使用@PreAuthorize
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.security.access.prepost.PreAuthorize; @RestController public class ApiController { @GetMapping("/user-resource") @PreAuthorize("hasRole('USER')") public String userResource() { return "仅USER角色可访问"; } @GetMapping("/common-resource") @PreAuthorize("hasAnyRole('USER', 'ADMIN')") public String commonResource() { return "USER或ADMIN角色可访问"; } @GetMapping("/admin-resource") @PreAuthorize("hasRole('ADMIN')") public String adminResource() { return "仅ADMIN角色可访问"; } }
关键注意事项
- 角色前缀:
hasRole/hasAnyRole会自动给角色名添加ROLE_前缀匹配,所以转换权限时必须手动加上该前缀 - 过滤器顺序:必须将自定义过滤器放在
UsernamePasswordAuthenticationFilter之前,确保权限先被注入 - 异常处理:JWT解析失败时清空SecurityContext,此时访问受保护接口会返回403未授权
内容的提问来源于stack exchange,提问作者Akhil
相关产品推荐
相关产品推荐

