Vert.x 4 Web JWT Token认证/鉴权入门

编程教程 > Java > Vert.x (276) 2024-11-26 14:39:04

前言

前面已经学习了Vert.x web的基础接口,本文主要讲解引入jwt为接口认证/鉴权。引用之前创建的项目《Vert.x 4 Web应用初识》,加入jwt token相关依赖 io.vertx:vertx-auth-jwt,开始了Vert.x中的Jwt认证之旅。

 

引入jwt依赖

maven pom.xml 依赖节点引入jwt相关依赖

<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-jwt</artifactId>
<version>4.5.9</version>
</dependency>

 

生成RSA密钥对备用

生成rsa密钥对参考 《OpenSSL 工具生成RSA密钥对》这里只需要最简单不加密的RSA密钥对就行。生成好以后复制到项目中,位置参考下面项目结构图

Vert.x 4 Web JWT Token认证/权限入门_图示-b364f46c4ed04a2c94725804ad4640e4.png
RSA密钥对

 

JWT TOKEN对接口认证/授权实现

创建jwt认证提供者(AuthenticationProvider)

在MainVerticle,start中创建jwt的认证提供者

代码聚焦

//采用RSA256非对称加密
Buffer privateKeyBuf = vertx.fileSystem().readFileBlocking("certs/rsa_private_2048.pem");//相对于resources目录
Buffer publicKeyBuf = vertx.fileSystem().readFileBlocking("certs/rsa_public_2048.pem");
JWTAuth jwtAuth = JWTAuth.create(vertx,
new JWTAuthOptions()
.addPubSecKey(
new PubSecKeyOptions()
.setAlgorithm("RS256")
.setBuffer(publicKeyBuf)
)
.addPubSecKey(
new PubSecKeyOptions()
.setAlgorithm("RS256")
.setBuffer(privateKeyBuf)
)
);

注意点:

  • 必须指定算法
  • 私钥,公钥都需要配置

 

模拟登录生成token

模拟用户输入用户名和密码登录,成功返回toen字符串

代码聚焦

router.post("/login").handler(ctx->{
String username = ctx.request().getParam("username");
String password = ctx.request().getParam("password");
if ("admin".equals(username) && "admin".equals(password)) {
//初始化几个权限和角色数据(实际应用这里应该调用数据库查询对应用户的角色权限)
List<String> roles = new ArrayList<>();
roles.add("admin");
roles.add("user");

List<String> permissions = new ArrayList<>();
permissions.add("list_products");

ctx.response()
.end(jwtAuth.generateToken(
new JsonObject()
.put("permissions", permissions)
.put("roles",roles)
.put("username", username)
.put("someKey", "someValue")
,
new JWTOptions()
.setIgnoreExpiration(false)
.setExpiresInMinutes(30)
.setAlgorithm("RS256") //默认值是HS256,所有必须指定算法RS256
)

);
}else {
ctx.fail(401);
}
});

以上代码实现了登录业务,包含

  • 鉴别用户是否合法
  • 用户合法后设置用户权限和角色编码
  • 生成jwt RS256加密的token
  • 用户不合法返回401 HTTP状态码

使用postman工具测试

Vert.x 4 Web JWT Token认证/权限入门_图示-4bf95e50896849a29a8023cf1f5e984a.png
错误密码返回 401
Vert.x 4 Web JWT Token认证/权限入门_图示-9f266e198e224b9f9bb735fe109e8dda.png
正确密码返回token值

以上操图作可以看到,与我们预期结果一致。

设定接口需要【认证/登录】才能访问

代码聚焦

//创建token后则访问 需要认证(登录)的资源
AuthenticationHandler authenticationHandler = JWTAuthHandler.create(jwtAuth);
router.route("/protected/*").handler(authenticationHandler);
router.route("/protected/info").handler(ctx ->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("protected info !");
});
//token 请求时候放入头部 Authorization Bearer <token>

此处jwtAuth 就是上面创建的jwtAuth RSA

上方代码表示访问/protected/开头的接口都需要认证才行否则返回401

 

使用postman工具测试

 

首先是直接访问接口/protected/info

Vert.x 4 Web JWT Token认证/鉴权入门_图示-b938b782dd2542d1a03f056051585cff.png
直接访问需要认证的接口

直接访问需要认证的接口,可以看到上方返回401状态码并返回未认证/未登录,与预期结果一致。

 

接下来设置token,使用认证的token来访问接口。

首先调用登录生成token,然后复制到下图的token位置

Vert.x 4 Web JWT Token认证/权限入门_图示-d1d0f3ca9cf24ecb931b159d17ab02a5.png
设置接口访问token
Vert.x 4 Web JWT Token认证/权限入门_图示-4ffa830e0db24258a30bd39907ab106a.png
token header

再次请求

Vert.x 4 Web JWT Token认证/鉴权入门_图示-de36f7d0013d41af99c6ca2b927f1fe9.png
携带token正常访问

从上图可以看到携带token再次请求接口成功获得了响应内容,与预期结果一致。

 

设置接口需要指定【授权】才能访问

代码聚焦

//意思是从jwt token的obj中拿出permissions节点进行权限判断
AuthorizationProvider authorizationProvider = JWTAuthorization.create("permissions");

//[/protected/list_products]开头的路径需要list_products权限才能访问,从authorizationProvider表示jwt中拿出permission节点进行和list_products对比
router.route("/protected/list_products*").handler(
AuthorizationHandler.create(PermissionBasedAuthorization.create("list_products"))
.addAuthorizationProvider(authorizationProvider)
);
router.route("/protected/list_products").handler(ctx->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("list_products Go !");
});

//指定地址指定权限
router.route("/protected/list_products/one").handler(
AuthorizationHandler.create(PermissionBasedAuthorization.create("one_products"))
.addAuthorizationProvider(authorizationProvider)
).handler(ctx->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("one_products Go !");
});

上方代码指定接口权限对应

  • /protected/list_products* 开头需要list_products权限
  • /protected/list_products 需要list_products权限
  • /protected/list_products/one 需要list_productsone_products权限

使用postman工具测试验证

注意:需要授权的接口是肯定需要认证的,所以以下接口测试都将默认携带上面生成的token

 

Vert.x 4 Web JWT Token认证/鉴权入门_图示-75c88d99fc9e47dabfd289cb6612e424.png
访问list_products接口

访问list_products接口成功,与预期结果一致 (token中包含list_products权限,有疑问可以返回查看上面的token生成部分)

Vert.x 4 Web JWT Token认证/鉴权入门_图示-a69a9535ea464b6e821e0df15379f1a6.png
访问one接口

访问one接口,从上图可以看到403了,与预期结果一致(token中没有包含one_products权限,有疑问可以返回查看上面的token生成部分)

 

指定角色代码访问

这里指定角色代码授权访问其实和权限相差不大,就是从token取值的节点变成了另外一个(同时节点名称也是可以自定义的)

代码聚焦

//意思是从jwt token的obj中拿出roles节点进行权限判断
AuthorizationProvider authorizationProviderRoles = JWTAuthorization.create("roles");
//指定路径需要admin role 才能访问
router.route("/protected/setting/get").handler(
AuthorizationHandler.create(PermissionBasedAuthorization.create("admin"))
.addAuthorizationProvider(authorizationProviderRoles)
).handler(ctx->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("Get setting !");
});

以上代码表示需要admin的角色才能访问该接口,我们上面初始化的token是包含admin的所以可以直接访问,参考下方请求图

Vert.x 4 Web JWT Token认证/鉴权入门_图示-2ddd936f7ba148aa95a195b5d3101791.png
访问指定角色授权接口

 

完整代码

[user]

rsa_private_key.pem

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcB6xtxRGn+gyN
t8qxjRIbVke/vQuO9Gr3AoAT3t8Ja7Ve489PJ/4r/KWtyDGrPcU+gWqjzaNM1Gph
w4ry2RZXQ9Dq92aG4F+hwxhi++ZgsHKNAukVEQdcWb+16idmBhm4/Ro8qoJZP+Je
vCCKpgovcKega+wcKBtBjSnQfxeDJ2A0VDU8js6KVXBiR+8WAyNtKjDHSnUe4z+Z
tJWOmu1yA89J0x3Y2owcejW9BjOoIBFszdRw1alP6EQn5tDB/nCiTxje7k11qBpf
wc5WOuRHOyakq0tsite+x2wvdjNnyN3LzbAhXIpMJDSFQX5KHzSk8qfV2tuHit27
XUTeug0nAgMBAAECggEABNBfy4s2/j3CwWSYRl4Z3uoMkKfegWydGD3lgbdwnjPK
hsD0lnafavAClJgGoEfpnAIWLjOcBDTRi26jDNcRz2NyQK2dZi/qA22nhPZMp1MK
VbtvQY0i8wB537z3tjgd9w0oEBMarnJAI7geMOjqi4goQ9TQlhXOsXkPfzVMnmk+
QazzmKKs4rQNlu+6Usjfg3LfvvXLL2FMrcU/N69ajEH8POLEGczEYNPtgotPffbO
z+t9ScFxsD0tFHIjA+zNDKixcec8/Q/n5gV5V1o+3axKobWAAzaixm95TiGXGxdI
quNjvtn8fTn6dHujZoWr/pj4NpvyxdWse5xiNu500QKBgQDOtw28pwi0EtrUU1Dd
A36FUlkrceeOZohv38WYu1liIeV5K22t83C4f4K8zq2KmKz6QTvp+oBiB6yJfZLi
EeJ8WTMbHOg7dHhfjWqJ/Jpkg9IpiyH4iCK2m/EWf7KafAxNImR+umZP+0n2twUl
SmuFbNwfKtq/u8Tv6QmngKwkHQKBgQDBOwiGkCwFBaqu7J1F6pyinzLdF+nYpQyP
hwv7cO+59CJTORwiFm1ZXJyGi30eY/w8DCIYY8F4svr+EXxDACX1nrp9jkgL47NH
nkIT4afQjJG3uAg4Nt9lHXz7l5hk7PQMtxPHhydvVl0xl8hj3eN6Aomk1AAv09HK
NXogHOSrEwKBgQCkFID+4cbyyJSSPJ/PDtr6kGbfKUaXraNWydRaazuDvUwcZfBl
RvqOOAhaPeNaQ93ptqYMDx6gsV6us9JHR9LyyQrb1pIvvz9c+S874Bnc9xV2jE2m
rMiBEj7HkQz/ur846rfCL8rOabRH9PZMp0m5WrNOugFwd2bW168mGeiJsQKBgAxH
uSn8HaAQFSHazb/0whGftnbQnz7ydlLkzUEkk0epGUlatsv/yuFD/nqagNAeoJgc
WUpdhJ0sGsFs0Q3dA4yRkt6J3VBMH0es6hwjWivp0xTu8C7KZfYiIqBGqRu452Wr
eOlUUJBF19RgBg86ucs2QBMmmgBwIMQOE/3YeDvbAoGAPibHuFlh7+7RKVEBa3uD
6hGWSEmnwxytS1x8bw24qYfzXPWipRWX7Hdtl+VILt7CqCyzXLLxmKkkFzigSC7n
P0FgUdw2GfgQlaNa47jq4TADBH9HeFbl+Qcb+qLvi8T6hPatOwGQXvBOrYHgQ20I
UBa6KgKMNA4DjwcIomSXyUk=
-----END PRIVATE KEY-----
 

 

rsa_public_key.pem

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnAesbcURp/oMjbfKsY0S
G1ZHv70LjvRq9wKAE97fCWu1XuPPTyf+K/ylrcgxqz3FPoFqo82jTNRqYcOK8tkW
V0PQ6vdmhuBfocMYYvvmYLByjQLpFREHXFm/teonZgYZuP0aPKqCWT/iXrwgiqYK
L3CnoGvsHCgbQY0p0H8XgydgNFQ1PI7OilVwYkfvFgMjbSowx0p1HuM/mbSVjprt
cgPPSdMd2NqMHHo1vQYzqCARbM3UcNWpT+hEJ+bQwf5wok8Y3u5NdagaX8HOVjrk
RzsmpKtLbIrXvsdsL3YzZ8jdy82wIVyKTCQ0hUF+Sh80pPKn1drbh4rdu11E3roN
JwIDAQAB
-----END PUBLIC KEY-----
 

 

MainVerticle.java

@Slf4j
public class MainVerticle extends AbstractVerticle {

@Override
public void start(Promise<Void> startPromise) throws Exception {


Router router = Router.router(vertx);


//采用RSA256非对称加密
Buffer privateKeyBuf = vertx.fileSystem().readFileBlocking("certs/rsa_private_2048.pem");//相对于resources目录
Buffer publicKeyBuf = vertx.fileSystem().readFileBlocking("certs/rsa_public_2048.pem");
JWTAuth jwtAuth = JWTAuth.create(vertx,
new JWTAuthOptions()
.addPubSecKey(
new PubSecKeyOptions()
.setAlgorithm("RS256")
.setBuffer(publicKeyBuf)
)
.addPubSecKey(
new PubSecKeyOptions()
.setAlgorithm("RS256")
.setBuffer(privateKeyBuf)
)
);

router.post("/login").handler(ctx->{
String username = ctx.request().getParam("username");
String password = ctx.request().getParam("password");
if ("admin".equals(username) && "admin".equals(password)) {
//初始化几个权限和角色数据(实际应用这里应该调用数据库查询对应用户的角色权限)
List<String> roles = new ArrayList<>();
roles.add("admin");
roles.add("user");

List<String> permissions = new ArrayList<>();
permissions.add("list_products");

ctx.response()
.end(jwtAuth.generateToken(
new JsonObject()
.put("permissions", permissions)
.put("roles",roles)
.put("username", username)
.put("someKey", "someValue")
,
new JWTOptions()
.setIgnoreExpiration(false)
.setExpiresInMinutes(30)
.setAlgorithm("RS256") //默认值是HS256,所有必须指定算法RS256
)

);
}else {
ctx.fail(401);
}
});

//创建token后则访问 需要认证(登录)的资源
AuthenticationHandler authenticationHandler = JWTAuthHandler.create(jwtAuth);
router.route("/protected/*").handler(authenticationHandler);
router.route("/protected/info").handler(ctx ->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("protected info !");
});
//token 请求时候放入头部 Authorization Bearer <token>

//意思是从jwt token的obj中拿出permissions节点进行权限判断
AuthorizationProvider authorizationProvider = JWTAuthorization.create("permissions");

//[/protected/list_products]开头的路径需要list_products权限才能访问,从authorizationProvider表示jwt中拿出permission节点进行和list_products对比
router.route("/protected/list_products*").handler(
AuthorizationHandler.create(PermissionBasedAuthorization.create("list_products"))
.addAuthorizationProvider(authorizationProvider)
);
router.route("/protected/list_products").handler(ctx->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("list_products Go !");
});

//指定地址指定权限
router.route("/protected/list_products/one").handler(
AuthorizationHandler.create(PermissionBasedAuthorization.create("one_products"))
.addAuthorizationProvider(authorizationProvider)
).handler(ctx->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("one_products Go !");
});


//意思是从jwt token的obj中拿出roles节点进行权限判断
AuthorizationProvider authorizationProviderRoles = JWTAuthorization.create("roles");
//指定路径需要admin role 才能访问
router.route("/protected/setting/get").handler(
AuthorizationHandler.create(PermissionBasedAuthorization.create("admin"))
.addAuthorizationProvider(authorizationProviderRoles)
).handler(ctx->{
ctx.response()
.putHeader("content-type", "text/plain")
.end("Get setting !");
});


vertx.createHttpServer()
.requestHandler(router)
.listen(config().getInteger("http.port", 8080), result -> {
if (result.succeeded()) {
startPromise.complete();
}else {
startPromise.fail(result.cause());
}
});
}
}

 

[/user]

 

 


评论
User Image
提示:请评论与当前内容相关的回复,广告、推广或无关内容将被删除。

相关文章
前言前面已经学习了Vert.x web的基础接口,本文主要讲解引入jwt为接口认证/鉴权。引用之前创建的项目《Vert.x 4 Web应用初识》,加入jwt t
前言项目由之前的第一个vert.x 4 项目改编而来,vert.x项目创建参考: vert.x 4 web应用编写修改MainVerticle文件,内容如下:p
Vert.x java 入门,Vert.x这个框架在常规的web业务开发中估计还不是很成熟。但是了解了他的一些原理我觉得可以有一番作为。今天主要简单讲解下eclipse Vert.x是什么有什么...
前言最新的Ver.x 4 从idea工具运行启动,并访问。项目创建通过vert.x官网生成器完成。Vert.x 4项目创建打开vert.x官网项目生成地址,ht
前言项目创建参考之前的《Vert.x 4 Web应用初识》。本文通过Vert.x实现了REST接口的CRUD操作。通过本教程你可以获得以下内容vert.x项目中
前言这里主要讲解Vert.x配置文件的读取和使用,引用之前的项目《Vert.x 4 Web应用初识》 引入必要依赖maven pom.xml&lt;dependenc
前言Vert.x Router路由中多个处理器之间通过RoutingContext共享数据实现。 Vert.x Router 上下文数据数据设置routingC
前言引用《Vert.x 4 Web REST CRUD接口应用》项目,加入日志依赖并编码实现类似Interceptor功能处理。vert.x日志集成参考《Ver
前言文件上传在web应用比较常见,本文以vert.x web实现文件上传功能。引用之前的项目《Vert.x 4 Web应用初识》作为基础,添加了日志。 Vert
前言接上一篇《Vert.x 4 Web应用初识》,在web应用中除了访问地址得到动态的信息还有静态的资源库访问,如 jQuery / bootstrap 等前端
前言创建一个常规的web项目肯定需要一个模板引擎来实现,引用之前的项目《Vert.x 4 Web应用初识》基础结构目前vert.x支持多款模板引擎,包括:MVE
前言Vert.x 中实现全局数据共享,如环境参数等。Vertx数据共享实现共享数据存入/更新SharedData sharedData = vertx.shar
前言本文主要讲解在Vert.x环境下与Mysql数据库连接和操作,实现基础的增删改查和事务,以及REST接口返回数据库中查询结果。项目引用之前的《Vert.x
前言Web项目开发一般接口入参都有校验需求,Vert.x 4 目前已有插件实现参数校验,引用之前的项目《Vert.x 4 Web应用初识》源码 引入验证插件ma
前言vert.x 默认是没有像spring的依赖注入的,需要自己结合vertx-service-proxy插件实现。本文引用项目为基础《Vert.x 4 Web