OAuth2 client with password provision in Spring Security

I use the oAuth2 secure services suite. Currently, it works as follows: a client is registered using a username and password. I exchange them for a token. I save the token in the session and send it every time I want to call the service. It works, but the problem is that I do it completely manually, without using most of the support

<!-- 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://xxxx/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"/> 

As you can see, I myself made an authentication provider. It accepts the standard UserPasswordAuthenticationToken, but produces my own extension, which also supports the actual OAuth2AccessToken, thereby preserving it in a security context.

 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. eg ROLE_USER } 

}

As you can see, this basically ensures that the token remains in the security area, from where I can simply extract it manually before each call to internal services. Similarly, I will check for freshness of the token before each call. This works well, but I'm sure I can use the Spring oauth namespace in XML (I don't use the Java configuration) to achieve the same thing in more-config-less-code. Most of the examples I find include an oAuth server implementation that I don't care about and just confuse me.

Can anyone help me with this?

+5
source share
1 answer

I developed a similar solution to view OAuth Spring's security sources and bits and other solutions found on the Internet. I am using Java Config, but maybe this can help you map the xml configuration, here it is:

 @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; } } 

One important bit that I found is that you need to use AccessTokenProviderChain even for one provider, otherwise automatic updating of the token (after authentication) will not work.

To set up user credentials on first request, you will need the following:

 @Autowired private OAuth2RestTemplate restTemplate; restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("username", username); restTemplate.getOAuth2ClientContext().getAccessTokenRequest().set("password", password); 

Then you can send requests as usual using the RestTemplate methods, for example:

  String url = "http://localhost:{port}/api/users/search/findByUsername?username={username}"; ResponseEntity<User> responseEntity = restTemplate.getForEntity( url, User.class, 8081, username); 

If you want to track requests on the wire, you can set the log level on the apache http client to DEBUG, for example. using Spring Download:

logging.level.org.apache.http = DEBUGGING

+3
source

All Articles