vertx源码_Vert.x(vertx) 认证和授权

每个线上系统几乎都是离不开认证和授权的,Vert.x提供了灵活、简单、便捷的认证和授权的支持。Vert.x抽象出了两个核心的认证和授权的接口,一个是 AuthProvider,另一个是User。通过这两个接口,我们可以非常灵活的实现我们自定义的认证和授权方法。当然,Vert.x也给我们提供了使用 JDBC、Shiro、MongoDB、JWT等授权的实现,我们可以直接使用。

Vert.x提供的认证和授权都非常简单,多种授权方式都有一定的规律性。一般来讲不需要刻意的学习,在使用的过程中,多读下Vert.x的源码就能够非常清楚的了解到Vert.x认证和授权底层的逻辑。但不是每一位开发者都有时间或者心情去读源码的,所以,这里我简单列出关于Vert.x的认证和授权的应用。

使用Vert.x认证和授权,需要经历三个阶段

1. 自己实现AuthProvider和User接口实现一个简单的认证和授权。

2. 使用Vert.x提供的授权方式,如JDBC

3. 在Web中使用认证和授权来管理访问权限

1. 自定义授权实现

自定义授权就是自己实现AuthProvider和User这两个接口,重写这两个接口中定义的认证和授权方法,这是Vert.x认证和授权最核心的也是最为底层的,把这两个接口弄明白了,后面使用JDBC授权,jwt等等都非常简单。当然了,如果你仅仅是为了使用,那么你可以直接关注第三个阶段,认证和授权在Web中的应用。

比如我们要实现一个最简单的根据用户名和密码的认证,只要认证通过了,就可以访问系统的所有资源这么一个需求,代码如下:

(0) pom中需要引入依赖

io.vertx

vertx-auth-common

3.6.2

(1)应用代码

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /**

2 * 认证与授权测试3 *4 *@authorlenovo5 *6 */

7 public class AuthTest extendsAbstractVerticle {8

9 @Override10 public void start() throwsException {11

12 //创建认证的Provider

13 AuthProvider provider = newUserNameAndPasswordProvider();14 JsonObject authInfo = new JsonObject().put("username", "admin").put("password", "admin");15

16 //调用认证方法,将认证的数据传入

17 provider.authenticate(authInfo, res ->{18 if(res.succeeded()) {19 //认证通过,可以获取到User对象,通过User对象可以进行权限校验

20 User user =res.result();21

22 //授权

23 user.isAuthorized("save:user", auth ->{24 if(auth.succeeded()) {25 System.out.println("授权成功");26 } else{27 System.out.println("授权失败");28 }29 });30 } else{31 System.out.println("认证失败!");32 }33 });34

35 }36

37 public static voidmain(String[] args) {38 Vertx.vertx().deployVerticle(newAuthTest());39 }40

41 }

View Code

用法非常简单,首先创建一个AuthProvider,这里我们使用了

UserNameAndPasswordProvider

这个类是我们自己定义的一个使用用户名和密码进行认证的一个Provider,这个类需要username和password,所以我们将这两个参数放到authInfo中,传递给

authenticate

这个方法,这个方法会异步返回认证的结果。如果认证成功,会返回授权的对象User,调用User对象的

isAuthorized

可以进行验证是否授权。下面是UserNameAndPasswordProvider的一个简单实现

(2) UserNameAndPasswordProvider 代码如下

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /**

2 * 自定义认证3 *4 *@authorlenovo5 */

6 public class UserNameAndPasswordProvider implementsAuthProvider {7

8 @Override9 public void authenticate(JsonObject authInfo, Handler>resultHandler) {10

11 //authInfo中存储了认证需要的相关信息,由调用者传入

12 String username = authInfo.getString("username");13 String password = authInfo.getString("password");14

15 //判断用户名和密码是否正确

16 if ("admin".equals(username) && "admin".equals(password)) {17 //密码验证通过,需要实例化授权对象,并在Future中响应给调用者18

19 //实例化授权对象,可以将认证信息传入

20 User user = newMyUser(authInfo);21 //所有情况均成功返回,并将授权对象响应回去

22 resultHandler.handle(Future.succeededFuture(user));23 } else{24 //密码验证不通过,响应认证失败

25 resultHandler.handle(Future.failedFuture("用户名或者密码错误"));26 }27

28 }29

30 }

View Code

看到上面的代码,AuthTest中的逻辑就更加清晰了,代码非常简单,就不多描述了。

(3)User接口实现

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /**

2 * 授权3 *4 *@authorlenovo5 *6 */

7 public class MyUser implementsUser {8

9 privateJsonObject authInfo;10

11 publicMyUser(JsonObject authInfo) {12 this.authInfo =authInfo;13 }14

15 /**

16 * 这里依然是通过resultHandle响应授权信息,返回值为当前对象是为了Fluent调用模式17 */

18 @Override19 public User isAuthorized(String authority, Handler>resultHandler) {20 //一直返回成功

21 resultHandler.handle(Future.succeededFuture(true));22 return this;23 }24

25 @Override26 publicUser clearCache() {27 return null;28 }29

30 @Override31 publicJsonObject principal() {32 returnauthInfo;33 }34

35 @Override36 public voidsetAuthProvider(AuthProvider authProvider) {37

38 }39

40 }

View Code

这里只是重写了

isAuthorized

这个方法,这个方法里,一直异步响应授权成功,并同步返回当前类的实例,是为了级联调用起来比较方便。这里也非常简单,不多说。

2. 使用Vert.x提供的授权方式

(1)JDBC授权实现

通过Vert.x提供的接口我们可以自己实现认证和授权,但一般的情况下,我们可能都会选择使用数据库来保存认证和授权信息,如果每次我们都要自己实现JDBCAuthProvider会非常麻烦,重复造轮子,因此Vert.x给我们提供了JDBC授权的实现。用法非常简单。对自定义授权熟悉之后,JDBC授权就非常好理解了。

① 引入pom依赖

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1

2 io.vertx

3 vertx-auth-jdbc

4 3.6.2

5

View Code

②创建数据表,为了简单,我们使用5张表

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 --用户表2 create table t_user (3 id intprimary key auto_increment,4 username varchar(40) not null,5 password varchar(255) not null

6 );7

8 --角色表9 create table t_role(10 id intprimary key auto_increment,11 role_name varchar(40) not null

12 );13

14 --权限表15 create table t_permission(16 id intprimary key auto_increment,17 prefix varchar(40) not null

18 );19

20 --用户角色对应关系表21 create table t_user_role (22 id intprimary key auto_increment,23 user_id int not null,24 role_id int not null

25 );26

27 --角色权限对应关系表28 create table t_role_permission(29 id intprimary key auto_increment,30 role_id int not null,31 per_id int not null

32 );33 ③编写测试类34

35

36 public class JDBCAuthTest extendsAbstractVerticle {37

38 privateJDBCClient jdbcClient;39

40 @Override41 public void start() throwsException {42 //获取到数据库的客户端

43 jdbcClient = newJdbcUtils(vertx).getDbClient();44

45 //这个就是实现了AuthProvider接口的认证的类

46 JDBCAuth auth =JDBCAuth.create(vertx, jdbcClient);47

48 //创建用于认证的参数

49 JsonObject authInfo = newJsonObject();50 auth.authenticate(authInfo, res ->{51 if(res.succeeded()) {52 //获取到授权接口

53 User user =res.result();54 System.out.println("认证成功");55 } else{56 //认证失败

57 System.out.println("认证失败");58 }59 });60

61 }

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 public static voidmain(String[] args) {2 Vertx.vertx().deployVerticle(newJDBCAuthTest());3 }4 }

View Code

运行之后发现,表也找不到,字段也找不到,为啥呢,因为我们创建的表和Vert.x创建的表的表名和字段名都不一样。那么如果我们想要使用我们自己的表结构,就要给JDBCAuth设置要执行的SQL有下面的几个方法

1 auth.setAuthenticationQuery(""); //指定认证的SQL

2 auth.setPermissionsQuery(""); //指定获取用户权限的SQL

3 auth.setRolesQuery(""); //指定获取用户角色的SQL

好了,到这里,JDBC的认证就完成了,是不是用起来还是比较简单的。不再需要我们来实现Provider和User接口了。

(2)JWT授权实现

JWT 是 JSON Web Token 的简称,通过名字就可以知道,JWT一般是用在web开发中,一种token的认证方式。在开发中用的还是比较多的。Web开发认证的实现主要有两种方式,第一种是Session的方式,第二种是Token方式,这两种认证方式的优劣我们这里不进行比较。

JWT认证和上面提到的基于JDBC的认证以及自定义实现的认证不同,JWT认证可以认为是在JDBC认证、手机短信认证或者自定义的认证证实身份之后,给认证者的一个唯一标识,以后认证只需要带着这个标识就可以了,而不需要再带着用户名或者密码进行认证。以此来保证用户信息安全。 对于带着用户名或者密码的这种认证方式,在上送用户名和密码这些敏感信息的时候,要使用https来保证传输信息的安全。

JWT认证核心两个,一个是生成token,第二个是验证客户端上送的token是否正确。下面是具体的开发步骤

①引入pom

io.vertx

vertx-auth-jwt

3.6.2

②JDBC认证,并生成token,返回给客户端

JWTAuthOptions config = new JWTAuthOptions()

.addPubSecKey(new PubSecKeyOptions()

.setAlgorithm("HS256")

.setPublicKey("keyboard cat")

.setSymmetric(true));

JWTAuth provider = JWTAuth.create(vertx, config);

// 模拟认证通过

if("admin".equals("username") ) {

String token = provider.generateToken(new JsonObject(), new JWTOptions());

System.out.println(token);

}

③第二次客户端带着token来,服务端进行校验

1 JWTAuthOptions config = newJWTAuthOptions()2 .addPubSecKey(3 new PubSecKeyOptions().setAlgorithm("HS256")4 .setPublicKey("keyboard cat")5 .setSymmetric(true)6 );7

8 JWTAuth provider =JWTAuth.create(vertx, config);9

10 provider.authenticate(new JsonObject().put("jwt", "dfasdfsadfsadfsdfs"), res ->{11 System.out.println(res.result());12 });

在token中带数据

jwt中可以附带一些非敏感的数据,比如用户的ID,再或者时间戳等。那么该怎么带数据呢,非常简单。注意上面生成token的代码中,传入了两个参数,一个是JsonObject,另一个是JWTOptions。其中,JsonObject就是在token中附带的数据。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 JsonObject data = newJsonObject()2 .put("userId","admin");3

4 String token = provider.generateToken(data, new JWTOptions());

View Code

如上代码,就可以在token中带上userId,那么当我们解析token的时候,就可以取出userId的值了,代码如下。

1 //使用jwt进行认证

2 provider.authenticate(new JsonObject().put("jwt", jwt), auth ->{3 if(auth.succeeded()) {4 JWTUser user =(JWTUser) auth.result();5 JsonObject authData = user.principal(); //这里就是token中解析的数据

6 String userId = authData.getString("userId");7 System.out.println(userId);8 request.response().end("认证成功!");9 } else{10 System.out.println("认证失败");11 request.response().end("token无效");12 }13 });

使用jwt的整个认证过程如下:

1 packagestu.vertx.auth.jwt;2

3 importio.vertx.core.AbstractVerticle;4 importio.vertx.core.Vertx;5 importio.vertx.core.http.HttpServer;6 importio.vertx.core.json.JsonObject;7 importio.vertx.ext.auth.PubSecKeyOptions;8 importio.vertx.ext.auth.jwt.JWTAuth;9 importio.vertx.ext.auth.jwt.JWTAuthOptions;10 importio.vertx.ext.auth.jwt.impl.JWTUser;11 importio.vertx.ext.jwt.JWTOptions;12 importorg.apache.commons.lang3.StringUtils;13 importstu.vertx.auth.basic.UserNameAndPasswordProvider;14

15 public class JwtAuthVerticle extendsAbstractVerticle {16

17 private JWTAuthOptions config = newJWTAuthOptions()18 .addPubSecKey(newPubSecKeyOptions()19 .setAlgorithm("HS256")20 .setPublicKey("keyboard cat")21 .setSymmetric(true));22

23 private JWTAuth provider =JWTAuth.create(vertx, config);24

25 @Override26 public void start() throwsException {27

28 HttpServer server =vertx.createHttpServer();29

30 //处理客户端请求

31 server.requestHandler(request ->{32 JsonObject req =JwtAuthVerticle.parseQuery(request.query());33

34 //判断用户是否带token来认证,如果带token,就直接通过token来认证,否则认为是第一次认证,通过用户名和密码的方式进行认证

35 String jwt = req.getString("jwt");36 if(StringUtils.isBlank(jwt)) {37

38 //先使用默认的用户名密码进行认证

39 UserNameAndPasswordProvider p = newUserNameAndPasswordProvider();40 p.authenticate(req, auth ->{41 if(auth.succeeded()) {42 //认证通过之后,再生成token,以后就使用token进行认证

43 JsonObject data = newJsonObject()44 .put("userId", "admin");45

46 String token = provider.generateToken(data, newJWTOptions());47

48 request.response().end(token);49 } else{50 System.out.println("认证失败");51 request.response().end("认证失败,请输出正确的用户名和密码");52 }53 });54 } else{55

56 //使用jwt进行认证

57 provider.authenticate(new JsonObject().put("jwt", jwt), auth ->{58 if(auth.succeeded()) {59 JWTUser user =(JWTUser) auth.result();60 JsonObject authData =user.principal();61 String userId = authData.getString("");62 System.out.println(userId);63 request.response().end("认证成功!");64 } else{65 System.out.println("认证失败");66 request.response().end("token无效");67 }68 });69 }70

71 });72 server.listen(8080);73 }74

75

76 /**

77 * 把URL后跟的查询字符串转成json对象78 *79 *@paramquery80 *@return

81 */

82 public staticJsonObject parseQuery(String query) {83 JsonObject data = newJsonObject();84 String[] params = query.split("&");85 for(String param : params) {86 String[] k = param.split("=");87 data.put(k[0], k[1]);88 }89 returndata;90 }91

92 public static voidmain(String[] args) {93 Vertx.vertx().deployVerticle(newJwtAuthVerticle());94 }95 }

(3)Shiro授权

在应用开发中,很多的应用场景都使用shiro来进行认证和授权。在Vert.x中,也提供了对Shiro的支持。对于shiro的用法这里不再详细的介绍,大家可以参考网络上关于shiro的文章,我们这里仅仅介绍shiro在Vert.x中的应用。

在Vert.x中使用shiro,首先要导入shiro的依赖,以及Vert.x-shiro的依赖包,如下

io.vertx

vertx-auth-shiro

3.5.6

下面创建shiro的配置文件auth.properties

user.tim=sausages,morris_dancer,developer,vtoons

role.developer=do_actual_work

role.vtoons=place_order

user.admin=admin,manager

在配置文件中,配置了一个admin用户,他的密码也是admin,他具有manager角色,下面是认证和授权的案例代码

1 /**

2 * 使用shiro实现认证和授权的演示案例3 *4 *@authorlenovo5 */

6 public class ShiroAuthVerticle extendsAbstractVerticle {7

8 @Override9 public void start() throwsException {10 JsonObject config = new JsonObject().put("properties_path", "classpath:auth.properties");11 ShiroAuth provider = ShiroAuth.create(vertx, newShiroAuthOptions().setType(ShiroAuthRealmType.PROPERTIES).setConfig(config));12

13 JsonObject data = newJsonObject()14 .put("username", "admin")15 .put("password", "admin");16 provider.authenticate(data, auth ->{17 if(auth.succeeded()) {18 System.out.println("认证成功");19 User user =auth.result();20 user.isAuthorized("role:manager",res->{21 if(res.result()) {22 System.out.println("授权成功");23 } else{24 System.out.println("没有权限");25 }26 });27 } else{28 System.out.println("认证失败");29 }30 });31 }32

33 @Override34 public void stop() throwsException {35 super.stop();36 }37

38 public static voidmain(String[] args) {39 Vertx.vertx().deployVerticle(newShiroAuthVerticle());40 }41 }

关于Vert.x提供的认证和授权还有 MongoDB认证,OAuth2认证等等,这里就不再多说了,大家感兴趣的话,可以参考https://vertx.io/docs/vertx-auth-oauth2/java/这里有对oauth2的介绍,关于认证和授权,就说到这里了,下面是认证和授权在Web应用中的使用。

3. 认证和授权在Web中的应用

有了AuthProvider和User之后,再来看认证和授权在Web中的应用就非常简单了。这里我们在Web应用中使用JDBC授权的实现,这也是Web开发中最为常用的。既然使用JDBC授权,那么首先就要创建数据库,创建表,这里我们就使用Vert.x定义的表,如果你的需求比较复杂,可以定义更复杂的模型,这里为了简单,就不再扩展了。

① 建表语句如下:

1 CREATE TABLE `user` (2 `username` VARCHAR(255) NOT NULL,3 `password` VARCHAR(255) NOT NULL,4 `password_salt` VARCHAR(255) NOT NULL5 );6

7 CREATE TABLE `user_roles` (8 `username` VARCHAR(255) NOT NULL,9 `role` VARCHAR(255) NOT NULL10 );11

12 CREATE TABLE `roles_perms` (13 `role` VARCHAR(255) NOT NULL,14 `perm` VARCHAR(255) NOT NULL15 );

注意:MySQL8 默认对表名区分大小写,JDBCAuth的实现类中,对表名是大写的,这就会导致提示找不到表的问题。

② 在路由中使用授权

比如我们想对 /private/* 的请求需要进行认证,其他的请求不需要授权都可以访问,那么我们就可以只针对/private/*实现拦截,然后进行权限的过滤。

router.route("/private/*").handler(authHandler);

路由后跟一个处理器,也就是拦截到/private/*的请求之后该如何处理,这里不需要再重复造轮子了,可以使用Vert.x提供的处理器RedirectAuthHandler,如下

AuthHandler authHandler = RedirectAuthHandler.create(authProvider,"/login.html");

create方法有两个参数,第一个就是我们上面花了大量篇幅所描述的authProvider,第二个参数很明显是一个url,表示如果认证失败,要跳转的页面。当然认证失败之后要跳转到登录页面,让用户进行登录了。下面是authProvider是如何创建的呢?

AuthProvider authProvider = JDBCAuth.create(vertx, jdbcClient);

到这里,在web应用中使用JDBC认证就完成了,是不是非常简单。但到这里,我们只是实现了一个认证的处理器,是不是还需要提供一个登录的处理器呢,不提供登录的入口,不管如何访问,都永远会跳转到登录页。对于登录的实现也非常简单,Vert.x也给我们提供了登录的处理器。

1 FormLoginHandler formLoginHandler =FormLoginHandler.create(authProvider)2 .setDirectLoggedInOKURL("/index.html");3

4 router.route("/login").handler(formLoginHandler);

当用户访问/login时,会使用FormLoginHandler,这个handle会读取到表单提交上来的用户名和密码,然后传递给authProvider进行认证,如果认证通过,则会跳转到setDirectLoggedInOKURL所指定的地址。当认证通过之后,再访问/private下的资源就可以了。

1 router.route("/private/hello").handler(re ->{2 re.user().isAuthorized("role:manager", a ->{3 System.out.println(a.succeeded());4 re.response().end("Over");5 });6 });

比如有上面的私有路径,在登录之前,访问会跳转到登录页面,当登录成功之后,就可以进入到handle中。通过routeContext对象可以获取到user对象,通过user对象的isAuthorized方法可以判断是否有权限。这就完成了认证和授权的整个过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值