Package org.springframework.security.oauth2.provider.token

Source Code of org.springframework.security.oauth2.provider.token.DefaultTokenServices

/*
* Copyright 2008 Web Cohesion
*
* 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.provider.token;

import java.util.Date;
import java.util.Set;
import java.util.UUID;

import org.springframework.beans.factory.InitializingBean;
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.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.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
*/
@Transactional
public class DefaultTokenServices 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;

  /**
   * 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");
  }

  public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {

    OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
    OAuth2RefreshToken refreshToken = null;
    if (existingAccessToken != null) {
      if (existingAccessToken.isExpired()) {
        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);
      }
      else {
        // Re-store the access token in case the authentication has changed
        tokenStore.storeAccessToken(existingAccessToken, authentication);
        return 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;

  }

  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);
    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.getScope());

    if (!reuseRefreshToken) {
      tokenStore.removeRefreshToken(refreshToken);
      refreshToken = createRefreshToken(authentication);
    }

    OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
    tokenStore.storeAccessToken(accessToken, authentication);
    if (!reuseRefreshToken) {
      tokenStore.storeRefreshToken(refreshToken, authentication);
    }
    return accessToken;
  }

  public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
    return tokenStore.getAccessToken(authentication);
  }

  /**
   * Create a refreshed authentication.
   *
   * @param authentication The authentication.
   * @param scope 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, Set<String> scope) {
    OAuth2Authentication narrowed = authentication;
    if (scope != null && !scope.isEmpty()) {
      OAuth2Request clientAuth = authentication.getOAuth2Request();
      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 {
        narrowed = new OAuth2Authentication(clientAuth.narrowScope(scope),
            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) {
    return tokenStore.readAccessToken(accessToken);
  }

  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 (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 ExpiringOAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
    if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
      return null;
    }
    int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
    ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken(UUID.randomUUID().toString(),
        new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
    return refreshToken;
  }

  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 authorizationRequest 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 authorizationRequest 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 authorizationRequest 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.
   *
   * @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 he 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;
  }

  /**
   * 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;
  }

}
TOP

Related Classes of org.springframework.security.oauth2.provider.token.DefaultTokenServices

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.