2015-04-18 6 views
5

Ich bin eine Reihe von oAuth2 geschützten Diensten. Es funktioniert derzeit so: Der Client meldet sich mit seinem Benutzernamen und seinem Passwort an. Ich tausche diese gegen ein Token. Ich halte das Token in der Sitzung und sende es jedes Mal, wenn ich einen Dienst anrufen möchte. Es funktioniert, aber das Problem ist, dass ich das komplett manuell mache, ohne viel von der oAuth2-Unterstützung von Spring Security zu verwenden. Hier ist, wie es aussieht:oAuth2-Client mit Passwort gewähren in Spring Security

<!-- Configure Authentication mechanism --> 
<authentication-manager alias="authenticationManager"> 
    <authentication-provider ref="oAuth2AuthenticationProvider"/> 
</authentication-manager> 


<beans:bean id="oAuth2AuthenticationProvider" class="my.custom.Oauth2AuthenticationProvider"> 
    <beans:constructor-arg name="accessTokenUri" value="http://x.x.x.x/oauth/token"/> 
    <beans:constructor-arg name="clientId" value="myClientId"/> 
    <beans:constructor-arg name="clientSecret" value="myClientSecret"/> 
    <beans:constructor-arg name="scope"> 
     <beans:list> 
      <beans:value>myScope</beans:value> 
     </beans:list> 
    </beans:constructor-arg> 
</beans:bean> 

<beans:bean id="resourceOwnerPasswordAccessTokenProvider" class="org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider"/> 

Wie man sehen kann ich mir die Authentifizierungsanbieter gemacht. Es akzeptiert den Standard UsernamePasswordAuthenticationToken, erzeugt aber eine eigene Erweiterung, die auch den eigentlichen OAuth2AccessToken enthält und somit im Sicherheitskontext bleibt.

public class Oauth2AuthenticationProvider implements AuthenticationProvider { 

@Autowired 
private ResourceOwnerPasswordAccessTokenProvider provider; 

private String accessTokenUri; 
private String clientId; 
private String clientSecret; 
private List<String> scope; 

public Oauth2AuthenticationProvider(String accessTokenUri, String clientId, String clientSecret, List<String> scope) { 
    this.accessTokenUri = accessTokenUri; 
    this.clientId = clientId; 
    this.clientSecret = clientSecret; 
    this.scope = scope; 
} 

@Override 
public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
    String username = authentication.getName(); 
    String password = authentication.getCredentials().toString(); 
    OAuth2AccessToken token = obtainToken(username, password); 
    return handleLogonSuccess(authentication, token); 
} 

private OAuth2AccessToken obtainToken(String username, String password) { 
    ResourceOwnerPasswordResourceDetails passwordResourceDetails = new ResourceOwnerPasswordResourceDetails(); 
    passwordResourceDetails.setUsername(username); 
    passwordResourceDetails.setPassword(password); 
    passwordResourceDetails.setClientId(clientId); 
    passwordResourceDetails.setClientSecret(clientSecret); 
    passwordResourceDetails.setScope(scope); 
    passwordResourceDetails.setAccessTokenUri(accessTokenUri); 
    DefaultAccessTokenRequest defaultAccessTokenRequest = new DefaultAccessTokenRequest(); 
    OAuth2AccessToken token; 
    try { 
     token = provider.obtainAccessToken(passwordResourceDetails, defaultAccessTokenRequest); 
    } catch (OAuth2AccessDeniedException accessDeniedException) { 
     throw new BadCredentialsException("Invalid credentials", accessDeniedException); 
    } 

    return token; 
} 

public OAuth2AccessToken refreshToken(OAuth2AuthenticationToken authentication) { 
    OAuth2AccessToken token = authentication.getoAuth2AccessToken(); 
    OAuth2RefreshToken refreshToken = token.getRefreshToken(); 
    BaseOAuth2ProtectedResourceDetails resourceDetails = new BaseOAuth2ProtectedResourceDetails(); 
    resourceDetails.setClientId(clientId); 
    resourceDetails.setClientSecret(clientSecret); 
    resourceDetails.setScope(scope); 
    resourceDetails.setAccessTokenUri(accessTokenUri); 
    OAuth2AccessToken newToken = provider.refreshAccessToken(resourceDetails, refreshToken, new DefaultAccessTokenRequest()); 
    authentication.setoAuth2AccessToken(newToken); 
    return newToken; 
} 

public boolean supports(Class<?> authentication) { 
    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); 
} 

private Authentication handleLogonSuccess(Authentication authentication, OAuth2AccessToken token) { 

    MyCustomOAuth2AuthenticationToken successAuthenticationToken = new MyCustomOAuth2AuthenticationToken(user, authentication.getCredentials(), calculateAuthorities(authentication), token); 

    return successAuthenticationToken; 
} 

public list<GrantedAuthority> calculateAuthorities(Authentication authentication) { 
     //my custom logic that assigns the correct role. e.g. ROLE_USER 
} 

}

Wie Sie sehen, macht es im Grunde sicher, dass die Token im Sicherheits Rahmen bleiben, von dem ich einfach manuell vor jedem Aufruf an den Back-End-Dienste extrahieren kann. Ebenso überprüfe ich vor jedem Anruf die Frische des Tokens. Das funktioniert gut, aber ich bin mir sicher, dass ich den OAUTH-Namespace von Spring in XML verwenden kann (ich benutze keine Java-Konfiguration), um das selbe in einer mehr konfigurationsfreien Weise zu erreichen. Die meisten Beispiele, die ich finde, schließen die Implementierung des oAuth-Servers ein, die mich nicht interessiert und mich nur verwirrt.

Kann mir bitte jemand dabei helfen?

Antwort

3

Ich habe eine ähnliche Lösung aus dem Durchsuchen der Spring Security OAuth Quellen und Bits und Stücke von anderen Lösungen online gefunden. Ich bin mit Java Config aber vielleicht kann es helfen, Sie zu einer XML-Konfigurationskarte, hier geht es:

@Configuration 
@EnableOAuth2Client 
public class RestClientConfig { 

    @Value("${http.client.maxPoolSize}") 
    private Integer maxPoolSize; 

    @Value("${oauth2.resourceId}") 
    private String resourceId; 

    @Value("${oauth2.clientId}") 
    private String clientId; 

    @Value("${oauth2.clientSecret}") 
    private String clientSecret; 

    @Value("${oauth2.accessTokenUri}") 
    private String accessTokenUri; 


    @Autowired 
    private OAuth2ClientContext oauth2ClientContext; 


    @Bean 
    public ClientHttpRequestFactory httpRequestFactory() { 
     return new HttpComponentsClientHttpRequestFactory(httpClient()); 
    } 

    @Bean 
    public HttpClient httpClient() { 
     PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); 
     connectionManager.setMaxTotal(maxPoolSize); 
     // This client is for internal connections so only one route is expected 
     connectionManager.setDefaultMaxPerRoute(maxPoolSize); 
     return HttpClientBuilder.create().setConnectionManager(connectionManager).build(); 
    } 

    @Bean 
    public OAuth2ProtectedResourceDetails oauth2ProtectedResourceDetails() { 
     ResourceOwnerPasswordResourceDetails details = new ResourceOwnerPasswordResourceDetails(); 
     details.setId(resourceId); 
     details.setClientId(clientId); 
     details.setClientSecret(clientSecret); 
     details.setAccessTokenUri(accessTokenUri); 
     return details; 
    } 

    @Bean 
    public AccessTokenProvider accessTokenProvider() { 
     ResourceOwnerPasswordAccessTokenProvider tokenProvider = new ResourceOwnerPasswordAccessTokenProvider(); 
     tokenProvider.setRequestFactory(httpRequestFactory()); 
     return new AccessTokenProviderChain(
        Arrays.<AccessTokenProvider> asList(tokenProvider) 
       ); 
    } 

    @Bean 
    public OAuth2RestTemplate restTemplate() { 
     OAuth2RestTemplate template = new OAuth2RestTemplate(oauth2ProtectedResourceDetails(), oauth2ClientContext); 
     template.setRequestFactory(httpRequestFactory()); 
     template.setAccessTokenProvider(accessTokenProvider()); 
     return template; 
    } 
} 

Ein wichtiges Stück fand ich, dass Sie die AccessTokenProviderChain sogar für einen einzelnen Provider sonst die automatische verwenden müssen Token-Aktualisierung (nach Authentifizierung) wird nicht funktionieren.

Um die Benutzerdaten auf der ersten Anfrage stellen Sie diese brauchen werden:

@Autowired 
private OAuth2RestTemplate restTemplate; 

restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("username", username); 
restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("password", password); 

Dann können Sie Anfragen als normale Ausgabe der RestTemplate Methoden, zB:

String url = "http://localhost:{port}/api/users/search/findByUsername?username={username}"; 

    ResponseEntity<User> responseEntity = restTemplate.getForEntity(
      url, User.class, 8081, username); 

Wenn Sie möchten, Um die Anforderungen auf der Leitung zu verfolgen, können Sie die Protokollierungsstufe auf dem Apache HTTP-Client auf DEBUG setzen, z mit Frühlings-Boot:

logging.level.org.apache.http = DEBUG

+0

was ist, wenn der Benutzername und das Kennwort in dem POST Körper ist? als x-www-url-codiert – cosbor11

Verwandte Themen