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

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();
}

方式二:使用方法级安全注解(更灵活)

  1. 在SecurityConfig上添加@EnableMethodSecurity注解,开启方法级权限校验:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 开启方法级安全
@RequiredArgsConstructor
public class SecurityConfig {
    // 现有代码不变
}
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 16:25:10