当前位置: 代码网 > it编程>编程语言>Java > 基于Spring Security实现RBAC权限控制的完整步骤

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

2026年04月22日 Java 我要评论
什么是 rbacrbac,全称 role-based access control,基于角色的访问控制。核心思路很简单:不直接给用户分配权限,而是引入"角色"作为中间层。用户 &r

什么是 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权限控制的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com