密钥

Spring Security 提供对密钥的支持。 通行密钥是一种比密码更安全的身份验证方法,并且是使用 WebAuthn 构建的。spring-doc.cn

要使用密钥进行身份验证,用户必须首先注册新凭证。 注册凭证后,可以通过验证身份验证断言来使用它进行身份验证。spring-doc.cn

必需的依赖项

首先,将依赖项添加到您的项目中。webauthn4j-corespring-doc.cn

这假定您正在使用 Spring Boot 或 Spring Security 的 BOM 管理 Spring Security 的版本,如获取 Spring Security 中所述。spring-doc.cn

Passkeys 依赖项
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
    <groupId>com.webauthn4j</groupId>
    <artifactId>webauthn4j-core</artifactId>
    <version>0.28.2.RELEASE</version>
</dependency>
depenendencies {
    implementation "org.springframework.security:spring-security-web"
    implementation "com.webauthn4j:webauthn4j-core:0.28.2.RELEASE"
}

配置

以下配置启用密钥身份验证。 它提供了一种注册新凭证的方法,以及允许使用密钥进行身份验证的默认登录页面。/webauthn/registerspring-doc.cn

@Bean
SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.formLogin(withDefaults())
		.webAuthn((webAuthn) -> webAuthn
			.rpName("Spring Security Relying Party")
			.rpId("example.com")
			.allowedOrigins("https://example.com")
		);
	return http.build();
}

@Bean
UserDetailsService userDetailsService() {
	UserDetails userDetails = User.withDefaultPasswordEncoder()
		.username("user")
		.password("password")
		.roles("USER")
		.build();

	return new InMemoryUserDetailsManager(userDetails);
}
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
	http {
		webAuthn {
			rpName = "Spring Security Relying Party"
			rpId = "example.com"
			allowedOrigins = setOf("https://example.com")
		}
	}
}

@Bean
open fun userDetailsService(): UserDetailsService {
	val userDetails = User.withDefaultPasswordEncoder()
		.username("user")
		.password("password")
		.roles("USER")
		.build()
	return InMemoryUserDetailsManager(userDetails)
}

注册新凭证

要使用密钥,用户必须首先注册 New Credentialspring-doc.cn

注册新凭证包括两个步骤:spring-doc.cn

  1. 请求注册选项spring-doc.cn

  2. 注册凭证spring-doc.cn

请求注册选项

注册新凭证的第一步是请求注册选项。 在 Spring Security 中,注册选项的请求通常是使用 JavaScript 完成的,如下所示:spring-doc.cn

Spring Security 提供了一个默认注册页面,可用作有关如何注册凭据的参考。spring-doc.cn

申请注册选项
POST /webauthn/register/options
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

上述请求将获取当前经过身份验证的用户的注册选项。 由于质询是持久的(状态已更改)以便在注册时进行比较,因此请求必须是 POST 并包含 CSRF 令牌。spring-doc.cn

注册选项的响应
{
  "rp": {
    "name": "SimpleWebAuthn Example",
    "id": "example.localhost"
  },
  "user": {
    "name": "[email protected]",
    "id": "oWJtkJ6vJ_m5b84LB4_K7QKTCTEwLIjCh4tFMCGHO4w",
    "displayName": "[email protected]"
  },
  "challenge": "q7lCdd3SVQxdC-v8pnRAGEn1B2M-t7ZECWPwCAmhWvc",
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -8
    },
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 300000,
  "excludeCredentials": [],
  "authenticatorSelection": {
    "residentKey": "required",
    "userVerification": "preferred"
  },
  "attestation": "direct",
  "extensions": {
    "credProps": true
  }
}

注册凭证

获取注册选项后,它们将用于创建已注册的凭证。 要注册新凭证,应用程序应在 base64url 解码二进制值(如 、 和 )后将选项传递给 navigator.credentials.createuser.idchallengeexcludeCredentials[].idspring-doc.cn

然后,可以将返回的值作为 JSON 请求发送到服务器。 注册请求示例如下:spring-doc.cn

示例注册请求
POST /webauthn/register
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

{
  "publicKey": { (1)
    "credential": {
      "id": "dYF7EGnRFFIXkpXi9XU2wg",
      "rawId": "dYF7EGnRFFIXkpXi9XU2wg",
      "response": {
        "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEHWBexBp0RRSF5KV4vV1NsKlAQIDJiABIVggQjmrekPGzyqtoKK9HPUH-8Z2FLpoqkklFpFPQVICQ3IiWCD6I9Jvmor685fOZOyGXqUd87tXfvJk8rxj9OhuZvUALA",
        "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSl9RTi10SFJYRWVKYjlNcUNrWmFPLUdOVmlibXpGVGVWMk43Z0ptQUdrQSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
        "transports": [
          "internal",
          "hybrid"
        ]
      },
      "type": "public-key",
      "clientExtensionResults": {},
      "authenticatorAttachment": "platform"
    },
    "label": "1password" (2)
  }
}
1 使用二进制值 base64url 编码的调用结果。navigator.credentials.create
2 用户选择与此凭证关联的标签,以帮助用户区分凭证。
成功注册响应示例
HTTP/1.1 200 OK

{
  "success": true
}

验证身份验证断言

注册新凭证后,可以验证(authenticated)密钥。spring-doc.cn

验证凭证由两个步骤组成:spring-doc.cn

  1. 请求验证选项spring-doc.cn

  2. 验证凭证spring-doc.cn

请求验证选项

验证凭据的第一步是请求验证选项。 在 Spring Security 中,验证选项的请求通常使用 JavaScript 完成,如下所示:spring-doc.cn

Spring Security 提供了一个默认的登录页面,可用作如何验证凭据的参考。spring-doc.cn

验证选项请求
POST /webauthn/authenticate/options
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

上述请求将获取验证选项。 由于质询是持久的(状态已更改)以在身份验证时进行比较,因此请求必须是 POST 并包含 CSRF 令牌。spring-doc.cn

响应将包含用于获取具有二进制值(如 base64url 编码)的凭证的选项。challengespring-doc.cn

验证选项的响应示例
{
  "challenge": "cQfdGrj9zDg3zNBkOH3WPL954FTOShVy0-CoNgSewNM",
  "timeout": 300000,
  "rpId": "example.localhost",
  "allowCredentials": [],
  "userVerification": "preferred",
  "extensions": {}
}

验证凭证

获取验证选项后,它们将用于获取凭证。 要获取凭证,应用程序应在 base64url 解码二进制值(如 .challengespring-doc.cn

然后,可以将返回的值 of 作为 JSON 请求发送到服务器。 二进制值(如 和 )必须采用 base64url 编码。 身份验证请求示例如下:navigator.credentials.getrawIdresponse.*spring-doc.cn

示例身份验证请求
POST /login/webauthn
X-CSRF-TOKEN: 4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

{
  "id": "dYF7EGnRFFIXkpXi9XU2wg",
  "rawId": "dYF7EGnRFFIXkpXi9XU2wg",
  "response": {
    "authenticatorData": "y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA",
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
    "signature": "MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z",
    "userHandle": "Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo"
  },
  "clientExtensionResults": {},
  "authenticatorAttachment": "platform"
}
成功的身份验证响应示例
HTTP/1.1 200 OK

{
  "redirectUrl": "/", (1)
  "authenticated": true (2)
}
1 要重定向到的 URL
2 指示用户已经过身份验证
身份验证失败响应示例
HTTP/1.1 401 OK