parent
f0b0cfc4fd
commit
16e69d06b4
|
@ -15,11 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
|
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
|
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.authentication.AuthorizationGrantTokenExchanger;
|
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
|
||||||
import org.springframework.security.oauth2.client.authentication.NimbusAuthorizationCodeTokenExchanger;
|
import org.springframework.security.oauth2.client.authentication.NimbusAuthorizationCodeTokenExchanger;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
|
||||||
|
@ -88,6 +92,12 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OAuth2LoginConfigurer<B> authorizedClientService(OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService) {
|
||||||
|
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
|
||||||
|
this.getBuilder().setSharedObject(OAuth2AuthorizedClientService.class, authorizedClientService);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2LoginConfigurer<B> loginPage(String loginPage) {
|
public OAuth2LoginConfigurer<B> loginPage(String loginPage) {
|
||||||
Assert.hasText(loginPage, "loginPage cannot be empty");
|
Assert.hasText(loginPage, "loginPage cannot be empty");
|
||||||
|
@ -299,6 +309,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||||
authorizationResponseFilter.setAuthorizationRequestRepository(
|
authorizationResponseFilter.setAuthorizationRequestRepository(
|
||||||
this.authorizationEndpointConfig.authorizationRequestRepository);
|
this.authorizationEndpointConfig.authorizationRequestRepository);
|
||||||
}
|
}
|
||||||
|
authorizationResponseFilter.setAuthorizedClientService(this.getAuthorizedClientService());
|
||||||
if (this.tokenEndpointConfig.accessTokenRepository != null) {
|
if (this.tokenEndpointConfig.accessTokenRepository != null) {
|
||||||
authorizationResponseFilter.setAccessTokenRepository(
|
authorizationResponseFilter.setAccessTokenRepository(
|
||||||
this.tokenEndpointConfig.accessTokenRepository);
|
this.tokenEndpointConfig.accessTokenRepository);
|
||||||
|
@ -324,6 +335,26 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
|
||||||
return this.getBuilder().getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
|
return this.getBuilder().getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private OAuth2AuthorizedClientService<OAuth2AuthorizedClient> getAuthorizedClientService() {
|
||||||
|
OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService = this.getBuilder().getSharedObject(OAuth2AuthorizedClientService.class);
|
||||||
|
if (authorizedClientService == null) {
|
||||||
|
authorizedClientService = this.getAuthorizedClientServiceBean();
|
||||||
|
if (authorizedClientService == null) {
|
||||||
|
authorizedClientService = new InMemoryOAuth2AuthorizedClientService<>(this.getClientRegistrationRepository());
|
||||||
|
}
|
||||||
|
this.getBuilder().setSharedObject(OAuth2AuthorizedClientService.class, authorizedClientService);
|
||||||
|
}
|
||||||
|
return authorizedClientService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OAuth2AuthorizedClientService<OAuth2AuthorizedClient> getAuthorizedClientServiceBean() {
|
||||||
|
Map<String, OAuth2AuthorizedClientService> authorizedClientServiceMap =
|
||||||
|
BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||||
|
this.getBuilder().getSharedObject(ApplicationContext.class),
|
||||||
|
OAuth2AuthorizedClientService.class);
|
||||||
|
return !authorizedClientServiceMap.isEmpty() ? authorizedClientServiceMap.values().iterator().next() : null;
|
||||||
|
}
|
||||||
|
|
||||||
private void initDefaultLoginFilter(B http) {
|
private void initDefaultLoginFilter(B http) {
|
||||||
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageGeneratingFilter.class);
|
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageGeneratingFilter.class);
|
||||||
if (loginPageGeneratingFilter == null || this.isCustomLoginPage()) {
|
if (loginPageGeneratingFilter == null || this.isCustomLoginPage()) {
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link OAuth2AuthorizedClientService} that stores
|
||||||
|
* {@link OAuth2AuthorizedClient Authorized Client(s)} <i>in-memory</i>.
|
||||||
|
*
|
||||||
|
* @author Joe Grandja
|
||||||
|
* @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<>();
|
||||||
|
private final ClientRegistrationRepository clientRegistrationRepository;
|
||||||
|
|
||||||
|
public InMemoryOAuth2AuthorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {
|
||||||
|
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
||||||
|
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T loadAuthorizedClient(String clientRegistrationId, Authentication principal) {
|
||||||
|
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
|
||||||
|
Assert.notNull(principal, "principal cannot be null");
|
||||||
|
ClientRegistration registration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);
|
||||||
|
if (registration == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.authorizedClients.get(this.getIdentifier(registration, principal));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAuthorizedClient(T 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T removeAuthorizedClient(String clientRegistrationId, Authentication principal) {
|
||||||
|
Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty");
|
||||||
|
Assert.notNull(principal, "principal cannot be null");
|
||||||
|
ClientRegistration registration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);
|
||||||
|
if (registration == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.authorizedClients.remove(this.getIdentifier(registration, principal));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getIdentifier(ClientRegistration registration, Authentication principal) {
|
||||||
|
String identifier = "[" + registration.getRegistrationId() + "][" + principal.getName() + "]";
|
||||||
|
return Base64.getEncoder().encodeToString(identifier.getBytes());
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,6 @@ import org.springframework.util.Assert;
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
* @see ClientRegistration
|
* @see ClientRegistration
|
||||||
* @see OAuth2AccessToken
|
* @see OAuth2AccessToken
|
||||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-5.1">Section 5.1 Access Token Response</a>
|
|
||||||
*/
|
*/
|
||||||
public class OAuth2AuthorizedClient {
|
public class OAuth2AuthorizedClient {
|
||||||
private final ClientRegistration clientRegistration;
|
private final ClientRegistration clientRegistration;
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.oauth2.client.oidc.OidcAuthorizedClient;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of this interface are responsible for the management
|
||||||
|
* of {@link OAuth2AuthorizedClient Authorized Client(s)}, which provide the purpose
|
||||||
|
* of associating an {@link OAuth2AuthorizedClient#getAccessToken() Access Token} to a
|
||||||
|
* {@link OAuth2AuthorizedClient#getClientRegistration() Client} and <i>Resource Owner</i>,
|
||||||
|
* who is the {@link OAuth2AuthorizedClient#getPrincipalName() Principal}
|
||||||
|
* that originally granted the authorization.
|
||||||
|
*
|
||||||
|
* @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>
|
||||||
|
*/
|
||||||
|
public interface OAuth2AuthorizedClientService<T extends OAuth2AuthorizedClient> {
|
||||||
|
|
||||||
|
T loadAuthorizedClient(String clientRegistrationId, Authentication principal);
|
||||||
|
|
||||||
|
void saveAuthorizedClient(T authorizedClient, Authentication principal);
|
||||||
|
|
||||||
|
T removeAuthorizedClient(String clientRegistrationId, Authentication principal);
|
||||||
|
|
||||||
|
}
|
|
@ -36,7 +36,6 @@ import org.springframework.util.Assert;
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
* @see OAuth2AuthorizedClient
|
* @see OAuth2AuthorizedClient
|
||||||
* @see OidcIdToken
|
* @see OidcIdToken
|
||||||
* @see <a target="_blank" href="http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse">3.1.3.3 Successful Token Response</a>
|
|
||||||
*/
|
*/
|
||||||
public class OidcAuthorizedClient extends OAuth2AuthorizedClient {
|
public class OidcAuthorizedClient extends OAuth2AuthorizedClient {
|
||||||
private final OidcIdToken idToken;
|
private final OidcIdToken idToken;
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
|
||||||
|
@ -84,6 +85,7 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
||||||
public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";
|
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 static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";
|
||||||
private ClientRegistrationRepository clientRegistrationRepository;
|
private ClientRegistrationRepository clientRegistrationRepository;
|
||||||
|
private OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService;
|
||||||
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =
|
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =
|
||||||
new HttpSessionOAuth2AuthorizationRequestRepository();
|
new HttpSessionOAuth2AuthorizationRequestRepository();
|
||||||
private OAuth2TokenRepository<OAuth2AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
|
private OAuth2TokenRepository<OAuth2AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
|
||||||
|
@ -100,6 +102,7 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
||||||
public void afterPropertiesSet() {
|
public void afterPropertiesSet() {
|
||||||
super.afterPropertiesSet();
|
super.afterPropertiesSet();
|
||||||
Assert.notNull(this.clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
Assert.notNull(this.clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
||||||
|
Assert.notNull(this.authorizedClientService, "authorizedClientService cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -140,6 +143,9 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
||||||
OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> oauth2Authentication =
|
OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient> oauth2Authentication =
|
||||||
(OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient>) this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
|
(OAuth2AuthenticationToken<OAuth2User, OAuth2AuthorizedClient>) this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
|
||||||
|
|
||||||
|
this.authorizedClientService.saveAuthorizedClient(
|
||||||
|
oauth2Authentication.getAuthorizedClient(), oauth2Authentication);
|
||||||
|
|
||||||
this.accessTokenRepository.saveToken(
|
this.accessTokenRepository.saveToken(
|
||||||
oauth2Authentication.getAuthorizedClient().getAccessToken(),
|
oauth2Authentication.getAuthorizedClient().getAccessToken(),
|
||||||
oauth2Authentication.getAuthorizedClient().getClientRegistration(),
|
oauth2Authentication.getAuthorizedClient().getClientRegistration(),
|
||||||
|
@ -153,6 +159,11 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
||||||
this.clientRegistrationRepository = clientRegistrationRepository;
|
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void setAuthorizedClientService(OAuth2AuthorizedClientService<OAuth2AuthorizedClient> authorizedClientService) {
|
||||||
|
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
|
||||||
|
this.authorizedClientService = authorizedClientService;
|
||||||
|
}
|
||||||
|
|
||||||
public final void setAuthorizationRequestRepository(AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {
|
public final void setAuthorizationRequestRepository(AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {
|
||||||
Assert.notNull(authorizationRequestRepository, "authorizationRequestRepository cannot be null");
|
Assert.notNull(authorizationRequestRepository, "authorizationRequestRepository cannot be null");
|
||||||
this.authorizationRequestRepository = authorizationRequestRepository;
|
this.authorizationRequestRepository = authorizationRequestRepository;
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
@ -184,6 +185,7 @@ public class OAuth2LoginAuthenticationFilterTests {
|
||||||
OAuth2LoginAuthenticationFilter filter = new OAuth2LoginAuthenticationFilter();
|
OAuth2LoginAuthenticationFilter filter = new OAuth2LoginAuthenticationFilter();
|
||||||
filter.setClientRegistrationRepository(clientRegistrationRepository);
|
filter.setClientRegistrationRepository(clientRegistrationRepository);
|
||||||
filter.setAuthenticationManager(authenticationManager);
|
filter.setAuthenticationManager(authenticationManager);
|
||||||
|
filter.setAuthorizedClientService(mock(OAuth2AuthorizedClientService.class));
|
||||||
|
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue