Quarkus系列——基于RBAC来实现权限控制(三)

发布时间 2023-04-03 23:15:35作者: loveletters

前言

我们在之前的两篇文章里面已经讲解了Quarkus的快速入门以及RESTEasy的相关功能,接下来的我将通过一系列日常的使用情景来讲解如何在日常开发中使用Quarkus。

权限控制是我们在开发web系统的时候经常会需要的一种功能,本文我将通过jwt的方式来实现基于RBAC的权限控制。

准备

我们继续基于上文的项目来做开发。实现RBAC需要用到5张表,用户表(t_user),角色表(t_role),权限表(t_permission),用户角色关联表(t_user_role),角色权限关联表(t_role_permission)。我这边简单的设计了一下这些表。

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `t_role_permission` (
  `role_id` int(11) NOT NULL COMMENT '角色id',
  `permission_id` int(11) NOT NULL COMMENT '资源id',
  PRIMARY KEY (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `t_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
  `comment` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `t_user_role` (
  `user_id` int(11) NOT NULL COMMENT '用户id',
  `role_id` int(11) NOT NULL COMMENT '角色id',
  PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `t_user_role` (
  `user_id` int(11) NOT NULL COMMENT '用户id',
  `role_id` int(11) NOT NULL COMMENT '角色id',
  PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

我们在之前的项目中添加一下以下依赖

 <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-jwt-build</artifactId>
        </dependency>
<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.5</version>
        </dependency>

生成token

我们这边使用非对称加密的方式来生成token所以我们还需要一对公/私钥。
简单的写个shell脚本来生成一下。


mkdir jwt

openssl genrsa -out jwt/rsaPrivateKey.pem 2048 -ii

openssl rsa -pubout -in jwt/rsaPrivateKey.pem -out jwt/publicKey.pem

openssl pkcs8 -topk8 -nocrypt -inform pem -in jwt/rsaPrivateKey.pem -outform pem -out jwt/privateKey.pem

chmod 600 jwt/rsaPrivateKey.pem

chmod 600 jwt/privateKey.pem

执行脚本,可以看到已经成功生成了

然后我们把它复制Resource下面去(只是我喜欢放这里)

我们需要在配置文件中添加如下配置


smallrye.jwt.sign.key.location=META-INF/resources/privateKey.pem
mp.jwt.verify.issuer=billets-jwt
mp.jwt.verify.publickey.location=META-INF/resources/publicKey.pem
mp.jwt.token.header=TOKEN

mp.jwt.token.header 表示我们会在请求头中以TOKEN参数来携带生成的token。

我们先准备t_role表的Entity对象

然后我们再创建一个UserRoleBo对象表示一个用户以及他的权限基本信息用于我们来生成token

我们来编码生成token的方法。其中issuer为签发机构,subject为主题,expiresAt为过期时间,groups则为当前用户具有的权限。

接下来,我们去写登陆方法。登陆分为三步,首先验证用户名跟密码,获取用户关联的权限,然后生成对应的token。

我们现在userService中新增一个方法,这里我暂时不做统一的异常处理的拦截器,留在下一期里面再弄。

这个t_role跟t_user_role这两个表的简单关联查询我直接用sql的方式来处理。

最后新增一个LoginController来生成token

ok,到此为止我们已经写完了生成token的代码,我们启动项目到页面上测试一下。

接口权限控制

在上一章节我们已经成功生成了token,现在我们需要基于token对于接口的权限控制。在Quarkus中要实现这个功能还是比较简单的,只需要通过几个简单的注解就可以完成。

@PermitAll 表示允许所有人访问,@RolesAllowed() 只允许指定的角色访问。而角色则在我们生成token的时候groups里面的参数。

页面测试一下,可以看到此时如果我们不携带token是无法访问接口的。

我们先生成token再在请求头中携带上token,可以看到我们能够成功访问到数据了。

我们再修改一下数据库,让当前的用户不具备ADMIN的权限,再重新生成一下token访问一下list接口,可以看到我们已经访问不了

LoginFilter

我们在生成token的时候携带了一些用户相关的信息,我们是可以通过解码token获取到这些数据的。但是在我们每次需要用到的时候都进行一次解码,太过于麻烦,所以我们可以通过连接器在请求前解析这个token,然后再存储起来需要用到的时候直接获取。

为了线程安全起见,这里我们把信息存到线程上下文中。先定义一个线程上下文

接着我们在编写LoginFilter的代码,我们在请求之前解析token放到线程上下文中,然后再在响应之前从线程上下文中移除

后面我们就可以在需要用到的时候直接从线程上下文中获取信息即可。

菜单的权限控制