2014-11-19 3 views
5

È possibile generare più token di accesso validi utilizzando il client_credentials o il tipo di concessione password per richiesta?Spring OAuth2 Genera token di accesso per richiesta all'endpoint Token

Generare un token utilizzando i tipi di sovvenzione di cui sopra fornisce un nuovo token quando quello corrente scade per richiesta.

Posso utilizzare il tipo di concessione della password per generare un token di aggiornamento e quindi generare più token di accesso, ma farlo annullerà qualsiasi token di accesso precedente.

Qualche idea su come potrei cambiare per consentire la generazione di un token di accesso per richiesta all'endpoint/oauth/token e assicurare che eventuali token precedenti non vengano invalidati?

Di seguito è la configurazione XML del mio server oauth.

<!-- oauth2 config start--> 
    <sec:http pattern="/test/oauth/token" create-session="never" 
       authentication-manager-ref="authenticationManager" > 
     <sec:intercept-url pattern="/test/oauth/token" access="IS_AUTHENTICATED_FULLY" /> 
     <sec:anonymous enabled="false" /> 
     <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint"/> 
     <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" /> 
     <sec:access-denied-handler ref="oauthAccessDeniedHandler" /> 
    </sec:http> 


    <bean id="clientCredentialsTokenEndpointFilter" 
      class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> 
     <property name="authenticationManager" ref="authenticationManager" /> 
    </bean> 

    <sec:authentication-manager alias="authenticationManager"> 
     <sec:authentication-provider user-service-ref="clientDetailsUserService" /> 
    </sec:authentication-manager> 

    <bean id="clientDetailsUserService" 
      class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> 
     <constructor-arg ref="clientDetails" /> 
    </bean> 

    <bean id="clientDetails" class="org.security.oauth2.ClientDetailsServiceImpl"></bean> 

    <bean id="clientAuthenticationEntryPoint" 
      class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> 
     <property name="realmName" value="springsec/client" /> 
     <property name="typeName" value="Basic" /> 
    </bean> 

    <bean id="oauthAccessDeniedHandler" 
      class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/> 

    <oauth:authorization-server 
     client-details-service-ref="clientDetails" token-services-ref="tokenServices"> 
     <oauth:authorization-code /> 
     <oauth:implicit/> 
     <oauth:refresh-token/> 
     <oauth:client-credentials /> 
     <oauth:password authentication-manager-ref="userAuthenticationManager"/> 
    </oauth:authorization-server> 

    <sec:authentication-manager id="userAuthenticationManager"> 
     <sec:authentication-provider ref="customUserAuthenticationProvider"> 
     </sec:authentication-provider> 
    </sec:authentication-manager> 

    <bean id="customUserAuthenticationProvider" 
      class="org.security.oauth2.CustomUserAuthenticationProvider"> 
    </bean> 

    <bean id="tokenServices" 
      class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> 
     <property name="tokenStore" ref="tokenStore" /> 
     <property name="supportRefreshToken" value="true" /> 
     <property name="accessTokenValiditySeconds" value="300"></property> 
     <property name="clientDetailsService" ref="clientDetails" /> 
    </bean> 

    <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore"> 
     <constructor-arg ref="jdbcTemplate" /> 
    </bean> 

    <bean id="jdbcTemplate" 
      class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
     <property name="driverClassName" value="com.mysql.jdbc.Driver"/> 
     <property name="url" value="jdbc:mysql://localhost:3306/oauthdb"/> 
     <property name="username" value="root"/> 
     <property name="password" value="password"/> 
    </bean> 
    <bean id="oauthAuthenticationEntryPoint" 
      class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> 
    </bean> 
+0

qual è lo scopo di avere più token attivi/validi per un account? Non riesco a pensare a un caso d'uso valido. – Jigish

+0

È molto utile quando si utilizza OAuth2 per l'app Web o l'app mobile lato client (JS) e avrete bisogno di un token diverso per ogni sessione. Questo è ciò che Google ha fatto con le loro API –

+0

@Thanh Nguyen Van questo è esattamente il motivo per cui ho bisogno di generarne uno nuovo su ogni richiesta. – JCS

risposta

10

aggiornamento il 21/11/2014

Quando doppio controllo, ho scoperto che InMemoryTokenStore utilizzare stringa hash 's un OAuth2Authentication come chiave di serveral Map. E quando uso lo stesso username, client_id, scope .. e ho ottenuto lo stesso key. Quindi questo potrebbe portare ad alcuni problemi. Quindi penso che i vecchi metodi siano deprecati. Quello che segue è quello che ho fatto per evitare il problema.

Creare un altro AuthenticationKeyGenerator in grado di calcolare chiave univoca, chiamato UniqueAuthenticationKeyGenerator

/* 
* Copyright 2006-2011 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. 
*/ 

/** 
* Basic key generator taking into account the client id, scope, resource ids and username (principal name) if they 
* exist. 
* 
* @author Dave Syer 
* @author thanh 
*/ 
public class UniqueAuthenticationKeyGenerator implements AuthenticationKeyGenerator { 

    private static final String CLIENT_ID = "client_id"; 

    private static final String SCOPE = "scope"; 

    private static final String USERNAME = "username"; 

    private static final String UUID_KEY = "uuid"; 

    public String extractKey(OAuth2Authentication authentication) { 
     Map<String, String> values = new LinkedHashMap<String, String>(); 
     OAuth2Request authorizationRequest = authentication.getOAuth2Request(); 
     if (!authentication.isClientOnly()) { 
      values.put(USERNAME, authentication.getName()); 
     } 
     values.put(CLIENT_ID, authorizationRequest.getClientId()); 
     if (authorizationRequest.getScope() != null) { 
      values.put(SCOPE, OAuth2Utils.formatParameterList(authorizationRequest.getScope())); 
     } 
     Map<String, Serializable> extentions = authorizationRequest.getExtensions(); 
     String uuid = null; 
     if (extentions == null) { 
      extentions = new HashMap<String, Serializable>(1); 
      uuid = UUID.randomUUID().toString(); 
      extentions.put(UUID_KEY, uuid); 
     } else { 
      uuid = (String) extentions.get(UUID_KEY); 
      if (uuid == null) { 
       uuid = UUID.randomUUID().toString(); 
       extentions.put(UUID_KEY, uuid); 
      } 
     } 
     values.put(UUID_KEY, uuid); 

     MessageDigest digest; 
     try { 
      digest = MessageDigest.getInstance("MD5"); 
     } 
     catch (NoSuchAlgorithmException e) { 
      throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK)."); 
     } 

     try { 
      byte[] bytes = digest.digest(values.toString().getBytes("UTF-8")); 
      return String.format("%032x", new BigInteger(1, bytes)); 
     } 
     catch (UnsupportedEncodingException e) { 
      throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK)."); 
     } 
    } 
} 

Infine, cablarlo con

<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore"> 
    <constructor-arg ref="jdbcTemplate" /> 
    <property name="authenticationKeyGenerator"> 
     <bean class="your.package.UniqueAuthenticationKeyGenerator" /> 
    </property> 
</bean> 

Sotto modo può che porta a qualche problema, vedi risposta aggiornato !!!

Si sta utilizzando DefaultTokenServices. Prova questo codice e assicurati di ridefinire il tuo pacchetto `tokenServices` com.thanh.backend.oauth2.core; import java.util.Date; import java.util.UUID; 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.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; /** * @author Thanh */public class SimpleTokenService estende DefaultTokenServices { privato TokenStore tokenStore; accesso TokenEnhancer privatoTokenEnhancer; @Override pubblico OAuth2AccessToken createAccessToken (autenticazione OAuth2Authentication) getta AuthenticationException { OAuth2RefreshToken refreshToken = createRefreshToken (autenticazione) ;; OAuth2AccessToken accessToken = createAccessToken (autenticazione, refreshToken); tokenStore.storeAccessToken (accessToken, autenticazione); tokenStore.storeRefreshToken (refreshToken, autenticazione); accesso di ritornoToken; } privato OAuth2AccessToken createAccessToken (autenticazione OAuth2Authentication, OAuth2RefreshToken refreshToken) { DefaultOAuth2AccessToken di 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; } privato ExpiringOAuth2RefreshToken createRefreshToken (autenticazione OAuth2Authentication) {{ se ritorno null (isSupportRefreshToken (authentication.getOAuth2Request())!); } int validitySeconds = getRefreshTokenValiditySeconds (authentication.getOAuth2Request()); ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken (UUID.randomUUID(). ToString(), new Date (System.currentTimeMillis() + (validitySeconds * 1000L))); return refreshToken; @Override public void setTokenEnhancer (TokenEnhancer accessTokenEnhancer) { super.setTokenEnhancer (accessTokenEnhancer); this.accessTokenEnhancer = accessTokenEnhancer; } @Override public void setTokenStore (TokenStore tokenStore) { super.setTokenStore (tokenStore); this.tokenStore = tokenStore; } }
+0

Questo è quello che ho finito e modificare il metodo createAccessToken per generare un nuovo token di accesso ogni volta che viene chiamato, ho pensato che ci sarebbe stato un modo molto più semplice senza sovrascrivere le classi predefinite. – JCS

+0

Ciao @ JCS, vedi la mia risposta aggiornata se hai trovato qualche problema con la modifica attuale, grazie! –

+0

ho modificato solo la funzione createAccessToken, ho lasciato il resto della classe non trattato con l'implementazione di default delle molle, che sembra funzionare correttamente. – JCS