mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-22 20:12:14 +00:00
Set PublicKeyCredentialCreationOptionsRepository by DSL or Bean
Closes gh-16369 Signed-off-by: DingHao <dh.hiekn@gmail.com>
This commit is contained in:
parent
4dc1dcbf24
commit
f4491f388e
@ -44,6 +44,7 @@ import org.springframework.security.web.webauthn.management.WebAuthnRelyingParty
|
|||||||
import org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations;
|
import org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations;
|
||||||
import org.springframework.security.web.webauthn.registration.DefaultWebAuthnRegistrationPageGeneratingFilter;
|
import org.springframework.security.web.webauthn.registration.DefaultWebAuthnRegistrationPageGeneratingFilter;
|
||||||
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
|
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
|
||||||
|
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository;
|
||||||
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
|
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,6 +65,8 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
|
|
||||||
private boolean disableDefaultRegistrationPage = false;
|
private boolean disableDefaultRegistrationPage = false;
|
||||||
|
|
||||||
|
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
|
||||||
|
|
||||||
private HttpMessageConverter<Object> converter;
|
private HttpMessageConverter<Object> converter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,6 +133,17 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets PublicKeyCredentialCreationOptionsRepository
|
||||||
|
* @param creationOptionsRepository the creationOptionsRepository
|
||||||
|
* @return the {@link WebAuthnConfigurer} for further customization
|
||||||
|
*/
|
||||||
|
public WebAuthnConfigurer<H> creationOptionsRepository(
|
||||||
|
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
|
||||||
|
this.creationOptionsRepository = creationOptionsRepository;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(H http) throws Exception {
|
public void configure(H http) throws Exception {
|
||||||
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class).orElseGet(() -> {
|
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class).orElseGet(() -> {
|
||||||
@ -141,6 +155,7 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
UserCredentialRepository userCredentials = getSharedOrBean(http, UserCredentialRepository.class)
|
UserCredentialRepository userCredentials = getSharedOrBean(http, UserCredentialRepository.class)
|
||||||
.orElse(userCredentialRepository());
|
.orElse(userCredentialRepository());
|
||||||
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
|
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
|
||||||
|
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();
|
||||||
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
|
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
|
||||||
webAuthnAuthnFilter.setAuthenticationManager(
|
webAuthnAuthnFilter.setAuthenticationManager(
|
||||||
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
|
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
|
||||||
@ -148,6 +163,10 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
rpOperations);
|
rpOperations);
|
||||||
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
||||||
rpOperations);
|
rpOperations);
|
||||||
|
if (creationOptionsRepository != null) {
|
||||||
|
webAuthnRegistrationFilter.setCreationOptionsRepository(creationOptionsRepository);
|
||||||
|
creationOptionsFilter.setCreationOptionsRepository(creationOptionsRepository);
|
||||||
|
}
|
||||||
if (this.converter != null) {
|
if (this.converter != null) {
|
||||||
webAuthnRegistrationFilter.setConverter(this.converter);
|
webAuthnRegistrationFilter.setConverter(this.converter);
|
||||||
creationOptionsFilter.setConverter(this.converter);
|
creationOptionsFilter.setConverter(this.converter);
|
||||||
@ -181,6 +200,14 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
|
||||||
|
if (this.creationOptionsRepository != null) {
|
||||||
|
return this.creationOptionsRepository;
|
||||||
|
}
|
||||||
|
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||||
|
return context.getBeanProvider(PublicKeyCredentialCreationOptionsRepository.class).getIfUnique();
|
||||||
|
}
|
||||||
|
|
||||||
private <C> Optional<C> getSharedOrBean(H http, Class<C> type) {
|
private <C> Optional<C> getSharedOrBean(H http, Class<C> type) {
|
||||||
C shared = http.getSharedObject(type);
|
C shared = http.getSharedObject(type);
|
||||||
return Optional.ofNullable(shared).or(() -> getBeanOrNull(type));
|
return Optional.ofNullable(shared).or(() -> getBeanOrNull(type));
|
||||||
|
@ -43,6 +43,7 @@ import org.springframework.security.web.authentication.ui.DefaultResourcesFilter
|
|||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||||
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
|
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
|
||||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||||
|
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
|||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,13 +143,53 @@ public class WebAuthnConfigurerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void webauthnWhenConfiguredMessageConverter() throws Exception {
|
public void webauthnWhenConfiguredPublicKeyCredentialCreationOptionsRepository() throws Exception {
|
||||||
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
SecurityContextHolder.setContext(new SecurityContextImpl(user));
|
SecurityContextHolder.setContext(new SecurityContextImpl(user));
|
||||||
PublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions
|
PublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions
|
||||||
.createPublicKeyCredentialCreationOptions()
|
.createPublicKeyCredentialCreationOptions()
|
||||||
.build();
|
.build();
|
||||||
WebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);
|
WebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);
|
||||||
|
ConfigCredentialCreationOptionsRepository.rpOperations = rpOperations;
|
||||||
|
given(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);
|
||||||
|
String attrName = "attrName";
|
||||||
|
HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();
|
||||||
|
creationOptionsRepository.setAttrName(attrName);
|
||||||
|
ConfigCredentialCreationOptionsRepository.creationOptionsRepository = creationOptionsRepository;
|
||||||
|
this.spring.register(ConfigCredentialCreationOptionsRepository.class).autowire();
|
||||||
|
this.mvc.perform(post("/webauthn/register/options"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(request().sessionAttribute(attrName, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void webauthnWhenConfiguredPublicKeyCredentialCreationOptionsRepositoryBeanPresent() throws Exception {
|
||||||
|
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
SecurityContextHolder.setContext(new SecurityContextImpl(user));
|
||||||
|
PublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions
|
||||||
|
.createPublicKeyCredentialCreationOptions()
|
||||||
|
.build();
|
||||||
|
WebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);
|
||||||
|
ConfigCredentialCreationOptionsRepositoryFromBean.rpOperations = rpOperations;
|
||||||
|
given(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);
|
||||||
|
String attrName = "attrName";
|
||||||
|
HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository = new HttpSessionPublicKeyCredentialCreationOptionsRepository();
|
||||||
|
creationOptionsRepository.setAttrName(attrName);
|
||||||
|
ConfigCredentialCreationOptionsRepositoryFromBean.creationOptionsRepository = creationOptionsRepository;
|
||||||
|
this.spring.register(ConfigCredentialCreationOptionsRepositoryFromBean.class).autowire();
|
||||||
|
this.mvc.perform(post("/webauthn/register/options"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(request().sessionAttribute(attrName, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void webauthnWhenConfiguredMessageConverter() throws Exception {
|
||||||
|
TestingAuthenticationToken user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
SecurityContextHolder.setContext(new SecurityContextImpl(user));
|
||||||
|
PublicKeyCredentialCreationOptions options = TestPublicKeyCredentialCreationOptions
|
||||||
|
.createPublicKeyCredentialCreationOptions()
|
||||||
|
.build();
|
||||||
|
WebAuthnRelyingPartyOperations rpOperations = mock(WebAuthnRelyingPartyOperations.class);
|
||||||
ConfigMessageConverter.rpOperations = rpOperations;
|
ConfigMessageConverter.rpOperations = rpOperations;
|
||||||
given(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);
|
given(rpOperations.createPublicKeyCredentialCreationOptions(any())).willReturn(options);
|
||||||
HttpMessageConverter<Object> converter = mock(HttpMessageConverter.class);
|
HttpMessageConverter<Object> converter = mock(HttpMessageConverter.class);
|
||||||
@ -161,8 +203,65 @@ public class WebAuthnConfigurerTests {
|
|||||||
ConfigMessageConverter.converter = converter;
|
ConfigMessageConverter.converter = converter;
|
||||||
this.spring.register(ConfigMessageConverter.class).autowire();
|
this.spring.register(ConfigMessageConverter.class).autowire();
|
||||||
this.mvc.perform(post("/webauthn/register/options"))
|
this.mvc.perform(post("/webauthn/register/options"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().string(expectedBody));
|
.andExpect(content().string(expectedBody));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class ConfigCredentialCreationOptionsRepository {
|
||||||
|
|
||||||
|
private static HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
|
||||||
|
|
||||||
|
private static WebAuthnRelyingPartyOperations rpOperations;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {
|
||||||
|
return ConfigCredentialCreationOptionsRepository.rpOperations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService userDetailsService() {
|
||||||
|
return new InMemoryUserDetailsManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
return http.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
.webAuthn((c) -> c.creationOptionsRepository(creationOptionsRepository))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class ConfigCredentialCreationOptionsRepositoryFromBean {
|
||||||
|
|
||||||
|
private static HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
|
||||||
|
|
||||||
|
private static WebAuthnRelyingPartyOperations rpOperations;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {
|
||||||
|
return ConfigCredentialCreationOptionsRepositoryFromBean.rpOperations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService userDetailsService() {
|
||||||
|
return new InMemoryUserDetailsManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
HttpSessionPublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
|
||||||
|
return ConfigCredentialCreationOptionsRepositoryFromBean.creationOptionsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
return http.csrf(AbstractHttpConfigurer::disable).webAuthn(Customizer.withDefaults()).build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -105,6 +105,17 @@ public class PublicKeyCredentialCreationOptionsFilter extends OncePerRequestFilt
|
|||||||
this.converter.write(options, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
|
this.converter.write(options, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link PublicKeyCredentialCreationOptionsRepository} to use. The default
|
||||||
|
* is {@link HttpSessionPublicKeyCredentialCreationOptionsRepository}.
|
||||||
|
* @param creationOptionsRepository the
|
||||||
|
* {@link PublicKeyCredentialCreationOptionsRepository} to use. Cannot be null.
|
||||||
|
*/
|
||||||
|
public void setCreationOptionsRepository(PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
|
||||||
|
Assert.notNull(creationOptionsRepository, "creationOptionsRepository cannot be null");
|
||||||
|
this.repository = creationOptionsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link HttpMessageConverter} to read the
|
* Set the {@link HttpMessageConverter} to read the
|
||||||
* {@link WebAuthnRegistrationFilter.WebAuthnRegistrationRequest} and write the
|
* {@link WebAuthnRegistrationFilter.WebAuthnRegistrationRequest} and write the
|
||||||
|
Loading…
x
Reference in New Issue
Block a user