From 84679a5d64cfb8a4b66a4312b774839df86e036d Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Mon, 22 Jan 2018 22:21:15 -0500 Subject: [PATCH] Polish #4904 Support GrantedAuthoritiesMapper @Bean for oauth2Login --- config/spring-security-config.gradle | 1 - ...t.java => OAuth2LoginConfigurerTests.java} | 170 ++++++++---------- docs/manual/src/docs/asciidoc/index.adoc | 19 ++ 3 files changed, 97 insertions(+), 93 deletions(-) rename config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/{OAuth2LoginConfigurerTest.java => OAuth2LoginConfigurerTests.java} (79%) diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle index 0984a4cc82..b6c6661299 100644 --- a/config/spring-security-config.gradle +++ b/config/spring-security-config.gradle @@ -66,7 +66,6 @@ dependencies { testRuntime 'cglib:cglib-nodep' testRuntime 'org.hsqldb:hsqldb' - testRuntime 'com.fasterxml.jackson.core:jackson-databind' } test { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTest.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java similarity index 79% rename from config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTest.java rename to config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java index 024eb19ea2..203143fec1 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTest.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java @@ -65,8 +65,6 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import javax.servlet.ServletException; -import java.io.IOException; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -83,7 +81,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Kazuki Shimizu * @since 5.0.1 */ -public class OAuth2LoginConfigurerTest { +public class OAuth2LoginConfigurerTests { private static final ClientRegistration CLIENT_REGISTRATION = CommonOAuth2Provider.GOOGLE .getBuilder("google").clientId("clientId").clientSecret("clientSecret") @@ -93,8 +91,10 @@ public class OAuth2LoginConfigurerTest { @Autowired private FilterChainProxy springSecurityFilterChain; + @Autowired private AuthorizationRequestRepository authorizationRequestRepository; + @Autowired SecurityContextRepository securityContextRepository; @@ -120,23 +120,21 @@ public class OAuth2LoginConfigurerTest { } @Test - public void oauth2Login() throws IOException, ServletException { - + public void oauth2Login() throws Exception { // setup application context loadConfig(OAuth2LoginConfig.class); // setup authorization request OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(); - this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, - this.request, this.response); + this.authorizationRequestRepository.saveAuthorizationRequest( + authorizationRequest, this.request, this.response); // setup authentication parameters this.request.setParameter("code", "code123"); this.request.setParameter("state", authorizationRequest.getState()); // perform test - this.springSecurityFilterChain.doFilter(this.request, this.response, - this.filterChain); + this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); // assertions Authentication authentication = this.securityContextRepository @@ -148,24 +146,21 @@ public class OAuth2LoginConfigurerTest { } @Test - public void oauth2LoginCustomizeUsingConfigurerMethod() - throws IOException, ServletException { - + public void oauth2LoginCustomWithConfigurer() throws Exception { // setup application context - loadConfig(OAuth2LoginConfigCustomizeUsingConfigurerMethod.class); + loadConfig(OAuth2LoginConfigCustomWithConfigurer.class); // setup authorization request OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(); - this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, - this.request, this.response); + this.authorizationRequestRepository.saveAuthorizationRequest( + authorizationRequest, this.request, this.response); // setup authentication parameters this.request.setParameter("code", "code123"); this.request.setParameter("state", authorizationRequest.getState()); // perform test - this.springSecurityFilterChain.doFilter(this.request, this.response, - this.filterChain); + this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); // assertions Authentication authentication = this.securityContextRepository @@ -173,29 +168,25 @@ public class OAuth2LoginConfigurerTest { .getAuthentication(); assertThat(authentication.getAuthorities()).hasSize(2); assertThat(authentication.getAuthorities()).first().hasToString("ROLE_USER"); - assertThat(authentication.getAuthorities()).last() - .hasToString("ROLE_OAUTH2_USER"); + assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER"); } @Test - public void oauth2LoginCustomizeUsingAutoDetection() - throws IOException, ServletException { - + public void oauth2LoginCustomWithBeanRegistration() throws Exception { // setup application context - loadConfig(OAuth2LoginConfigCustomizeUsingAutoDetection.class); + loadConfig(OAuth2LoginConfigCustomWithBeanRegistration.class); // setup authorization request OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest(); - this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, - this.request, this.response); + this.authorizationRequestRepository.saveAuthorizationRequest( + authorizationRequest, this.request, this.response); // setup authentication parameters this.request.setParameter("code", "code123"); this.request.setParameter("state", authorizationRequest.getState()); // perform test - this.springSecurityFilterChain.doFilter(this.request, this.response, - this.filterChain); + this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); // assertions Authentication authentication = this.securityContextRepository @@ -203,30 +194,26 @@ public class OAuth2LoginConfigurerTest { .getAuthentication(); assertThat(authentication.getAuthorities()).hasSize(2); assertThat(authentication.getAuthorities()).first().hasToString("ROLE_USER"); - assertThat(authentication.getAuthorities()).last() - .hasToString("ROLE_OAUTH2_USER"); + assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER"); } @Test - public void oidcLogin() throws IOException, ServletException { - + public void oidcLogin() throws Exception { // setup application context loadConfig(OAuth2LoginConfig.class); registerJwtDecoder(); // setup authorization request - OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest( - "openid"); - this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, - this.request, this.response); + OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest("openid"); + this.authorizationRequestRepository.saveAuthorizationRequest( + authorizationRequest, this.request, this.response); // setup authentication parameters this.request.setParameter("code", "code123"); this.request.setParameter("state", authorizationRequest.getState()); // perform test - this.springSecurityFilterChain.doFilter(this.request, this.response, - this.filterChain); + this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); // assertions Authentication authentication = this.securityContextRepository @@ -238,26 +225,22 @@ public class OAuth2LoginConfigurerTest { } @Test - public void oidcLoginCustomizeUsingConfigurerMethod() - throws IOException, ServletException { - + public void oidcLoginCustomWithConfigurer() throws Exception { // setup application context - loadConfig(OAuth2LoginConfigCustomizeUsingConfigurerMethod.class); + loadConfig(OAuth2LoginConfigCustomWithConfigurer.class); registerJwtDecoder(); // setup authorization request - OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest( - "openid"); - this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, - this.request, this.response); + OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest("openid"); + this.authorizationRequestRepository.saveAuthorizationRequest( + authorizationRequest, this.request, this.response); // setup authentication parameters this.request.setParameter("code", "code123"); this.request.setParameter("state", authorizationRequest.getState()); // perform test - this.springSecurityFilterChain.doFilter(this.request, this.response, - this.filterChain); + this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); // assertions Authentication authentication = this.securityContextRepository @@ -269,26 +252,22 @@ public class OAuth2LoginConfigurerTest { } @Test - public void oidcLoginCustomizeUsingAutoDetection() - throws IOException, ServletException { - + public void oidcLoginCustomWithBeanRegistration() throws Exception { // setup application context - loadConfig(OAuth2LoginConfigCustomizeUsingAutoDetection.class); + loadConfig(OAuth2LoginConfigCustomWithBeanRegistration.class); registerJwtDecoder(); // setup authorization request - OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest( - "openid"); - this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, - this.request, this.response); + OAuth2AuthorizationRequest authorizationRequest = createOAuth2AuthorizationRequest("openid"); + this.authorizationRequestRepository.saveAuthorizationRequest( + authorizationRequest, this.request, this.response); // setup authentication parameters this.request.setParameter("code", "code123"); this.request.setParameter("state", authorizationRequest.getState()); // perform test - this.springSecurityFilterChain.doFilter(this.request, this.response, - this.filterChain); + this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); // assertions Authentication authentication = this.securityContextRepository @@ -318,56 +297,60 @@ public class OAuth2LoginConfigurerTest { Collections.singletonMap("header1", "value1"), claims); }; this.springSecurityFilterChain.getFilters("/login/oauth2/code/google").stream() - .filter(OAuth2LoginAuthenticationFilter.class::isInstance).findFirst() + .filter(OAuth2LoginAuthenticationFilter.class::isInstance) + .findFirst() .ifPresent(filter -> PropertyAccessorFactory.forDirectFieldAccess(filter) - .setPropertyValue( - "authenticationManager.providers[2].jwtDecoders['google']", - decoder)); + .setPropertyValue( + "authenticationManager.providers[2].jwtDecoders['google']", + decoder)); } - private OAuth2AuthorizationRequest createOAuth2AuthorizationRequest( - String... scopes) { + private OAuth2AuthorizationRequest createOAuth2AuthorizationRequest(String... scopes) { return OAuth2AuthorizationRequest.authorizationCode() - .authorizationUri( - CLIENT_REGISTRATION.getProviderDetails().getAuthorizationUri()) - .clientId(CLIENT_REGISTRATION.getClientId()).state("state123") + .authorizationUri(CLIENT_REGISTRATION.getProviderDetails().getAuthorizationUri()) + .clientId(CLIENT_REGISTRATION.getClientId()) + .state("state123") .redirectUri("http://localhost") .additionalParameters( - Collections.singletonMap(OAuth2ParameterNames.REGISTRATION_ID, - CLIENT_REGISTRATION.getRegistrationId())) - .scope(scopes).build(); + Collections.singletonMap( + OAuth2ParameterNames.REGISTRATION_ID, + CLIENT_REGISTRATION.getRegistrationId())) + .scope(scopes) + .build(); } @EnableWebSecurity static class OAuth2LoginConfig extends CommonWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - http.oauth2Login().clientRegistrationRepository( - new InMemoryClientRegistrationRepository(CLIENT_REGISTRATION)); + http + .oauth2Login() + .clientRegistrationRepository( + new InMemoryClientRegistrationRepository(CLIENT_REGISTRATION)); super.configure(http); } } @EnableWebSecurity - static class OAuth2LoginConfigCustomizeUsingConfigurerMethod - extends CommonWebSecurityConfigurerAdapter { + static class OAuth2LoginConfigCustomWithConfigurer extends CommonWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - http.oauth2Login() + http + .oauth2Login() .clientRegistrationRepository( new InMemoryClientRegistrationRepository(CLIENT_REGISTRATION)) .userInfoEndpoint() - .userAuthoritiesMapper(createGrantedAuthoritiesMapper()); + .userAuthoritiesMapper(createGrantedAuthoritiesMapper()); super.configure(http); } } @EnableWebSecurity - static class OAuth2LoginConfigCustomizeUsingAutoDetection - extends CommonWebSecurityConfigurerAdapter { + static class OAuth2LoginConfigCustomWithBeanRegistration extends CommonWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - http.oauth2Login(); + http + .oauth2Login(); super.configure(http); } @@ -382,15 +365,20 @@ public class OAuth2LoginConfigurerTest { } } - private static abstract class CommonWebSecurityConfigurerAdapter - extends WebSecurityConfigurerAdapter { + private static abstract class CommonWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - http.securityContext().securityContextRepository(securityContextRepository()) - .and().oauth2Login().tokenEndpoint() - .accessTokenResponseClient(createOauth2AccessTokenResponseClient()) - .and().userInfoEndpoint().userService(createOauth2UserService()) - .oidcUserService(createOidcUserService()); + http + .securityContext() + .securityContextRepository(securityContextRepository()) + .and() + .oauth2Login() + .tokenEndpoint() + .accessTokenResponseClient(createOauth2AccessTokenResponseClient()) + .and() + .userInfoEndpoint() + .userService(createOauth2UserService()) + .oidcUserService(createOidcUserService()); } @Bean @@ -407,13 +395,13 @@ public class OAuth2LoginConfigurerTest { private static OAuth2AccessTokenResponseClient createOauth2AccessTokenResponseClient() { return request -> { Map additionalParameters = new HashMap<>(); - if (request.getAuthorizationExchange().getAuthorizationRequest().getScopes() - .contains("openid")) { + if (request.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains("openid")) { additionalParameters.put(OidcParameterNames.ID_TOKEN, "token123"); } return OAuth2AccessTokenResponse.withToken("accessToken123") - .tokenType(OAuth2AccessToken.TokenType.BEARER) - .additionalParameters(additionalParameters).build(); + .tokenType(OAuth2AccessToken.TokenType.BEARER) + .additionalParameters(additionalParameters) + .build(); }; } @@ -426,8 +414,7 @@ public class OAuth2LoginConfigurerTest { private static OAuth2UserService createOidcUserService() { OidcIdToken idToken = new OidcIdToken("token123", Instant.now(), - Instant.now().plusSeconds(3600), - Collections.singletonMap(IdTokenClaimNames.SUB, "sub123")); + Instant.now().plusSeconds(3600), Collections.singletonMap(IdTokenClaimNames.SUB, "sub123")); return request -> new DefaultOidcUser( Collections.singleton(new OidcUserAuthority(idToken)), idToken); } @@ -442,5 +429,4 @@ public class OAuth2LoginConfigurerTest { return mappedAuthorities; }; } - } diff --git a/docs/manual/src/docs/asciidoc/index.adoc b/docs/manual/src/docs/asciidoc/index.adoc index cbd65ee912..986db42460 100644 --- a/docs/manual/src/docs/asciidoc/index.adoc +++ b/docs/manual/src/docs/asciidoc/index.adoc @@ -6912,6 +6912,25 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { } ---- +Alternatively, you may register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.oauth2Login(); + } + + @Bean + public GrantedAuthoritiesMapper userAuthoritiesMapper() { + ... + } +} +---- + [[oauth2login-advanced-map-authorities-oauth2userservice]] ===== Delegation-based strategy with `OAuth2UserService`