Polish OAuth2AuthorizedClientService

Fixes gh-4746
This commit is contained in:
Joe Grandja 2017-10-29 20:25:03 -04:00
parent b496ad4d86
commit c3d2effc1d
10 changed files with 36 additions and 183 deletions

View File

@ -20,12 +20,11 @@ import org.springframework.core.ResolvableType;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.AuthorizationRequestUriBuilder;
import org.springframework.security.oauth2.client.endpoint.NimbusAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.jwt.JwtDecoderRegistry;
import org.springframework.security.oauth2.client.jwt.NimbusJwtDecoderRegistry;
@ -79,7 +78,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
return this;
}
public OAuth2LoginConfigurer<B> authorizedClientService(OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService) {
public OAuth2LoginConfigurer<B> authorizedClientService(OAuth2AuthorizedClientService authorizedClientService) {
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
this.getBuilder().setSharedObject(OAuth2AuthorizedClientService.class, authorizedClientService);
return this;
@ -318,8 +317,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
return this.getBuilder().getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
}
private OAuth2AuthorizedClientService<OAuth2AuthorizedClient> getAuthorizedClientService() {
OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService =
private OAuth2AuthorizedClientService getAuthorizedClientService() {
OAuth2AuthorizedClientService authorizedClientService =
this.getBuilder().getSharedObject(OAuth2AuthorizedClientService.class);
if (authorizedClientService == null) {
authorizedClientService = this.getAuthorizedClientServiceBean();
@ -328,7 +327,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
return authorizedClientService;
}
private OAuth2AuthorizedClientService<OAuth2AuthorizedClient> getAuthorizedClientServiceBean() {
private OAuth2AuthorizedClientService getAuthorizedClientServiceBean() {
return this.getBuilder().getSharedObject(ApplicationContext.class).getBean(OAuth2AuthorizedClientService.class);
}

View File

@ -16,7 +16,6 @@
package org.springframework.security.oauth2.client;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.oidc.OidcAuthorizedClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.util.Assert;
@ -33,14 +32,11 @@ import java.util.concurrent.ConcurrentHashMap;
* @since 5.0
* @see OAuth2AuthorizedClientService
* @see OAuth2AuthorizedClient
* @see OidcAuthorizedClient
* @see ClientRegistration
* @see Authentication
*
* @param <T> The type of <i>OAuth 2.0 Authorized Client</i>
*/
public final class InMemoryOAuth2AuthorizedClientService<T extends OAuth2AuthorizedClient> implements OAuth2AuthorizedClientService<T> {
private final Map<String, T> authorizedClients = new ConcurrentHashMap<>();
public final class InMemoryOAuth2AuthorizedClientService implements OAuth2AuthorizedClientService {
private final Map<String, OAuth2AuthorizedClient> authorizedClients = new ConcurrentHashMap<>();
private final ClientRegistrationRepository clientRegistrationRepository;
public InMemoryOAuth2AuthorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {
@ -49,37 +45,36 @@ public final class InMemoryOAuth2AuthorizedClientService<T extends OAuth2Authori
}
@Override
public T loadAuthorizedClient(String clientRegistrationId, Authentication principal) {
public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId, String principalName) {
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
Assert.notNull(principal, "principal cannot be null");
Assert.hasText(principalName, "principalName cannot be empty");
ClientRegistration registration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);
if (registration == null) {
return null;
}
return this.authorizedClients.get(this.getIdentifier(registration, principal));
return (T) this.authorizedClients.get(this.getIdentifier(registration, principalName));
}
@Override
public void saveAuthorizedClient(T authorizedClient, Authentication principal) {
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
Assert.notNull(principal, "principal cannot be null");
this.authorizedClients.put(this.getIdentifier(
authorizedClient.getClientRegistration(), principal), authorizedClient);
authorizedClient.getClientRegistration(), principal.getName()), authorizedClient);
}
@Override
public T removeAuthorizedClient(String clientRegistrationId, Authentication principal) {
public void removeAuthorizedClient(String clientRegistrationId, String principalName) {
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
Assert.notNull(principal, "principal cannot be null");
Assert.hasText(principalName, "principalName cannot be empty");
ClientRegistration registration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);
if (registration == null) {
return null;
if (registration != null) {
this.authorizedClients.remove(this.getIdentifier(registration, principalName));
}
return this.authorizedClients.remove(this.getIdentifier(registration, principal));
}
private String getIdentifier(ClientRegistration registration, Authentication principal) {
String identifier = "[" + registration.getRegistrationId() + "][" + principal.getName() + "]";
private String getIdentifier(ClientRegistration registration, String principalName) {
String identifier = "[" + registration.getRegistrationId() + "][" + principalName + "]";
return Base64.getEncoder().encodeToString(identifier.getBytes());
}
}

View File

@ -16,8 +16,8 @@
package org.springframework.security.oauth2.client;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.oidc.OidcAuthorizedClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
/**
* Implementations of this interface are responsible for the management
@ -30,18 +30,16 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
* @author Joe Grandja
* @since 5.0
* @see OAuth2AuthorizedClient
* @see OidcAuthorizedClient
* @see ClientRegistration
* @see Authentication
*
* @param <T> The type of <i>OAuth 2.0 Authorized Client</i>
* @see OAuth2AccessToken
*/
public interface OAuth2AuthorizedClientService<T extends OAuth2AuthorizedClient> {
public interface OAuth2AuthorizedClientService {
T loadAuthorizedClient(String clientRegistrationId, Authentication principal);
<T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId, String principalName);
void saveAuthorizedClient(T authorizedClient, Authentication principal);
void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal);
T removeAuthorizedClient(String clientRegistrationId, Authentication principal);
void removeAuthorizedClient(String clientRegistrationId, String principalName);
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2002-2017 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.security.oauth2.client.oidc;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.util.Assert;
/**
* A representation of an OpenID Connect 1.0 <i>&quot;Authorized Client&quot;</i>.
* <p>
* A client is considered <i>&quot;authorized&quot;</i> when the End-User (Resource Owner)
* grants authorization to the Client to access its protected resources.
* <p>
* This class associates the {@link #getClientRegistration() Client}
* to the {@link #getAccessToken() Access Token}
* granted/authorized by the {@link #getPrincipalName() Resource Owner}, along with
* the {@link #getIdToken() ID Token} which contains Claims about the authentication of the End-User.
*
* @author Joe Grandja
* @since 5.0
* @see OAuth2AuthorizedClient
* @see OidcIdToken
*/
public class OidcAuthorizedClient extends OAuth2AuthorizedClient {
private final OidcIdToken idToken;
public OidcAuthorizedClient(ClientRegistration clientRegistration, String principalName,
OAuth2AccessToken accessToken, OidcIdToken idToken) {
super(clientRegistration, principalName, accessToken);
Assert.notNull(idToken, "idToken cannot be null");
this.idToken = idToken;
}
public OidcIdToken getIdToken() {
return this.idToken;
}
}

View File

@ -62,7 +62,7 @@ import java.util.List;
*
* @author Joe Grandja
* @since 5.0
* @see OidcAuthorizationCodeAuthenticationToken
* @see OAuth2LoginAuthenticationToken
* @see OAuth2AccessTokenResponseClient
* @see OidcUserService
* @see OidcUser
@ -158,13 +158,12 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
Collection<? extends GrantedAuthority> mappedAuthorities =
this.authoritiesMapper.mapAuthorities(oidcUser.getAuthorities());
OidcAuthorizationCodeAuthenticationToken authenticationResult = new OidcAuthorizationCodeAuthenticationToken(
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
authorizationCodeAuthentication.getClientRegistration(),
authorizationCodeAuthentication.getAuthorizationExchange(),
oidcUser,
mappedAuthorities,
accessToken,
idToken);
accessToken);
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
return authenticationResult;

View File

@ -1,81 +0,0 @@
/*
* Copyright 2012-2017 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.security.oauth2.client.oidc.authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.util.Assert;
import java.util.Collection;
/**
* An {@link OAuth2LoginAuthenticationToken} for <i>OpenID Connect 1.0 Authentication</i>,
* which leverages the <i>Authorization Code Flow</i>.
*
* @author Joe Grandja
* @since 5.0
* @see OAuth2LoginAuthenticationToken
* @see OidcIdToken
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth">3.1 Authorization Code Flow</a>
*/
public class OidcAuthorizationCodeAuthenticationToken extends OAuth2LoginAuthenticationToken {
private OidcIdToken idToken;
/**
* This constructor should be used when the Authentication Request/Response is complete.
*
* @param clientRegistration
* @param authorizationExchange
*/
public OidcAuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
OAuth2AuthorizationExchange authorizationExchange) {
super(clientRegistration, authorizationExchange);
}
/**
* This constructor should be used when the Token Request/Response is complete,
* which indicates that the Authorization Code Flow has fully completed
* and OpenID Connect 1.0 Authentication has been achieved.
*
* @param clientRegistration
* @param authorizationExchange
* @param principal
* @param authorities
* @param accessToken
* @param idToken
*/
public OidcAuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
OAuth2AuthorizationExchange authorizationExchange,
OidcUser principal,
Collection<? extends GrantedAuthority> authorities,
OAuth2AccessToken accessToken,
OidcIdToken idToken) {
super(clientRegistration, authorizationExchange, principal, authorities, accessToken);
Assert.notNull(idToken, "idToken cannot be null");
this.idToken = idToken;
}
public OidcIdToken getIdToken() {
return this.idToken;
}
}

View File

@ -81,18 +81,18 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";
private static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";
private ClientRegistrationRepository clientRegistrationRepository;
private OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService;
private OAuth2AuthorizedClientService authorizedClientService;
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =
new HttpSessionOAuth2AuthorizationRequestRepository();
public OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService) {
OAuth2AuthorizedClientService authorizedClientService) {
this(DEFAULT_FILTER_PROCESSES_URI, clientRegistrationRepository, authorizedClientService);
}
public OAuth2LoginAuthenticationFilter(String filterProcessesUrl,
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService) {
OAuth2AuthorizedClientService authorizedClientService) {
super(filterProcessesUrl);
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
@ -137,8 +137,7 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
oauth2Authentication.getName(),
authenticationResult.getAccessToken());
this.authorizedClientService.saveAuthorizedClient(
authorizedClient, oauth2Authentication);
this.authorizedClientService.saveAuthorizedClient(authorizedClient, oauth2Authentication);
return oauth2Authentication;
}

View File

@ -38,7 +38,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
@ -397,8 +396,8 @@ public class OAuth2LoginApplicationTests {
private ClientRegistrationRepository clientRegistrationRepository;
@Bean
public OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService() {
return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository);
public OAuth2AuthorizedClientService authorizedClientService() {
return new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository);
}
}
}

View File

@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
@ -33,7 +32,7 @@ public class OAuth2LoginConfig {
private ClientRegistrationRepository clientRegistrationRepository;
@Bean
public OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService() {
return new InMemoryOAuth2AuthorizedClientService<>(this.clientRegistrationRepository);
public OAuth2AuthorizedClientService authorizedClientService() {
return new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository);
}
}

View File

@ -71,7 +71,7 @@ public class MainController {
private OAuth2AuthorizedClient getAuthorizedClient(OAuth2AuthenticationToken authentication) {
return this.authorizedClientService.loadAuthorizedClient(
authentication.getAuthorizedClientRegistrationId(), authentication);
authentication.getAuthorizedClientRegistrationId(), authentication.getName());
}
private ExchangeFilterFunction oauth2Credentials(OAuth2AuthorizedClient authorizedClient) {