<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-common</artifactId>
<version>5.0.1</version>
</dependency>
通用认证与授权
此 Vert.x 组件提供认证和授权接口,可用于您的 Vert.x 应用程序,并可由不同的提供者支持。
Vert.x auth 也被 vertx-web 用于处理其认证和授权。
要使用此项目,请将以下依赖项添加到您的构建描述符的依赖项部分
-
Maven(在您的
pom.xml
中)
-
Gradle(在您的
build.gradle
文件中)
compile 'io.vertx:vertx-auth-common:5.0.1'
基本概念
认证意味着验证用户的身份。
授权意味着验证用户是否有权执行特定任务
为支持多种模型并保持高度灵活性,所有授权操作均在类型 Authorization
上执行。
在某些情况下,授权可能代表一种权限,例如访问所有打印机或特定打印机的权限。在其他情况下,授权可能是一个角色(例如:'admin'、'manager'等)。为了提供一小部分实现,以下工厂可用:
-
RoleBasedAuthorization
基于角色的授权。 -
PermissionBasedAuthorization
基于权限的授权。 -
WildcardPermissionBasedAuthorization
作为通配符匹配的基于角色的授权。 -
AndAuthorization
逻辑授权。 -
OrAuthorization
逻辑授权。 -
NotAuthorization
逻辑授权。
这组授权代表了任何类型的授权,例如:
-
基于角色的授权
-
基于权限的授权
-
逻辑授权(AND、OR、NOT)
-
基于时间的授权(例如:允许在每月的最后 5 天,早上 8 点到 10 点访问等)
-
基于上下文的授权(例如:如果 IP 地址是 'xxx.xxx.xxx.xxx' 则允许访问)
-
基于自定义的授权(例如:基于脚本或针对特定应用程序硬编码的代码)
-
等等…
要了解特定的 AuthorizationProvider
期望什么,请查阅该认证提供者的文档。
认证
要认证用户,请使用 authenticate
。
第一个参数是一个包含认证信息的 Credentials 对象。它实际包含的内容取决于具体的实现;对于简单的基于用户名/密码的认证,它可能包含类似以下内容:
{
"username": "tim",
"password": "mypassword"
}
对于基于 JWT 令牌或 OAuth bearer 令牌的实现,它可能包含令牌信息。
认证是异步进行的,结果通过调用中提供的结果处理器传递给用户。异步结果包含一个 User
实例,该实例代表已认证的用户。
认证用户对象没有关于该对象有权获得哪些授权的上下文或信息。授权和认证分离的原因是,认证和授权是两个不同的操作,不需要在同一提供者上执行。一个简单的例子是,使用普通的 OAuth2.0
进行认证的用户可以使用 JWT
授权提供者来匹配给定权限的令牌,或者其他任何场景,例如使用 LDAP
进行认证并使用 MongoDB
执行授权。
这是一个使用简单用户名/密码实现来认证用户的示例:
Credentials authInfo =
new UsernamePasswordCredentials("tim", "mypassword");
authProvider.authenticate(authInfo)
.onSuccess(user -> {
System.out.println("User " + user.principal() + " is now authenticated");
})
.onFailure(Throwable::printStackTrace);
授权
一旦您拥有一个 User
实例,您可以调用 authorizations
来获取其授权。新创建的用户将不包含任何授权。您可以直接在 User
本身或通过 AuthorizationProvider
添加授权。
上述所有结果都在处理器中异步提供。
这是一个通过 AuthorizationProvider
添加授权的示例:
authorizationProvider.getAuthorizations(user)
.onSuccess(done -> {
// cache is populated, perform query
if (PermissionBasedAuthorization.create("printer1234").match(user)) {
System.out.println("User has the authority");
} else {
System.out.println("User does not have the authority");
}
});
另一个在基于角色的模型中进行授权的示例,它使用接口 RoleBasedAuthorization
。
请注意,如上所述,权限字符串的解释完全由底层实现决定,Vert.x 在此不作任何假设。
用户主体和属性
您可以使用 principal
获取与已认证用户对应的主体。
此返回的内容取决于底层实现。主体映射是用于创建用户实例的源数据。属性是额外的属性,它们在实例创建时未提供,但作为用户数据处理的结果而产生。这种区分旨在确保对主体的处理不会篡改或覆盖现有数据。
为了简化使用,可以使用两个辅助方法在两个源上查找和读取值
if (user.containsKey("sub")) {
// the check will first assert that the attributes contain
// the given key and if not assert that the principal contains
// the given key
// just like the check before the get will follow the same
// rules to retrieve the data, first "attributes" then "principal"
String sub = user.get("sub");
}
创建您自己的认证或授权提供者实现
伪随机数生成器
由于 Java 的 Secure Random 在从系统获取熵时可能会阻塞,我们提供了一个简单的包装器,可以在不阻塞事件循环的危险下使用它。
默认情况下,此 PRNG 使用混合模式,播种时阻塞,生成时非阻塞。PRNG 还会每 5 分钟用 64 位新熵重新播种。但是,所有这些都可以通过系统属性进行配置:
-
io.vertx.ext.auth.prng.algorithm 例如:SHA1PRNG
-
io.vertx.ext.auth.prng.seed.interval 例如:1000(每秒)
-
io.vertx.ext.auth.prng.seed.bits 例如:128
除非您注意到应用程序的性能受到 PRNG 算法的影响,否则大多数用户无需配置这些值。
共享伪随机数生成器
由于伪随机数生成器对象资源开销大,它们消耗稀缺的系统熵资源,因此在所有处理器之间共享 PRNG 是明智的。为了实现这一点并使其可用于 Vert.x 支持的所有语言,您应该研究 VertxContextPRNG
。
该接口放宽了 PRNG 对终端用户的生命周期管理,并确保它可以在您的所有应用程序中重用,例如:
String token = VertxContextPRNG.current(vertx).nextString(32);
// Generate a secure random integer
int randomInt = VertxContextPRNG.current(vertx).nextInt();
使用密钥
在处理安全性时,您将面临加载安全密钥的需求。安全密钥有多种格式和标准,这使得它成为一项相当复杂的任务。为了简化开发人员的工作,本模块包含 2 个抽象:
-
KeyStoreOptions
抽象了 JVM 密钥库的通用格式。 -
PubSecKeyOptions
抽象了 PEM 的通用格式。
要加载本地密钥库,模块应请求一个选项对象,例如:
KeyStoreOptions options = new KeyStoreOptions()
.setPath("/path/to/keystore/file")
.setType("pkcs8")
.setPassword("keystore-password")
.putPasswordProtection("key-alias", "alias-password");
类型非常重要,因为它随所使用的 JVM 版本而异。在版本 9 之前,默认是 jks
,它是 JVM 特定的;之后是 pkcs12
,这是一个通用标准。
非 JVM 密钥库密钥可以导入到 pkcs12
文件中,甚至无需使用 keytool
命令,例如这可以通过 OpenSSL
完成:
openssl pkcs12 -export -in mykeycertificate.pem -out mykeystore.pkcs12 -name myAlias -noiter -nomaciter
上述命令会将一个现有的 pem 文件转换为 pkcs12 密钥库,并将给定的密钥放在 myAlias
名称下。需要额外的参数 -noiter -nomaciter
以使文件与 JVM 加载器兼容。
要加载 PEM
文件,您应该注意有一些限制。默认的 JVM 类仅支持 PKCS8
格式的密钥,因此如果您有不同的 PEM 文件,您需要使用 OpenSSL
转换它,例如:
openssl pkcs8 -topk8 -inform PEM -in private.pem -out private_key.pem -nocrypt
之后,使用这样的文件就如同以下示例一样简单:
PubSecKeyOptions options = new PubSecKeyOptions()
.setAlgorithm("RS256")
.setBuffer(
vertx.fileSystem()
.readFileBlocking("/path/to/pem/file")
.toString());
PEM 文件常见且易于使用,但没有密码保护,因此私钥很容易被嗅探。
JSON Web 密钥
JWK 是 OpenID Connect 和 JWT 提供者使用的标准。它们将密钥表示为 JSON 对象。通常这些 JSON 文档由身份提供者服务器(如 Google、Microsoft 等)提供,但您也可以使用在线应用程序 https://mkjwk.org 生成自己的密钥。对于离线体验,还有一个工具:https://connect2id.com/products/nimbus-jose-jwt/generator。