Add OAuth PrincipalExtractor strategy interface
Update `UserInfoTokenServices` to use a PrincipalExtractor interface to extract the principal. Fixes gh-5186
This commit is contained in:
parent
7f45485e61
commit
f3e630933a
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.resource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link PrincipalExtractor}. Extracts the principal from the
|
||||
* map with well known keys.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class FixedPrincipalExtractor implements PrincipalExtractor {
|
||||
|
||||
private static final String[] PRINCIPAL_KEYS = new String[] { "user", "username",
|
||||
"userid", "user_id", "login", "id", "name" };
|
||||
|
||||
@Override
|
||||
public Object extractPrincipal(Map<String, Object> map) {
|
||||
for (String key : PRINCIPAL_KEYS) {
|
||||
if (map.containsKey(key)) {
|
||||
return map.get(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.resource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Strategy used by {@link UserInfoTokenServices} to extract the principal from the
|
||||
* resource server's response.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public interface PrincipalExtractor {
|
||||
|
||||
/**
|
||||
* Extract the principal that should be used for the token.
|
||||
* @param map the source map
|
||||
* @return the extracted principal or {@code null}
|
||||
*/
|
||||
Object extractPrincipal(Map<String, Object> map);
|
||||
|
||||
}
|
||||
|
|
@ -129,14 +129,18 @@ public class ResourceServerTokenServicesConfiguration {
|
|||
|
||||
private final AuthoritiesExtractor authoritiesExtractor;
|
||||
|
||||
private final PrincipalExtractor principalExtractor;
|
||||
|
||||
public SocialTokenServicesConfiguration(ResourceServerProperties sso,
|
||||
ObjectProvider<OAuth2ConnectionFactory<?>> connectionFactoryProvider,
|
||||
UserInfoRestTemplateFactory restTemplateFactory,
|
||||
ObjectProvider<AuthoritiesExtractor> authoritiesExtractorProvider) {
|
||||
ObjectProvider<AuthoritiesExtractor> authoritiesExtractor,
|
||||
ObjectProvider<PrincipalExtractor> principalExtractor) {
|
||||
this.sso = sso;
|
||||
this.connectionFactory = connectionFactoryProvider.getIfAvailable();
|
||||
this.restTemplate = restTemplateFactory.getUserInfoRestTemplate();
|
||||
this.authoritiesExtractor = authoritiesExtractorProvider.getIfAvailable();
|
||||
this.authoritiesExtractor = authoritiesExtractor.getIfAvailable();
|
||||
this.principalExtractor = principalExtractor.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -158,6 +162,9 @@ public class ResourceServerTokenServicesConfiguration {
|
|||
if (this.authoritiesExtractor != null) {
|
||||
services.setAuthoritiesExtractor(this.authoritiesExtractor);
|
||||
}
|
||||
if (this.principalExtractor != null) {
|
||||
services.setPrincipalExtractor(this.principalExtractor);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
@ -174,12 +181,16 @@ public class ResourceServerTokenServicesConfiguration {
|
|||
|
||||
private final AuthoritiesExtractor authoritiesExtractor;
|
||||
|
||||
private final PrincipalExtractor principalExtractor;
|
||||
|
||||
public UserInfoTokenServicesConfiguration(ResourceServerProperties sso,
|
||||
UserInfoRestTemplateFactory restTemplateFactory,
|
||||
ObjectProvider<AuthoritiesExtractor> authoritiesExtractorProvider) {
|
||||
ObjectProvider<AuthoritiesExtractor> authoritiesExtractor,
|
||||
ObjectProvider<PrincipalExtractor> principalExtractor) {
|
||||
this.sso = sso;
|
||||
this.restTemplate = restTemplateFactory.getUserInfoRestTemplate();
|
||||
this.authoritiesExtractor = authoritiesExtractorProvider.getIfAvailable();
|
||||
this.authoritiesExtractor = authoritiesExtractor.getIfAvailable();
|
||||
this.principalExtractor = principalExtractor.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -192,6 +203,9 @@ public class ResourceServerTokenServicesConfiguration {
|
|||
if (this.authoritiesExtractor != null) {
|
||||
services.setAuthoritiesExtractor(this.authoritiesExtractor);
|
||||
}
|
||||
if (this.principalExtractor != null) {
|
||||
services.setPrincipalExtractor(this.principalExtractor);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import org.springframework.security.oauth2.common.exceptions.InvalidTokenExcepti
|
|||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Request;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ResourceServerTokenServices} that uses a user info REST service.
|
||||
|
|
@ -46,9 +47,6 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
|
|||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private static final String[] PRINCIPAL_KEYS = new String[] { "user", "username",
|
||||
"userid", "user_id", "login", "id", "name" };
|
||||
|
||||
private final String userInfoEndpointUrl;
|
||||
|
||||
private final String clientId;
|
||||
|
|
@ -59,6 +57,8 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
|
|||
|
||||
private AuthoritiesExtractor authoritiesExtractor = new FixedAuthoritiesExtractor();
|
||||
|
||||
private PrincipalExtractor principalExtractor = new FixedPrincipalExtractor();
|
||||
|
||||
public UserInfoTokenServices(String userInfoEndpointUrl, String clientId) {
|
||||
this.userInfoEndpointUrl = userInfoEndpointUrl;
|
||||
this.clientId = clientId;
|
||||
|
|
@ -73,9 +73,15 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
|
|||
}
|
||||
|
||||
public void setAuthoritiesExtractor(AuthoritiesExtractor authoritiesExtractor) {
|
||||
Assert.notNull(authoritiesExtractor, "AuthoritiesExtractor must not be null");
|
||||
this.authoritiesExtractor = authoritiesExtractor;
|
||||
}
|
||||
|
||||
public void setPrincipalExtractor(PrincipalExtractor principalExtractor) {
|
||||
Assert.notNull(principalExtractor, "PrincipalExtractor must not be null");
|
||||
this.principalExtractor = principalExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2Authentication loadAuthentication(String accessToken)
|
||||
throws AuthenticationException, InvalidTokenException {
|
||||
|
|
@ -101,17 +107,13 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
|
|||
|
||||
/**
|
||||
* Return the principal that should be used for the token. The default implementation
|
||||
* looks for well know {@code user*} keys in the map.
|
||||
* delegates to the {@link PrincipalExtractor}.
|
||||
* @param map the source map
|
||||
* @return the principal or {@literal "unknown"}
|
||||
*/
|
||||
protected Object getPrincipal(Map<String, Object> map) {
|
||||
for (String key : PRINCIPAL_KEYS) {
|
||||
if (map.containsKey(key)) {
|
||||
return map.get(key);
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
Object principal = this.principalExtractor.extractPrincipal(map);
|
||||
return (principal == null ? "unknown" : principal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -125,6 +125,19 @@ public class ResourceServerTokenServicesConfigurationTests {
|
|||
.containsExactly(this.context.getBean(AuthoritiesExtractor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userInfoWithPrincipal() {
|
||||
EnvironmentTestUtils.addEnvironment(this.environment,
|
||||
"security.oauth2.resource.userInfoUri:http://example.com");
|
||||
this.context = new SpringApplicationBuilder(PrincipalConfiguration.class)
|
||||
.environment(this.environment).web(false).run();
|
||||
UserInfoTokenServices services = this.context
|
||||
.getBean(UserInfoTokenServices.class);
|
||||
assertThat(services).isNotNull();
|
||||
assertThat(services).extracting("principalExtractor")
|
||||
.containsExactly(this.context.getBean(PrincipalExtractor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userInfoWithClient() {
|
||||
EnvironmentTestUtils.addEnvironment(this.environment,
|
||||
|
|
@ -228,6 +241,23 @@ public class ResourceServerTokenServicesConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class PrincipalConfiguration extends ResourceConfiguration {
|
||||
|
||||
@Bean
|
||||
PrincipalExtractor authoritiesExtractor() {
|
||||
return new PrincipalExtractor() {
|
||||
|
||||
@Override
|
||||
public Object extractPrincipal(Map<String, Object> map) {
|
||||
return "boot";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Import({ OAuth2RestOperationsConfiguration.class })
|
||||
protected static class ResourceNoClientConfiguration extends ResourceConfiguration {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue