基于Spring Security实现RBAC权限控制的完整步骤

 更新时间:2026年04月22日 09:02:03   作者:无籽西瓜a  
文章主要讲述了基于角色的访问控制(RBAC)的基本概念、SpringSecurity的核心功能,以及实现RBAC的详细步骤,包括定义用户模型和角色、密码加密、JWT工具类、让SpringSecurity认识用户的用户模型、JWT认证过滤器以及安全配置规则等,需要的朋友可以参考下

什么是 RBAC

RBAC,全称 Role-Based Access Control,基于角色的访问控制。核心思路很简单:不直接给用户分配权限,而是引入"角色"作为中间层。

用户角色权限

举个例子:一家公司里,"实习生"能查看文档,"正式员工"能查看和编辑文档,"部门经理"能查看、编辑、删除文档。你入职的时候,HR 不会一条条给你勾权限,而是直接给你一个角色,权限就跟着来了。

这里面有几个关键关系:

  • 一个用户可以拥有多个角色
  • 一个角色可以分配给多个用户
  • 一个角色可以包含多个权限
  • 一个权限可以属于多个角色

还有一种模型叫角色继承——上层角色自动继承下层角色的所有权限。比如"经理"自动拥有"员工"的全部权限,再额外拥有管理权限。这在组织架构复杂的系统中非常实用。

Spring Security 是什么

Spring Security 本质上是一个基于 Filter 的安全框架。请求进入应用时,会经过一系列过滤器链,每个过滤器各司其职:有的负责认证,有的负责授权,有的负责 CSRF 防护。

它解决两个核心问题:

  • 认证(Authentication):你是谁?验证用户名密码是否正确。
  • 授权(Authorization):你能干什么?检查你有没有权限访问某个接口。

用 RBAC 的思路来说,认证就是确认你的身份,授权就是根据你的角色判断你能不能进这扇门。

整体架构设计

要基于 Spring Security 实现 RBAC,需要这几个核心模块:

config/          → 安全配置、JWT 过滤器
controller/      → 接收请求的入口
service/         → 业务逻辑、用户详情加载
repository/      → 数据库访问
model/           → 用户实体、角色定义
utils/           → 密码加密、JWT 工具

下面按照请求的生命周期来讲,从注册到登录到鉴权,一步步走通。

第一步:定义用户模型和角色

@Data
@Entity
@Table(name = "wzxg_users", uniqueConstraints = @UniqueConstraint(columnNames = "username"))
public class WzxgUser {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private WzxgRole role;

    @CreationTimestamp
    private LocalDateTime createdTime;

    @UpdateTimestamp
    private LocalDateTime updatedTime;

    public enum WzxgRole {
        USER, ADMIN
    }
}

@Enumerated(EnumType.STRING) 让角色以字符串形式存到数据库里,比如存的是 "ADMIN" 而不是 0,可读性好,也不容易出错。

数据访问层很简单,一个根据用户名查询的方法就够了:

public interface WzxgUserRepository extends JpaRepository<WzxgUser, Long> {
    Optional<WzxgUser> findByUsername(String username);
}

第二步:密码加密

密码绝对不能明文存储。BCrypt 是目前主流的选择,它内置盐值机制,同一个密码每次加密结果都不同,能有效防御彩虹表攻击。

public class WzxgPasswordUtil {
    private static final BCryptPasswordEncoder wzxgEncoder = new BCryptPasswordEncoder();

    public static String encode(String rawPassword) {
        return wzxgEncoder.encode(rawPassword);
    }

    public static boolean matches(String rawPassword, String encodedPassword) {
        return wzxgEncoder.matches(rawPassword, encodedPassword);
    }
}

注册时调 encode(),登录时调 matches()

第三步:JWT 工具类

系统采用无状态认证,服务端不存 session,而是通过 JWT 令牌来传递身份信息。

@Component
public class WzxgJwtUtils {
    private static final String WZXG_SECRET_KEY = "wzxg_jwt_secret_key_2024";
    private static final long WZXG_EXPIRATION = 86400000; // 24小时

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + WZXG_EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, WZXG_SECRET_KEY)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(WZXG_SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public String extractUsername(String token) {
        Claims wzxgClaims = Jwts.parser()
                .setSigningKey(WZXG_SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
        return wzxgClaims.getSubject();
    }
}

JWT 是一个自包含的令牌,里面可以塞用户名、角色、组织信息等。这样每次请求过来,解析 token 就能知道用户是谁、有什么权限,不用查数据库。

第四步:让 Spring Security 认识你的用户

Spring Security 有自己的一套用户模型 UserDetails,我们需要实现 UserDetailsService 接口,把数据库里的用户转换成它能理解的格式。

@Service
public class WzxgUserDetailsService implements UserDetailsService {

    @Autowired
    private WzxgUserRepository wzxgUserRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        WzxgUser wzxgUser = wzxgUserRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));

        return new org.springframework.security.core.userdetails.User(
                wzxgUser.getUsername(),
                wzxgUser.getPassword(),
                buildAuthorities(wzxgUser.getRole())
        );
    }

    private Collection<? extends GrantedAuthority> buildAuthorities(WzxgUser.WzxgRole role) {
        return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.name()));
    }
}

注意 buildAuthorities 方法里的 "ROLE_" 前缀,这是 Spring Security 的约定。角色 ADMIN 会变成 ROLE_ADMIN,这样后面配置 hasRole("ADMIN") 时框架才能正确匹配。

第五步:JWT 认证过滤器

这是整个流程的关键——每个请求进来时,过滤器从请求头里取出 JWT,验证有效性,然后把用户信息塞进 Spring Security 的上下文。

@Component
public class WzxgJwtAuthFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService wzxgUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        try {
            String wzxgToken = getToken(request);//从请求头获取jwt token
            if (wzxgToken != null) {
                String username = getUsername(wzxgToken);
                UserDetails wzxgUserDetails = wzxgUserDetailsService.loadUserByUsername(username);//根据用户名加载用户详细信息
                UsernamePasswordAuthenticationToken wzxgAuth =
                        new UsernamePasswordAuthenticationToken(
                                wzxgUserDetails, null, wzxgUserDetails.getAuthorities());
                wzxgAuth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(wzxgAuth);
            }
        } catch (Exception e) {
        }
        filterChain.doFilter(request, response);
    }

    private String getToken(HttpServletRequest request) {
        String wzxgBearer = request.getHeader("Authorization");
        if (wzxgBearer != null && wzxgBearer.startsWith("Bearer ")) {
            return wzxgBearer.substring(7);
        }
        return null;
    }

    private String getUsername(String token) {
        try {
            Claims claims = extractClaimsIgnoreExpiration(token);
            return claims != null ? claims.getSubject() : null;
        } catch (Exception e) {
            return null;
        }
    }
}

继承 OncePerRequestFilter 保证每个请求只过滤一次。如果 token 无效或不存在,不会报错,只是不设置认证信息,后续的权限检查自然会拦住未认证的请求。

第六步:安全配置——定义规则

最后把所有东西串起来,告诉 Spring Security:哪些接口谁能访问。

@Configuration
@EnableWebSecurity
public class WzxgSecurityConfig {

    @Autowired
    private WzxgJwtAuthFilter wzxgJwtAuthFilter;

    @Bean
    public SecurityFilterChain wzxgFilterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                // 登录注册不需要认证
                .requestMatchers("/api/v1/auth/register", "/api/v1/auth/login").permitAll()
                // 管理接口只有 ADMIN 能访问
                .requestMatchers("/api/v1/manage/**").hasRole("ADMIN")
                // 普通业务接口,USER 和 ADMIN 都能访问
                .requestMatchers("/api/v1/workspace/**").hasAnyRole("USER", "ADMIN")
                // 其余接口都需要认证
                .anyRequest().authenticated()
            )
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            // 把 JWT 过滤器插到 UsernamePasswordAuthenticationFilter 前面
            .addFilterBefore(wzxgJwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}
  • csrf.disable():因为用的是 JWT 无状态认证,不需要 CSRF 防护
  • SessionCreationPolicy.STATELESS:不创建 session,完全靠 token
  • addFilterBefore:确保 JWT 过滤器在 Spring Security 默认的认证过滤器之前执行

完整请求流程

把上面的模块串起来,一个请求的完整生命周期是这样的:

1. 用户注册 → 密码用 BCrypt 加密 → 分配默认角色 USER → 存入数据库

2. 用户登录 → 验证用户名密码 → 生成 JWT Token → 返回给客户端

3. 客户端请求业务接口 → 请求头带上 Authorization: Bearer <token>

4. WzxgJwtAuthFilter 拦截请求 → 提取 token → 验证签名和有效期
   → 解析出用户名 → 加载用户信息和角色 → 写入 SecurityContext

5. Spring Security 根据 WzxgSecurityConfig 中的规则
   → 检查当前用户的角色是否匹配目标接口的权限要求
   → 通过则放行,否则返回 403

以上就是基于Spring Security实现RBAC权限控制的完整步骤的详细内容,更多关于Spring Security RBAC权限控制的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Java的Hibernate框架中的搜索工具的运用

    详解Java的Hibernate框架中的搜索工具的运用

    这篇文章主要介绍了详解Java的Hibernate框架中的搜索工具的运用,Hibernate是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-11-11
  • Java多线程的用法详解

    Java多线程的用法详解

    本篇文章介绍了,在Java中多线程的用法详解。需要的朋友参考下
    2013-04-04
  • SpringBoot与Elasticsearch8.0集成的实现步骤

    SpringBoot与Elasticsearch8.0集成的实现步骤

    本文介绍了SpringBoot与Elasticsearch8.0的集成方法和最佳实践,详细探讨了Elasticsearch8.0的安全增强、性能优化、API改进等新特性,下面就一起来了解一下
    2026-04-04
  • java实现猜数字小游戏(Swing版)

    java实现猜数字小游戏(Swing版)

    这篇文章主要介绍了java实现猜数字小游戏,Swing编程版的猜数字游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05
  • springdata jpa使用Example快速实现动态查询功能

    springdata jpa使用Example快速实现动态查询功能

    这篇文章主要介绍了springdata jpa使用Example快速实现动态查询功能,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Hadoop环境配置之hive环境配置详解

    Hadoop环境配置之hive环境配置详解

    这篇文章主要介绍了Hadoop环境配置之hive环境配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • Java开发神器Lombok使用详解

    Java开发神器Lombok使用详解

    这篇文章主要介绍了Java开发神器Lombok使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • java基于quasar实现协程池的方法示例

    java基于quasar实现协程池的方法示例

    本文主要介绍了java基于quasar实现协程池的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧<BR>
    2022-06-06
  • 通过java字节码分析学习对象初始化顺序

    通过java字节码分析学习对象初始化顺序

    今天用了jmock对进行单元测试编码,发现一个比较奇怪的语法,static使用方法,见下面例子
    2013-11-11
  • 深入了解java8的foreach循环

    深入了解java8的foreach循环

    虽然java8出来很久了,但是之前用的一直也不多,最近正好学习了java8。下面给大家分享java8中的foreach循环,感兴趣的朋友一起看看吧
    2017-05-05

最新评论