Pārlūkot izejas kodu

Refactor SecurityTokenRepository

Fixes gh-4650
Joe Grandja 8 gadi atpakaļ
vecāks
revīzija
9fbea5a11e

+ 8 - 12
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

@@ -20,10 +20,10 @@ 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.authentication.OAuth2LoginAuthenticationProvider;
 import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
 import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
 import org.springframework.security.oauth2.client.authentication.NimbusAuthorizationCodeTokenExchanger;
+import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
 import org.springframework.security.oauth2.client.authentication.jwt.JwtDecoderRegistry;
 import org.springframework.security.oauth2.client.authentication.jwt.NimbusJwtDecoderRegistry;
 import org.springframework.security.oauth2.client.authentication.userinfo.CustomUserTypesOAuth2UserService;
@@ -33,7 +33,7 @@ import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
 import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
-import org.springframework.security.oauth2.client.token.SecurityTokenRepository;
+import org.springframework.security.oauth2.client.token.OAuth2TokenRepository;
 import org.springframework.security.oauth2.client.web.AuthorizationRequestRedirectFilter;
 import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
 import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
@@ -132,7 +132,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 
 	public class TokenEndpointConfig {
 		private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
-		private SecurityTokenRepository<AccessToken> accessTokenRepository;
+		private OAuth2TokenRepository<AccessToken> accessTokenRepository;
 		private JwtDecoderRegistry jwtDecoderRegistry;
 
 		private TokenEndpointConfig() {
@@ -146,7 +146,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 			return this;
 		}
 
-		public TokenEndpointConfig accessTokenRepository(SecurityTokenRepository<AccessToken> accessTokenRepository) {
+		public TokenEndpointConfig accessTokenRepository(OAuth2TokenRepository<AccessToken> accessTokenRepository) {
 			Assert.notNull(accessTokenRepository, "accessTokenRepository cannot be null");
 			this.accessTokenRepository = accessTokenRepository;
 			return this;
@@ -249,10 +249,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 
 		OAuth2LoginAuthenticationProvider oauth2LoginAuthenticationProvider =
 			new OAuth2LoginAuthenticationProvider(authorizationCodeTokenExchanger, oauth2UserService);
-		if (this.tokenEndpointConfig.accessTokenRepository != null) {
-			oauth2LoginAuthenticationProvider.setAccessTokenRepository(
-				this.tokenEndpointConfig.accessTokenRepository);
-		}
 		if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) {
 			oauth2LoginAuthenticationProvider.setAuthoritiesMapper(
 				this.userInfoEndpointConfig.userAuthoritiesMapper);
@@ -267,10 +263,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 		OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider =
 			new OidcAuthorizationCodeAuthenticationProvider(
 				authorizationCodeTokenExchanger, oidcUserService, jwtDecoderRegistry);
-		if (this.tokenEndpointConfig.accessTokenRepository != null) {
-			oidcAuthorizationCodeAuthenticationProvider.setAccessTokenRepository(
-				this.tokenEndpointConfig.accessTokenRepository);
-		}
 		if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) {
 			oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(
 				this.userInfoEndpointConfig.userAuthoritiesMapper);
@@ -308,6 +300,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 			authorizationResponseFilter.setAuthorizationRequestRepository(
 				this.authorizationEndpointConfig.authorizationRequestRepository);
 		}
+		if (this.tokenEndpointConfig.accessTokenRepository != null) {
+			authorizationResponseFilter.setAccessTokenRepository(
+				this.tokenEndpointConfig.accessTokenRepository);
+		}
 		super.configure(http);
 	}
 

+ 0 - 13
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java

@@ -22,8 +22,6 @@ import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
 import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken;
 import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService;
-import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository;
-import org.springframework.security.oauth2.client.token.SecurityTokenRepository;
 import org.springframework.security.oauth2.core.AccessToken;
 import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
@@ -49,7 +47,6 @@ import java.util.Collection;
  * @author Joe Grandja
  * @since 5.0
  * @see AuthorizationCodeAuthenticationToken
- * @see SecurityTokenRepository
  * @see OAuth2AuthenticationToken
  * @see AuthorizedClient
  * @see OAuth2UserService
@@ -63,7 +60,6 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
 	private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
 	private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
 	private final OAuth2UserService userService;
-	private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
 	private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
 
 	public OAuth2LoginAuthenticationProvider(
@@ -121,10 +117,6 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
 		AuthorizedClient authorizedClient = new AuthorizedClient(
 			authorizationCodeAuthentication.getClientRegistration(), "unknown", accessToken);
 
-		this.accessTokenRepository.saveSecurityToken(
-			authorizedClient.getAccessToken(),
-			authorizedClient.getClientRegistration());
-
 		OAuth2User oauth2User = this.userService.loadUser(authorizedClient);
 
 		// Update AuthorizedClient now that we know the 'principalName'
@@ -141,11 +133,6 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
 		return authenticationResult;
 	}
 
-	public final void setAccessTokenRepository(SecurityTokenRepository<AccessToken> accessTokenRepository) {
-		Assert.notNull(accessTokenRepository, "accessTokenRepository cannot be null");
-		this.accessTokenRepository = accessTokenRepository;
-	}
-
 	public final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
 		Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null");
 		this.authoritiesMapper = authoritiesMapper;

+ 17 - 29
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/InMemoryAccessTokenRepository.java

@@ -15,6 +15,7 @@
  */
 package org.springframework.security.oauth2.client.token;
 
+import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.core.AccessToken;
 import org.springframework.util.Assert;
@@ -24,55 +25,42 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * A {@link SecurityTokenRepository} that associates an {@link AccessToken}
- * to a {@link ClientRegistration Client} and stores it <i>in-memory</i>.
+ * An <i>in-memory</i> {@link OAuth2TokenRepository} for {@link AccessToken}'s.
  *
  * @author Joe Grandja
  * @since 5.0
- * @see SecurityTokenRepository
+ * @see OAuth2TokenRepository
  * @see AccessToken
  * @see ClientRegistration
+ * @see Authentication
  */
-public final class InMemoryAccessTokenRepository implements SecurityTokenRepository<AccessToken> {
+public final class InMemoryAccessTokenRepository implements OAuth2TokenRepository<AccessToken> {
 	private final Map<String, AccessToken> accessTokens = new ConcurrentHashMap<>();
 
 	@Override
-	public AccessToken loadSecurityToken(ClientRegistration registration) {
+	public AccessToken loadToken(ClientRegistration registration, Authentication principal) {
 		Assert.notNull(registration, "registration cannot be null");
-		return this.accessTokens.get(this.getClientIdentifier(registration));
+		Assert.notNull(principal, "principal cannot be null");
+		return this.accessTokens.get(this.getIdentifier(registration, principal));
 	}
 
 	@Override
-	public void saveSecurityToken(AccessToken accessToken, ClientRegistration registration) {
+	public void saveToken(AccessToken accessToken, ClientRegistration registration, Authentication principal) {
 		Assert.notNull(accessToken, "accessToken cannot be null");
 		Assert.notNull(registration, "registration cannot be null");
-		this.accessTokens.put(this.getClientIdentifier(registration), accessToken);
+		Assert.notNull(principal, "principal cannot be null");
+		this.accessTokens.put(this.getIdentifier(registration, principal), accessToken);
 	}
 
 	@Override
-	public void removeSecurityToken(ClientRegistration registration) {
+	public AccessToken removeToken(ClientRegistration registration, Authentication principal) {
 		Assert.notNull(registration, "registration cannot be null");
-		this.accessTokens.remove(this.getClientIdentifier(registration));
+		Assert.notNull(principal, "principal cannot be null");
+		return this.accessTokens.remove(this.getIdentifier(registration, principal));
 	}
 
-	/**
-	 * A client is considered <i>&quot;authorized&quot;</i>, if it receives a successful response from the <i>Token Endpoint</i>.
-	 *
-	 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.3">Section 4.1.3 Access Token Request</a>
-	 * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-5.1">Section 5.1 Access Token Response</a>
-	 */
-	private String getClientIdentifier(ClientRegistration clientRegistration) {
-		StringBuilder builder = new StringBuilder();
-
-		// Access Token Request attributes
-		builder.append("[").append(clientRegistration.getAuthorizationGrantType().getValue()).append("]");
-		builder.append("[").append(clientRegistration.getRedirectUri()).append("]");
-		builder.append("[").append(clientRegistration.getClientId()).append("]");
-
-		// Access Token Response attributes
-		builder.append("[").append(clientRegistration.getScopes().toString()).append("]");
-
-		return Base64.getEncoder().encodeToString(builder.toString().getBytes());
+	private String getIdentifier(ClientRegistration registration, Authentication principal) {
+		String identifier = "[" + registration.getRegistrationId() + "][" + principal.getName() + "]";
+		return Base64.getEncoder().encodeToString(identifier.getBytes());
 	}
 }
-

+ 9 - 5
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/SecurityTokenRepository.java → oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/OAuth2TokenRepository.java

@@ -15,24 +15,28 @@
  */
 package org.springframework.security.oauth2.client.token;
 
+import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.core.AbstractOAuth2Token;
 
 /**
  * Implementations of this interface are responsible for the persistence
- * and association of an {@link AbstractOAuth2Token} to a {@link ClientRegistration Client}.
+ * and association of an {@link AbstractOAuth2Token OAuth 2.0 Token}
+ * to a {@link ClientRegistration Client} and <i>Resource Owner</i>,
+ * which is the {@link Authentication Principal} who originally granted the authorization.
  *
  * @author Joe Grandja
  * @since 5.0
  * @see AbstractOAuth2Token
  * @see ClientRegistration
+ * @see Authentication
  */
-public interface SecurityTokenRepository<T extends AbstractOAuth2Token> {
+public interface OAuth2TokenRepository<T extends AbstractOAuth2Token> {
 
-	T loadSecurityToken(ClientRegistration registration);
+	T loadToken(ClientRegistration registration, Authentication principal);
 
-	void saveSecurityToken(T securityToken, ClientRegistration registration);
+	void saveToken(T token, ClientRegistration registration, Authentication principal);
 
-	void removeSecurityToken(ClientRegistration registration);
+	T removeToken(ClientRegistration registration, Authentication principal);
 
 }

+ 23 - 3
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2LoginAuthenticationFilter.java

@@ -18,11 +18,15 @@ package org.springframework.security.oauth2.client.web;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
 import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider;
+import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
 import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository;
+import org.springframework.security.oauth2.client.token.OAuth2TokenRepository;
+import org.springframework.security.oauth2.core.AccessToken;
 import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.OAuth2ErrorCode;
 import org.springframework.security.oauth2.core.endpoint.AuthorizationExchange;
@@ -63,12 +67,14 @@ import java.io.IOException;
  * @since 5.0
  * @see AbstractAuthenticationProcessingFilter
  * @see AuthorizationCodeAuthenticationToken
+ * @see OAuth2AuthenticationToken
  * @see OAuth2LoginAuthenticationProvider
- * @see AuthorizationResponse
  * @see AuthorizationRequest
+ * @see AuthorizationResponse
  * @see AuthorizationRequestRepository
  * @see AuthorizationRequestRedirectFilter
  * @see ClientRegistrationRepository
+ * @see OAuth2TokenRepository
  * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a>
  * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.2">Section 4.1.2 Authorization Response</a>
  */
@@ -77,6 +83,7 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
 	private static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";
 	private ClientRegistrationRepository clientRegistrationRepository;
 	private AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionAuthorizationRequestRepository();
+	private OAuth2TokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
 
 	public OAuth2LoginAuthenticationFilter() {
 		this(DEFAULT_FILTER_PROCESSES_URI);
@@ -127,7 +134,15 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
 				clientRegistration, new AuthorizationExchange(authorizationRequest, authorizationResponse));
 		authorizationCodeAuthentication.setDetails(this.authenticationDetailsSource.buildDetails(request));
 
-		return this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
+		OAuth2AuthenticationToken oauth2Authentication =
+			(OAuth2AuthenticationToken) this.getAuthenticationManager().authenticate(authorizationCodeAuthentication);
+
+		this.accessTokenRepository.saveToken(
+			oauth2Authentication.getAuthorizedClient().getAccessToken(),
+			oauth2Authentication.getAuthorizedClient().getClientRegistration(),
+			oauth2Authentication);
+
+		return oauth2Authentication;
 	}
 
 	public final void setClientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
@@ -140,6 +155,11 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
 		this.authorizationRequestRepository = authorizationRequestRepository;
 	}
 
+	public final void setAccessTokenRepository(OAuth2TokenRepository<AccessToken> accessTokenRepository) {
+		Assert.notNull(accessTokenRepository, "accessTokenRepository cannot be null");
+		this.accessTokenRepository = accessTokenRepository;
+	}
+
 	private AuthorizationResponse convert(HttpServletRequest request) {
 		String code = request.getParameter(OAuth2Parameter.CODE);
 		String errorCode = request.getParameter(OAuth2Parameter.ERROR);

+ 1 - 13
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/oidc/client/authentication/OidcAuthorizationCodeAuthenticationProvider.java

@@ -27,8 +27,6 @@ import org.springframework.security.oauth2.client.authentication.jwt.JwtDecoderR
 import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2AuthenticationToken;
 import org.springframework.security.oauth2.client.authentication.userinfo.OAuth2UserService;
 import org.springframework.security.oauth2.client.registration.ClientRegistration;
-import org.springframework.security.oauth2.client.token.InMemoryAccessTokenRepository;
-import org.springframework.security.oauth2.client.token.SecurityTokenRepository;
 import org.springframework.security.oauth2.core.AccessToken;
 import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.endpoint.AuthorizationRequest;
@@ -61,7 +59,7 @@ import java.util.Collection;
  * @author Joe Grandja
  * @since 5.0
  * @see AuthorizationCodeAuthenticationToken
- * @see SecurityTokenRepository
+ * @see OAuth2AuthenticationToken
  * @see OidcAuthorizedClient
  * @see OidcUserService
  * @see OidcUser
@@ -75,7 +73,6 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
 	private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
 	private final OAuth2UserService userService;
 	private final JwtDecoderRegistry jwtDecoderRegistry;
-	private SecurityTokenRepository<AccessToken> accessTokenRepository = new InMemoryAccessTokenRepository();
 	private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities);
 
 	public OidcAuthorizationCodeAuthenticationProvider(
@@ -151,10 +148,6 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
 		OidcAuthorizedClient authorizedClient = new OidcAuthorizedClient(
 			clientRegistration, idToken.getSubject(), accessToken, idToken);
 
-		this.accessTokenRepository.saveSecurityToken(
-			authorizedClient.getAccessToken(),
-			authorizedClient.getClientRegistration());
-
 		OAuth2User oauth2User = this.userService.loadUser(authorizedClient);
 
 		// Update AuthorizedClient as the 'principalName' may have changed
@@ -172,11 +165,6 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
 		return authenticationResult;
 	}
 
-	public final void setAccessTokenRepository(SecurityTokenRepository<AccessToken> accessTokenRepository) {
-		Assert.notNull(accessTokenRepository, "accessTokenRepository cannot be null");
-		this.accessTokenRepository = accessTokenRepository;
-	}
-
 	public final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
 		Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null");
 		this.authoritiesMapper = authoritiesMapper;