|
@@ -1,438 +0,0 @@
|
|
|
-package com.yonge.core.auth.core.service;
|
|
|
-
|
|
|
-import java.util.Date;
|
|
|
-import java.util.Set;
|
|
|
-import java.util.UUID;
|
|
|
-
|
|
|
-import org.apache.commons.lang3.StringUtils;
|
|
|
-import org.springframework.beans.factory.InitializingBean;
|
|
|
-import org.springframework.security.authentication.AuthenticationManager;
|
|
|
-import org.springframework.security.core.Authentication;
|
|
|
-import org.springframework.security.core.AuthenticationException;
|
|
|
-import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
|
|
|
-import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
|
|
-import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
|
|
|
-import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
|
|
|
-import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
|
|
-import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
|
|
-import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
|
|
-import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
|
|
|
-import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
|
|
-import org.springframework.security.oauth2.provider.ClientDetails;
|
|
|
-import org.springframework.security.oauth2.provider.ClientDetailsService;
|
|
|
-import org.springframework.security.oauth2.provider.ClientRegistrationException;
|
|
|
-import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
|
|
-import org.springframework.security.oauth2.provider.OAuth2Request;
|
|
|
-import org.springframework.security.oauth2.provider.TokenRequest;
|
|
|
-import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
|
|
-import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
|
|
|
-import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
|
|
-import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
|
|
-import org.springframework.security.oauth2.provider.token.TokenStore;
|
|
|
-import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
|
|
-import org.springframework.transaction.annotation.Transactional;
|
|
|
-import org.springframework.util.Assert;
|
|
|
-
|
|
|
-/**
|
|
|
- * Base implementation for token services using random UUID values for the access token and refresh token values. The
|
|
|
- * main extension point for customizations is the {@link TokenEnhancer} which will be called after the access and
|
|
|
- * refresh tokens have been generated but before they are stored.
|
|
|
- * <p>
|
|
|
- * Persistence is delegated to a {@code TokenStore} implementation and customization of the access token to a
|
|
|
- * {@link TokenEnhancer}.
|
|
|
- *
|
|
|
- * @author Ryan Heaton
|
|
|
- * @author Luke Taylor
|
|
|
- * @author Dave Syer
|
|
|
- */
|
|
|
-public class CustomTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
|
|
|
- ConsumerTokenServices, InitializingBean {
|
|
|
-
|
|
|
- private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.
|
|
|
-
|
|
|
- private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.
|
|
|
-
|
|
|
- private boolean supportRefreshToken = false;
|
|
|
-
|
|
|
- private boolean reuseRefreshToken = true;
|
|
|
-
|
|
|
- private TokenStore tokenStore;
|
|
|
-
|
|
|
- private ClientDetailsService clientDetailsService;
|
|
|
-
|
|
|
- private TokenEnhancer accessTokenEnhancer;
|
|
|
-
|
|
|
- private AuthenticationManager authenticationManager;
|
|
|
-
|
|
|
- /**
|
|
|
- * Initialize these token services. If no random generator is set, one will be created.
|
|
|
- */
|
|
|
- public void afterPropertiesSet() throws Exception {
|
|
|
- Assert.notNull(tokenStore, "tokenStore must be set");
|
|
|
- }
|
|
|
-
|
|
|
- @Transactional
|
|
|
- public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
|
|
|
-
|
|
|
- OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
|
|
|
- OAuth2RefreshToken refreshToken = null;
|
|
|
- if (existingAccessToken != null) {
|
|
|
- if (existingAccessToken.getRefreshToken() != null) {
|
|
|
- refreshToken = existingAccessToken.getRefreshToken();
|
|
|
- // The token store could remove the refresh token when the
|
|
|
- // access token is removed, but we want to
|
|
|
- // be sure...
|
|
|
- tokenStore.removeRefreshToken(refreshToken);
|
|
|
- }
|
|
|
- tokenStore.removeAccessToken(existingAccessToken);
|
|
|
- }
|
|
|
-
|
|
|
- // Only create a new refresh token if there wasn't an existing one
|
|
|
- // associated with an expired access token.
|
|
|
- // Clients might be holding existing refresh tokens, so we re-use it in
|
|
|
- // the case that the old access token
|
|
|
- // expired.
|
|
|
- if (refreshToken == null) {
|
|
|
- refreshToken = createRefreshToken(authentication);
|
|
|
- }
|
|
|
- // But the refresh token itself might need to be re-issued if it has
|
|
|
- // expired.
|
|
|
- else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
|
|
|
- ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
|
|
|
- if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
|
|
|
- refreshToken = createRefreshToken(authentication);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
|
|
|
- tokenStore.storeAccessToken(accessToken, authentication);
|
|
|
- // In case it was modified
|
|
|
- refreshToken = accessToken.getRefreshToken();
|
|
|
- if (refreshToken != null) {
|
|
|
- tokenStore.storeRefreshToken(refreshToken, authentication);
|
|
|
- }
|
|
|
- return accessToken;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
|
|
|
- public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
|
|
|
- throws AuthenticationException {
|
|
|
-
|
|
|
- if (!supportRefreshToken) {
|
|
|
- throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
|
|
|
- }
|
|
|
-
|
|
|
- OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
|
|
|
- if (refreshToken == null) {
|
|
|
- throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
|
|
|
- }
|
|
|
-
|
|
|
- OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
|
|
|
- if (this.authenticationManager != null && !authentication.isClientOnly()) {
|
|
|
- // The client has already been authenticated, but the user authentication might be old now, so give it a
|
|
|
- // chance to re-authenticate.
|
|
|
- Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
|
|
|
- user = authenticationManager.authenticate(user);
|
|
|
- Object details = authentication.getDetails();
|
|
|
- authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
|
|
|
- authentication.setDetails(details);
|
|
|
- }
|
|
|
- String clientId = authentication.getOAuth2Request().getClientId();
|
|
|
- if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
|
|
|
- throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
|
|
|
- }
|
|
|
-
|
|
|
- // clear out any access tokens already associated with the refresh
|
|
|
- // token.
|
|
|
- tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);
|
|
|
-
|
|
|
- if (isExpired(refreshToken)) {
|
|
|
- tokenStore.removeRefreshToken(refreshToken);
|
|
|
- throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
|
|
|
- }
|
|
|
-
|
|
|
- authentication = createRefreshedAuthentication(authentication, tokenRequest);
|
|
|
-
|
|
|
- if (!reuseRefreshToken) {
|
|
|
- tokenStore.removeRefreshToken(refreshToken);
|
|
|
- refreshToken = createRefreshToken(authentication);
|
|
|
- }
|
|
|
-
|
|
|
- OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
|
|
|
- tokenStore.storeAccessToken(accessToken, authentication);
|
|
|
- if (!reuseRefreshToken) {
|
|
|
- tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
|
|
|
- }
|
|
|
- return accessToken;
|
|
|
- }
|
|
|
-
|
|
|
- public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
|
|
|
- return tokenStore.getAccessToken(authentication);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Create a refreshed authentication.
|
|
|
- *
|
|
|
- * @param authentication The authentication.
|
|
|
- * @param request The scope for the refreshed token.
|
|
|
- * @return The refreshed authentication.
|
|
|
- * @throws InvalidScopeException If the scope requested is invalid or wider than the original scope.
|
|
|
- */
|
|
|
- private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) {
|
|
|
- OAuth2Authentication narrowed = authentication;
|
|
|
- Set<String> scope = request.getScope();
|
|
|
- OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
|
|
|
- if (scope != null && !scope.isEmpty()) {
|
|
|
- Set<String> originalScope = clientAuth.getScope();
|
|
|
- if (originalScope == null || !originalScope.containsAll(scope)) {
|
|
|
- throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope
|
|
|
- + ".", originalScope);
|
|
|
- }
|
|
|
- else {
|
|
|
- clientAuth = clientAuth.narrowScope(scope);
|
|
|
- }
|
|
|
- }
|
|
|
- narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
|
|
|
- return narrowed;
|
|
|
- }
|
|
|
-
|
|
|
- protected boolean isExpired(OAuth2RefreshToken refreshToken) {
|
|
|
- if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
|
|
|
- ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken;
|
|
|
- return expiringToken.getExpiration() == null
|
|
|
- || System.currentTimeMillis() > expiringToken.getExpiration().getTime();
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- public OAuth2AccessToken readAccessToken(String accessToken) {
|
|
|
- OAuth2AccessToken token = tokenStore.readAccessToken(accessToken);
|
|
|
- if (token == null) {
|
|
|
- throw new InvalidTokenException("Token was not recognised");
|
|
|
- }
|
|
|
-
|
|
|
- if (token.isExpired()) {
|
|
|
- throw new InvalidTokenException("Token has expired");
|
|
|
- }
|
|
|
-
|
|
|
- DefaultOAuth2AccessToken oAuth2AccessToken = (DefaultOAuth2AccessToken) token;
|
|
|
-
|
|
|
- OAuth2Authentication authentication = tokenStore.readAuthentication(accessToken);
|
|
|
-
|
|
|
- if (StringUtils.equalsIgnoreCase("system", authentication.getOAuth2Request().getClientId())) {
|
|
|
- int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
|
|
|
- if (validitySeconds > 0) {
|
|
|
- oAuth2AccessToken.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
|
|
|
-
|
|
|
- tokenStore.storeAccessToken(oAuth2AccessToken, authentication);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return oAuth2AccessToken;
|
|
|
- }
|
|
|
-
|
|
|
- public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
|
|
|
- InvalidTokenException {
|
|
|
- OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
|
|
|
- if (accessToken == null) {
|
|
|
- throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
|
|
|
- }
|
|
|
- else if (accessToken.isExpired()) {
|
|
|
- tokenStore.removeAccessToken(accessToken);
|
|
|
- throw new InvalidTokenException("Access token expired: " + accessTokenValue);
|
|
|
- }
|
|
|
-
|
|
|
- OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
|
|
|
- if (result == null) {
|
|
|
- // in case of race condition
|
|
|
- throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
|
|
|
- }
|
|
|
- if (clientDetailsService != null) {
|
|
|
- String clientId = result.getOAuth2Request().getClientId();
|
|
|
- try {
|
|
|
- clientDetailsService.loadClientByClientId(clientId);
|
|
|
- }
|
|
|
- catch (ClientRegistrationException e) {
|
|
|
- throw new InvalidTokenException("Client not valid: " + clientId, e);
|
|
|
- }
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- public String getClientId(String tokenValue) {
|
|
|
- OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
|
|
|
- if (authentication == null) {
|
|
|
- throw new InvalidTokenException("Invalid access token: " + tokenValue);
|
|
|
- }
|
|
|
- OAuth2Request clientAuth = authentication.getOAuth2Request();
|
|
|
- if (clientAuth == null) {
|
|
|
- throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue);
|
|
|
- }
|
|
|
- return clientAuth.getClientId();
|
|
|
- }
|
|
|
-
|
|
|
- public boolean revokeToken(String tokenValue) {
|
|
|
- OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
|
|
|
- if (accessToken == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (accessToken.getRefreshToken() != null) {
|
|
|
- tokenStore.removeRefreshToken(accessToken.getRefreshToken());
|
|
|
- }
|
|
|
- tokenStore.removeAccessToken(accessToken);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
|
|
|
- if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
|
|
|
- String value = UUID.randomUUID().toString();
|
|
|
- if (validitySeconds > 0) {
|
|
|
- return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis()
|
|
|
- + (validitySeconds * 1000L)));
|
|
|
- }
|
|
|
- return new DefaultOAuth2RefreshToken(value);
|
|
|
- }
|
|
|
-
|
|
|
- private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
|
|
|
- DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
|
|
|
- int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
|
|
|
- if (validitySeconds > 0) {
|
|
|
- token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
|
|
|
- }
|
|
|
- token.setRefreshToken(refreshToken);
|
|
|
- token.setScope(authentication.getOAuth2Request().getScope());
|
|
|
-
|
|
|
- return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The access token validity period in seconds
|
|
|
- *
|
|
|
- * @param clientAuth the current authorization request
|
|
|
- * @return the access token validity period in seconds
|
|
|
- */
|
|
|
- protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) {
|
|
|
- if (clientDetailsService != null) {
|
|
|
- ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
|
|
|
- Integer validity = client.getAccessTokenValiditySeconds();
|
|
|
- if (validity != null) {
|
|
|
- return validity;
|
|
|
- }
|
|
|
- }
|
|
|
- return accessTokenValiditySeconds;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The refresh token validity period in seconds
|
|
|
- *
|
|
|
- * @param clientAuth the current authorization request
|
|
|
- * @return the refresh token validity period in seconds
|
|
|
- */
|
|
|
- protected int getRefreshTokenValiditySeconds(OAuth2Request clientAuth) {
|
|
|
- if (clientDetailsService != null) {
|
|
|
- ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
|
|
|
- Integer validity = client.getRefreshTokenValiditySeconds();
|
|
|
- if (validity != null) {
|
|
|
- return validity;
|
|
|
- }
|
|
|
- }
|
|
|
- return refreshTokenValiditySeconds;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Is a refresh token supported for this client (or the global setting if
|
|
|
- * {@link #setClientDetailsService(ClientDetailsService) clientDetailsService} is not set.
|
|
|
- *
|
|
|
- * @param clientAuth the current authorization request
|
|
|
- * @return boolean to indicate if refresh token is supported
|
|
|
- */
|
|
|
- protected boolean isSupportRefreshToken(OAuth2Request clientAuth) {
|
|
|
- if (clientDetailsService != null) {
|
|
|
- ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
|
|
|
- return client.getAuthorizedGrantTypes().contains("refresh_token");
|
|
|
- }
|
|
|
- return this.supportRefreshToken;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * An access token enhancer that will be applied to a new token before it is saved in the token store.
|
|
|
- *
|
|
|
- * @param accessTokenEnhancer the access token enhancer to set
|
|
|
- */
|
|
|
- public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
|
|
|
- this.accessTokenEnhancer = accessTokenEnhancer;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The validity (in seconds) of the refresh token. If less than or equal to zero then the tokens will be
|
|
|
- * non-expiring.
|
|
|
- *
|
|
|
- * @param refreshTokenValiditySeconds The validity (in seconds) of the refresh token.
|
|
|
- */
|
|
|
- public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
|
|
|
- this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The default validity (in seconds) of the access token. Zero or negative for non-expiring tokens. If a client
|
|
|
- * details service is set the validity period will be read from the client, defaulting to this value if not defined
|
|
|
- * by the client.
|
|
|
- *
|
|
|
- * @param accessTokenValiditySeconds The validity (in seconds) of the access token.
|
|
|
- */
|
|
|
- public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
|
|
|
- this.accessTokenValiditySeconds = accessTokenValiditySeconds;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Whether to support the refresh token.
|
|
|
- *
|
|
|
- * @param supportRefreshToken Whether to support the refresh token.
|
|
|
- */
|
|
|
- public void setSupportRefreshToken(boolean supportRefreshToken) {
|
|
|
- this.supportRefreshToken = supportRefreshToken;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Whether to reuse refresh tokens (until expired).
|
|
|
- *
|
|
|
- * @param reuseRefreshToken Whether to reuse refresh tokens (until expired).
|
|
|
- */
|
|
|
- public void setReuseRefreshToken(boolean reuseRefreshToken) {
|
|
|
- this.reuseRefreshToken = reuseRefreshToken;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The persistence strategy for token storage.
|
|
|
- *
|
|
|
- * @param tokenStore the store for access and refresh tokens.
|
|
|
- */
|
|
|
- public void setTokenStore(TokenStore tokenStore) {
|
|
|
- this.tokenStore = tokenStore;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * An authentication manager that will be used (if provided) to check the user authentication when a token is
|
|
|
- * refreshed.
|
|
|
- *
|
|
|
- * @param authenticationManager the authenticationManager to set
|
|
|
- */
|
|
|
- public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
|
|
- this.authenticationManager = authenticationManager;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The client details service to use for looking up clients (if necessary). Optional if the access token expiry is
|
|
|
- * set globally via {@link #setAccessTokenValiditySeconds(int)}.
|
|
|
- *
|
|
|
- * @param clientDetailsService the client details service
|
|
|
- */
|
|
|
- public void setClientDetailsService(ClientDetailsService clientDetailsService) {
|
|
|
- this.clientDetailsService = clientDetailsService;
|
|
|
- }
|
|
|
-
|
|
|
-}
|