 
本指南将向您展示如何构建并保护一个简单的 符合 FIDO2 标准(通常称为 Web Authentication API)的 vert.x Web 应用程序。Web Authentication API(也称为 WebAuthn)是由 W3C 和 FIDO 联盟在 Google、Mozilla、Microsoft、Yubico 和其他公司的参与下编写的规范。该 API 允许服务器使用公钥加密而不是密码来注册和认证用户。
通过本指南,您将拥有一个无需密码即可执行身份验证的运行中的应用程序。这将通过 WebAuthn 实现,WebAuthn 是一个新的 W3C 全球标准,用于 Web 上的安全身份验证,并受所有主流浏览器和平台支持。
文本编辑器或 IDE
Java 8 或更高版本(建议使用 11 或 >=15 获取额外的安全算法)
互联网连接
Web Authentication API 是一个安全的 API,供应商决定遵循最佳实践。虽然您可以在不使用 SSL 的情况下构建服务器,但现代 Web 浏览器不会连接或允许将 WebAuthn API 用于不使用 SSL 的服务器,即使是在开发过程中也是如此。
在我们开始使用 WebAuthn 之前,我们需要确保即使是我们的开发应用程序也已启用 SSL。为此,我们需要创建一个有效的 SSL 证书。请注意,自签名证书仍然允许使用
要为您的 IP 地址创建自签名证书,请执行以下操作:
export IP=10.0.0.2
export CERTSTORE_SECRET=password    # (1)
keytool \
  -genkeypair \
  -alias rsakey \
  -keyalg rsa \
  -storepass ${CERTSTORE_SECRET} \
  -keystore certstore.jks \
  -storetype JKS \
  -dname "CN=${IP}.nip.io,O=Vert.x Development" # (2)不要使用此密码!
将 CN 替换为您自己的 IP 地址(localhost 除外),后缀为 .nip.io
对于此设置,我们依赖于一个免费的 DNS 服务器,它在查询时返回您的 IP 地址。Web 上也存在其他提供相同结果的服务,例如:
目前我们有了一个 SSL 证书,但对于现代 Java 版本而言,其格式被认为是过时的,因此我们需要第二步将其转换为 PKCS#12
keytool \
  -importkeystore \
  -srckeystore certstore.jks \
  -destkeystore certstore.jks \
  -deststoretype pkcs12您的新 SSL 证书位于 certstore.jks 文件中。
在能够登录您的应用程序之前,我们需要注册一个 FIDO2 身份验证器。此过程类似于 Web 应用程序中的“注册”流程。然而,此图试图说明一些差异
 
用户仅使用用户名注册
您的服务器(依赖方)创建安全挑战
浏览器将此信息传递给令牌设备
令牌为此信息生成新的密钥对
挑战被签名并返回给服务器(RP)
服务器验证挑战是否正确并存储公钥
 
用户仅使用用户名进行身份验证
您的服务器(依赖方)创建安全挑战
浏览器将此信息传递给令牌设备
令牌生成验证凭据并签署挑战
浏览器创建身份验证断言
服务器验证断言是否正确并允许用户访问
您可以在此处找到完整的源代码,现在我们只介绍重要部分。
为了实现注册和身份验证功能,我们需要能够存储和查询身份验证器数据。为此,我们需要提供一些精确执行此操作的函数。您可以在此处查看其源代码。在您的 Verticle 中,您首先创建此对象,如下所示:
    // Dummy database, real world workloads
    // use a persistent store or course!
    InMemoryStore database = new InMemoryStore();为了使用 FIDO2,我们需要配置身份验证提供者如何工作。为此,我们需要创建并配置一个 WebAuthn 对象
    // create the webauthn security object
    WebAuthn webAuthN = WebAuthn.create(
      vertx,
      new WebAuthnOptions()   // (1)
        .setRelyingParty(new RelyingParty()
          .setName("Vert.x FIDO2/webauthn Howto"))
        .setUserVerification(UserVerification.DISCOURAGED)  // (2)
        .setAttestation(Attestation.NONE)   // (3)
        .setRequireResidentKey(false)   // (4)
        .setChallengeLength(64)   // (5)
        .addPubKeyCredParam(PublicKeyCredential.ES256)    // (6)
        .addPubKeyCredParam(PublicKeyCredential.RS256)
        .addTransport(AuthenticatorTransport.USB)   // (7)
        .addTransport(AuthenticatorTransport.NFC)
        .addTransport(AuthenticatorTransport.BLE)
        .addTransport(AuthenticatorTransport.INTERNAL))
      // where to load/update authenticators data
      .authenticatorFetcher(database::fetcher)
      .authenticatorUpdater(database::updater);所有配置都在 WebAuthnOptions 中进行。这只是一个合理默认值的小例子,有关更多选项,请查阅 javadoc。
验证用户时,我们并不真正要求他们被验证。
在注册过程中,我们不希望验证硬件。
我们不需要常驻密钥来让用户进行身份验证。
定义挑战的长度,最小为 32。
我们接受哪些安全算法。
我们允许从浏览器到身份验证器使用哪种传输方式。
让我们开始配置我们的 HTTP 路由以确保安全。在使用 FIDO2 之前,有几个处理器始终需要到位
BodyHandler
SessionHandler
还建议使用 StaticHandler,因为整个过程既需要 vert.x 代码(我们目前正在探索的)和一个小的辅助 JavaScript 脚本。为了简化开发,vert.x 也通过 Maven 依赖提供了这种辅助功能
  <dependencies>
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-web</artifactId>
    </dependency>
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-auth-webauthn</artifactId>
    </dependency>
    <dependency>    <!--(1)-->
      <groupId>io.vertx</groupId>
      <artifactId>vertx-auth-webauthn</artifactId>
      <version>${vertx.version}</version>
      <classifier>client</classifier>
      <type>js</type>
    </dependency>
  </dependencies>提供了一个简单的辅助脚本,用于与 vert.x 后端交互。
现在我们可以像这样初始化 Web 路由器
    final Router app = Router.router(vertx);
    app.route()   // (1)
      .handler(StaticHandler.create());
    app.post()    // (2)
      .handler(BodyHandler.create());
    app.route()   // (3)
      .handler(SessionHandler
        .create(LocalSessionStore.create(vertx)));
    WebAuthnHandler webAuthnHandler = WebAuthnHandler.create(webAuthN) // (4)
      .setOrigin(String.format("https://%s.nip.io:8443", System.getenv("IP")))
      // required callback
      .setupCallback(app.post("/webauthn/callback"))
      // optional register callback
      .setupCredentialsCreateCallback(app.post("/webauthn/register"))
      // optional login callback
      .setupCredentialsGetCallback(app.post("/webauthn/login"));
    app.route()
      .handler(webAuthnHandler);
    app.route("/protected")   // (5)
      .handler(ctx ->
        ctx.response()
          .end(
            "FIDO2 is Awesome!\n" +
              "No Password phishing here!\n"));为客户端应用程序提供服务(稍后详述)。
启用 POST 请求体的解析。
启用会话。
使用之前定义的配置挂载 webauthn 处理器。
示例安全路由。
现在我们有了一个最小的路由器,我们需要创建一个 HTTPS 服务器。请注意,这是一个必需的步骤,也是我们为开发环境创建自签名证书并使用自定义域名k 的原因。
    vertx.createHttpServer(
      new HttpServerOptions()
        .setSsl(true)
        .setKeyStoreOptions(
          new JksOptions()
            .setPath("certstore.jks")
            .setPassword(System.getenv("CERTSTORE_SECRET"))))
      .requestHandler(app)
      .listen(8443, "0.0.0.0")
      .onSuccess(v -> {
        System.out.printf("Server: https://%s.nip.io:8443%n", System.getenv("IP"));
        start.complete();
      })
      .onFailure(start::fail);至此,我们的后端应用程序已完成,路由 /protected 应受 FIDO2 保护。运行应用程序如下:
IP=10.0.0.2 \
mvn exec:java
# The following line should be present in your console:
# Server listening at: https://10.0.0.2.nip.io:8443您的浏览器给出关于自签名证书的警告是正常的
 
这是为了您的保护。在真实的应用程序中,您应该使用适当的 SSL 证书,例如由 Let's Encrypt 颁发的证书。
用您的浏览器导航到:https://10.0.0.2.nip.io:8443/protected 应该会显示一个 Forbidden 错误。下一步是创建一个最小的登录和注册 Web 应用程序。
对于客户端应用程序,不使用任何框架,以表明该脚本可以与任何框架一起使用,也可以独立使用。
我们将创建一个包含 3 个部分的极简 HTML 页面
一个表单,用户只需输入以下内容即可注册或登录:
显示名称:例如,一个用户友好的名称,如“John Doe”
用户名:一个唯一的用户名,如“[email protected]”
用于注册和登录的按钮
一个指向受保护资源的链接,只有在注册和登录后才允许访问。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>WebAuthn Howto</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="message"></div>
<div> <!--(1)-->
  <label for="displayName">Display Name: </label>
  <input id="displayName" name="displayName" type="text" value="Your Name"><br/>
  <label for="username">Username: </label>
  <input id="username" name="username" type="email" value="[email protected]">
</div>
<hr>
<div> <!--(2)-->
  <button id="register">register</button>
  <button id="login">login</button>
</div>
<hr>
<div> <!--(3)-->
  <a href="/protected">Want some secret info?</a>
</div>
<script src="vertx-auth-webauthn-client.js"></script>   <!--(4)-->
<script src="main.js"></script>   <!--(5)-->
</body>
</html>如上所述的第一部分。
如上所述的第二部分。
如上所述的第三部分。
已添加到项目依赖项中的辅助脚本
您的应用程序脚本
您可以在此处查看完整脚本。我们只介绍重要部分。
所包含的脚本 vertx-auth-webauthn-client.js 定义了一个全局类型 WebAuthn。第一步是创建此对象的一个实例并对其进行配置,使其与我们的后端配置匹配。
const webAuthn = new WebAuthn({
  callbackPath: '/webauthn/callback',
  registerPath: '/webauthn/register',
  loginPath: '/webauthn/login'
});配置应该很简单,只需定义与您的后端路由匹配的路径即可。
接下来,我们需要为按钮添加事件处理程序。我们从注册操作开始
registerButton.onclick = () => {
  webAuthn
    .register({
      name: document.getElementById('username').value,
      displayName: document.getElementById('displayName').value
    })
    .then(() => {
      displayMessage('registration successful');
    })
    .catch(err => {
      displayMessage('registration failed');
      console.error(err);
    });
};onclick 事件将只使用 Webauthn 对象。Webauthn 对象只有 2 个方法,它是基于 Promise 的,因此对于 vert.x 用户来说应该不难理解。如果您主要是 Java vert.x 开发人员,只需将 JavaScript Promise 视为 vert.x Future,那么一切都会非常相似。
要注册用户,只需要 2 个必需属性
name 唯一的用户名,例如,电子邮件地址。
displayName 人类友好的文本描述,例如您的名字和姓氏。
填写表单并点击注册按钮后,用户应该会看到一个弹出窗口,要求授权注册。
 
触摸身份验证器后,流程应成功完成,显示
 
此时您可以关闭浏览器甚至打开一个不同的浏览器。注册过程已完成,因此您现在可以使用您的令牌从任何地方登录。
我们现在需要处理登录按钮的 onclick 事件
loginButton.onclick = () => {
  webAuthn
    .login({
      name: document.getElementById('username').value
    })
    .then(() => {
      displayMessage('You are logged in');
    })
    .catch(err => {
      displayMessage('Invalid credential');
      console.error(err);
    });
};正如 FIDO2 所描述的,使用 WebAuthn 是一种无密码身份验证,因此执行登录唯一需要的字段是
name 唯一的用户名,例如,电子邮件地址。
 
就像在注册屏幕中一样,将弹出一个窗口,告诉您有登录意图并请求用户授权。当您触摸身份验证器时,您可以看到
 
现在您已登录,终于可以尝试查看秘密信息链接了,它应该会给您一些类似的东西
 
在本操作指南中,我们介绍了
创建一个 Web 项目
使用 Webauthn 保护 Web 应用程序
在 webauthn-client.js 的帮助下编写客户端代码
希望您现在可以在下一个项目中使用 FIDO2/webauthn 了!
最后发布:2025-06-11 02:03:27 +0000。