Spring Boot Security角色权限配置:为接口绑定用户角色
Spring Boot JWT 角色权限配置方案
我跟着视频教程实现了Spring Boot应用的注册登录功能,已经完成JWT认证,持有有效Token可以正常访问接口。目前User实体包含USER、ADMIN两种Role枚举角色,现在想配置应用校验当前用户角色,给DemoController中的sayHelloAdmin(仅ADMIN可访问)、sayHelloUser(USER和ADMIN均可访问)等接口绑定对应角色权限,以下是我的代码实现:
User 实体类
@Builder @Getter @Setter @ToString @AllArgsConstructor @NoArgsConstructor @Entity(name = "users") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true, columnDefinition = "varchar(30)") @Email private String email; @Column(nullable = false) private String firstName; @Column(nullable = false) private String lastName; @Column(nullable = false, columnDefinition = "varchar(60)") @Length(min = 8, max = 256) @NotBlank private String password; @Column @Enumerated(EnumType.STRING) private Role role; @Column(nullable = false) private String teamName; @Column(nullable = false) private int overallScore; @Column(nullable = false) private int gameDayScore; @ManyToMany @JoinTable( name = "users_players", joinColumns = {@JoinColumn(name = "user_id", nullable = false)}, inverseJoinColumns = {@JoinColumn(name = "player_id", nullable = false)} ) @JsonIgnoreProperties({"users"}) private List<Player> players; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(new SimpleGrantedAuthority(role.name())); } @Override public String getUsername() { return email; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
Role 枚举类
public enum Role{ USER, ADMIN; }
JwtService 工具类
@Service public class JwtService { private static final String SECRET_KEY = "b7acf8204b85ab7cec6e73b91f682677d642d328fdec7788e8cbff11ff1a5b22"; public String extractUsername(String jwtToken) { return extractClaim(jwtToken, Claims::getSubject); } public <T> T extractClaim(String jwtToken, Function<Claims, T> claimsResolver) { final Claims claims = extractAllclaims(jwtToken); return claimsResolver.apply(claims); } public String generateJwtToken(Map<String, Object> extraClaims, UserDetails userDetails) { return Jwts.builder() .setClaims(extraClaims) .setSubject(userDetails.getUsername()) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24)) .signWith(getSigningKey(), SignatureAlgorithm.HS256) .compact(); } public String generateJwtToken(UserDetails userDetails) { return generateJwtToken(new HashMap<>(), userDetails); } public boolean isJwtTokenValid(String jwtToken, UserDetails userDetails) { final String username = extractUsername(jwtToken); return (username.equals(userDetails.getUsername())) && !isJwtTokenExpired(jwtToken); } private boolean isJwtTokenExpired(String jwtToken) { return extractExpiration(jwtToken).before(new Date()); } private Date extractExpiration(String jwtToken) { return extractClaim(jwtToken, Claims::getExpiration); } private Claims extractAllclaims(String jwtToken) { return Jwts .parserBuilder() .setSigningKey(getSigningKey()) .build() .parseClaimsJws(jwtToken) .getBody(); } private Key getSigningKey() { byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); return Keys.hmacShaKeyFor(keyBytes); } }
JwtAuthFilter 过滤器
@Component @RequiredArgsConstructor public class JwtAuthFilter extends OncePerRequestFilter { private final JwtService jwtService; private final UserDetailsService userDetailsService; @Override protected void doFilterInternal( @NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain ) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); final String jwtToken; final String userEmail; if (authHeader == null || !authHeader.startsWith("Bearer ")) { filterChain.doFilter(request, response); return; } jwtToken = authHeader.substring(7); userEmail = jwtService.extractUsername(jwtToken); if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); if (jwtService.isJwtTokenValid(jwtToken, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails( new WebAuthenticationDetailsSource().buildDetails(request) ); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } }
SecurityConfig 安全配置类
@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final JwtAuthFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/v1/auth/**").permitAll() .anyRequest().authenticated() ) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return httpSecurity.build(); } }
ApplicationConfig 应用配置类
@Configuration @RequiredArgsConstructor public class ApplicationConfig { private final UserRepository userRepository; @Bean public UserDetailsService userDetailsService() { return username -> userRepository.findByEmail(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService()); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
AuthenticationController 认证控制器
@RestController @RequestMapping("/api/v1/auth") @RequiredArgsConstructor public class AuthenticationController { private final AuthenticationService authenticationService; @PostMapping("/register") public ResponseEntity<AuthenticationResponse> registerUser(@RequestBody RegisterRequestDto request) { return authenticationService.registerUser(request); } @PostMapping("/authenticate") public ResponseEntity<AuthenticationResponse> authenticateUser(@RequestBody AuthRequestDto request) { return authenticationService.authenticateUser(request); } }
DemoController 测试控制器(需配置角色权限)
@RestController @RequestMapping("/api/v1/demo-controller") public class DemoController { @GetMapping(value = "/admin") public ResponseEntity<String> sayHelloAdmin() { return ResponseEntity.ok("Hello from admin endpoint"); } @GetMapping(value = "/user") public ResponseEntity<String> sayHelloUser() { return ResponseEntity.ok("Hello from user endpoint"); } }
AuthenticationService 认证服务类
@Service @AllArgsConstructor public class AuthenticationServiceImpl implements AuthenticationService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final JwtService jwtService; private final AuthenticationManager authManager; private final UserMapper userMapper; @Override public ResponseEntity<AuthenticationResponse> registerUser(RegisterRequestDto request) { request.setPassword(passwordEncoder.encode(request.getPassword())); User user = userMapper.registerRequestDtoToUser(request); user.setRole(Role.USER); user.setGameDayScore(0); user.setOverallScore(0); user.setPlayers(new ArrayList<>()); userRepository.save(user); var jwtToken = jwtService.generateJwtToken(user); return new ResponseEntity<>(AuthenticationResponse.builder() .jwtToken(jwtToken) .build(), HttpStatus.CREATED); } @Override public ResponseEntity<AuthenticationResponse> authenticateUser(AuthRequestDto request) { authManager.authenticate( new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()) ); var user = userRepository.findByEmail(request.getEmail()) .orElseThrow(); var jwtToken = jwtService.generateJwtToken((user)); return new ResponseEntity<>(AuthenticationResponse.builder() .jwtToken(jwtToken) .build(), HttpStatus.OK); } }
AuthRequestDto 和 RegisterRequestDto 与User实体结构类似,仅包含部分属性。
角色权限配置方案
第一步:统一权限前缀(关键)
Spring Security 默认识别带 ROLE_ 前缀的角色权限,因此需要修改User类的getAuthorities方法,为角色添加前缀:
@Override public Collection<? extends GrantedAuthority> getAuthorities() { // 添加ROLE_前缀,适配Spring Security的角色校验规则 return List.of(new SimpleGrantedAuthority("ROLE_" + role.name())); }
方式一:通过SecurityFilterChain配置路径权限
修改SecurityConfig中的securityFilterChain方法,为指定路径绑定角色权限:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/v1/auth/**").permitAll() // 仅ADMIN角色可访问/admin路径 .requestMatchers("/api/v1/demo-controller/admin").hasRole("ADMIN") // USER或ADMIN角色均可访问/user路径 .requestMatchers("/api/v1/demo-controller/user").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated() ) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return httpSecurity.build(); }
方式二:使用方法级安全注解(更灵活)
- 在SecurityConfig上添加
@EnableMethodSecurity注解,开启方法级权限校验:
@Configuration @EnableWebSecurity @EnableMethodSecurity // 开启方法级安全 @RequiredArgsConstructor public class SecurityConfig { // 现有代码不变 }
- 在DemoController的方法上添加
@PreAuthorize注解,直接绑定角色权限:
@RestController @RequestMapping("/api/v1/demo-controller") public class DemoController { // 仅ADMIN角色可访问 @GetMapping("/admin") @PreAuthorize("hasRole('ADMIN')") public ResponseEntity<String> sayHelloAdmin() { return ResponseEntity.ok("Hello from admin endpoint"); } // USER或ADMIN角色均可访问 @GetMapping("/user") @PreAuthorize("hasAnyRole('USER', 'ADMIN')") public ResponseEntity<String> sayHelloUser() { return ResponseEntity.ok("Hello from user endpoint"); } }
补充:创建ADMIN角色用户
当前注册接口默认创建USER角色用户,可通过以下方式创建ADMIN用户:
- 直接在数据库手动插入一条
role字段为ADMIN的用户记录(密码需用BCrypt加密) - 添加后台专属接口创建ADMIN用户(需做好权限控制,避免公开访问)
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

