
WebAuthn是无密码身份验证技术,解决了密码泄露的风险,主流的浏览器都支持。有很多开源的类库实现了WebAuthn规范,Java下流行的类库有:
Spring Security官方暂时未支持WebAuthn,可以用webauthn4j的webauthn4j-spring-security项目,它将webauthn4j和Spring Security打通,项目还处于开发阶段,设计上可能还会调整。但我们可以通过项目里的demo,很好的学习怎么实现WebAuthn。简单介绍一下webauthn4j-spring-security中例子的打开方式。
demo启动流程
克隆项目
git clone git@github.com:webauthn4j/webauthn4j-spring-security.git
打包前端资源
cd webauthn4j-spring-security/samples/lib/spa-angular-client
npm install
PS: 这里可能会碰到打包异常,是@angular/cdk包版本和其他模块版本冲突了,将@angular/cdk改成“13.3.9”就能正常打包
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: sample-angular-client@0.0.0
npm ERR! Found: @angular/common@13.3.12
npm ERR! node_modules/@angular/common
npm ERR! @angular/common@"^13.3.11" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/common@"^15.0.0 || ^16.0.0" from @angular/cdk@15.2.1
npm ERR! node_modules/@angular/cdk
npm ERR! @angular/cdk@"^15.1.2" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
启动项目
cd webauthn4j-spring-security
./gradlew build
./gradlew samples:spa:bootRun
查看登录页

HttpSecurity核心配置
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
// WebAuthn Login
http.apply(WebAuthnLoginConfigurer.webAuthnLogin())
.usernameParameter("username")
.passwordParameter("password")
.credentialIdParameter("credentialId")
.clientDataJSONParameter("clientDataJSON")
.authenticatorDataParameter("authenticatorData")
.signatureParameter("signature")
.clientExtensionsJSONParameter("clientExtensionsJSON")
.loginProcessingUrl("/login")
.attestationOptionsEndpoint()
.rp()
.name("WebAuthn4J Spring Security Sample")
.and()
.pubKeyCredParams(
new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.RS256), // Windows Hello
new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256) // FIDO U2F Key, etc
)
.extensions()
.credProps(true)
.and()
.assertionOptionsEndpoint()
.and()
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.and()
.authenticationManager(authenticationManager);
http.headers(headers -> {
// 'publickey-credentials-get *' allows getting WebAuthn credentials to all nested browsing contexts (iframes) regardless of their origin.
headers.permissionsPolicy(config -> config.policy("publickey-credentials-get *"));
// Disable "X-Frame-Options" to allow cross-origin iframe access
headers.frameOptions().disable();
});
// Logout
http.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler);
// Authorization
http.authorizeRequests()
.mvcMatchers("/").permitAll()
.mvcMatchers("/static/**").permitAll()
.mvcMatchers("/angular/**").permitAll()
.mvcMatchers("/webjars/**").permitAll()
.mvcMatchers("/favicon.ico").permitAll()
.mvcMatchers("/api/auth/status").permitAll()
.mvcMatchers(HttpMethod.GET, "/login").permitAll()
.mvcMatchers(HttpMethod.POST, "/api/profile").permitAll()
.mvcMatchers("/health/**").permitAll()
.mvcMatchers("/info/**").permitAll()
.mvcMatchers("/h2-console/**").denyAll()
.mvcMatchers("/api/admin/**").access("hasRole('ADMIN_ROLE') and isAuthenticated()")
.anyRequest().access("@webAuthnSecurityExpression.isWebAuthnAuthenticated(authentication) || hasAuthority('SINGLE_FACTOR_AUTHN_ALLOWED')");
http.sessionManagement()
.sessionAuthenticationFailureHandler(authenticationFailureHandler);
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
// As WebAuthn has its own CSRF protection mechanism (challenge), CSRF token is disabled here
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
http.csrf().ignoringAntMatchers("/webauthn/**");
return http.build();
}
这个方法将SpringSecurity需要的配置基本都说清楚了,除了#1、#2跟WebAuthn直接相关,其他几点都是SpringSecurity常规配置。
添加了自定义的WebAuthnLoginConfigurer描述登录页面的URL、字段名等信息
通过pubKeyCredParams描述服务器可接受的公钥类型的对象数组。
设置自定义的authenticationManager、successHandler、failureHandler等
设置http的header
设置authorizeRequests控制权限
设置session和exceptionHandling
设置Csrf配置
引用
体验WebAuthn登录: https://webauthn.io/