可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a situation where I would like to create an access token myself (so not through the usual process). I have come up with something like this:
@Inject
private DefaultTokenServices defaultTokenServices;
...
OAuth2Authentication auth = xxx;
OAuth2AccessToken token = defaultTokenServices.createAccessToken(auth);
The only problem is that I am not sure how to create the OAuth2Authentication (in my code the part with xxx). I have the user & client info and I know which Authorities I want to grant this token.
回答1:
Here it is, your use case may differ slightly based on the flow you are using. This is what works for a password grant flow. There are a few custom class like token store, token enhancer ect. but that is really just extended versions of the spring classes modified for our own needs.
HashMap<String, String> authorizationParameters = new HashMap<String, String>();
authorizationParameters.put("scope", "read");
authorizationParameters.put("username", "mobile_client");
authorizationParameters.put("client_id", "mobile-client");
authorizationParameters.put("grant", "password");
DefaultAuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest(authorizationParameters);
authorizationRequest.setApproved(true);
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_UNTRUSTED_CLIENT"));
authorizationRequest.setAuthorities(authorities);
HashSet<String> resourceIds = new HashSet<String>();
resourceIds.add("mobile-public");
authorizationRequest.setResourceIds(resourceIds);
// Create principal and auth token
User userPrincipal = new User(user.getUserID(), "", true, true, true, true, authorities);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities) ;
OAuth2Authentication authenticationRequest = new OAuth2Authentication(authorizationRequest, authenticationToken);
authenticationRequest.setAuthenticated(true);
CustomTokenStore tokenStore = new CustomTokenStore();
// Token Enhancer
CustomTokenEnhancer tokenEnhancer = new CustomTokenEnhancer(user.getUserID());
CustomTokenServices tokenServices = new CustomTokenServices();
tokenServices.setTokenEnhancer(tokenEnhancer);
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
OAuth2AccessToken accessToken = tokenServices.createAccessTokenForUser(authenticationRequest, user);
回答2:
Here is how to generate a Token using the TokenEndpoint interface (used to expose REST service) :
@Inject
private TokenEndpoint tokenEndpoint;
public ResponseEntity<?> getToken(Principal principal) {
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("client_id", "appid");
parameters.put("client_secret", "myOAuthSecret");
parameters.put("grant_type", "password");
parameters.put("password", myUser.getPassword());
parameters.put("scope", "read write");
parameters.put("username", myUser.getLogin());
return tokenEndpoint.getAccessToken(principal, parameters);
}
回答3:
Other way, to manually generate an OAuth2 Accesss Token
we can use an instance of TokenService
@Autowired
private AuthorizationServerEndpointsConfiguration configuration;
@Override
public String generateOAuth2AccessToken(User user, List<Role> roles, List<String> scopes) {
Map<String, String> requestParameters = new HashMap<String, String>();
Map<String, Serializable> extensionProperties = new HashMap<String, Serializable>();
boolean approved = true;
Set<String> responseTypes = new HashSet<String>();
responseTypes.add("code");
// Authorities
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for(Role role: roles)
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
OAuth2Request oauth2Request = new OAuth2Request(requestParameters, "clientIdTest", authorities, approved, new HashSet<String>(scopes), new HashSet<String>(Arrays.asList("resourceIdTest")), null, responseTypes, extensionProperties);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), "N/A", authorities);
OAuth2Authentication auth = new OAuth2Authentication(oauth2Request, authenticationToken);
AuthorizationServerTokenServices tokenService = configuration.getEndpointsConfigurer().getTokenServices();
OAuth2AccessToken token = tokenService.createAccessToken(auth);
return token.getValue();
}
回答4:
This has worked for me:
@Override public OAuth2AccessToken getToken(String username, String password) {
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("client_id", clientid);
parameters.put("grant_type", "password");
parameters.put("password", username);
parameters.put("scope", scope);
parameters.put("username", password);
AuthorizationRequest authorizationRequest = defaultOAuth2RequestFactory.createAuthorizationRequest(parameters);
authorizationRequest.setApproved(true);
OAuth2Request oauth2Request = defaultOAuth2RequestFactory.createOAuth2Request(authorizationRequest);
// Create principal and auth token
final UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken(
username, password);
Authentication authentication = authenticationManager.authenticate(loginToken);
OAuth2Authentication authenticationRequest = new OAuth2Authentication(oauth2Request, authentication);
authenticationRequest.setAuthenticated(true);
OAuth2AccessToken accessToken = tokenServices.createAccessToken(authenticationRequest);
return accessToken;
}
In the Oauth2Configuration:
@Bean
DefaultOAuth2RequestFactory defaultOAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService);
}
The rest of the Oauth2Configuration should look like in the article:
http://stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/
回答5:
I based my solution on Mop So's answer but instead of using:
return tokenEndpoint.getAccessToken(principal, parameters);
I used:
tokenEndpoint.postAccessToken(principal, parameters);
Why? Because if you use tokenEndpoint.getAccessToken(principal, parameters)
the endpoing will throw you a HttpRequestMethodNotSupportedException
because it has not been called with a GET
method. At least, this is what happened to me with spring-security-oauth2-2.0.13.RELEASE
public OAuth2AccessToken getAccessToken() throws HttpRequestMethodNotSupportedException {
HashMap<String, String> parameters = new HashMap<>();
parameters.put("client_id", CLIENT_ID);
parameters.put("client_secret", CLIENT_SECRET);
parameters.put("grant_type", "client_credentials");
ClientDetails clientDetails = clientDetailsStore.get(CLIENT_ID);
// Create principal and auth token
User userPrincipal = new User(CLIENT_ID, CLIENT_SECRET, true, true, true, true, clientDetails.getAuthorities());
UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken(userPrincipal, CLIENT_SECRET,
clientDetails.getAuthorities());
ResponseEntity<OAuth2AccessToken> accessToken = tokenEndpoint.postAccessToken(principal, parameters);
return accessToken.getBody();
}
回答6:
Problem
I had problems with all the implementations listed here, so I finally managed to get my own with a stateless server, oauth2 and google social. Its just the last part of the tutorial that is missing here
The problem for me is that after executing the google oauth, I need to exchange a 10 second duration token for a long lived token. In order to do that I need to generate a JWT token and exchange it with a real access token generated by myself.
Implementation
@Service
class SocialTokenVerificationService {
@Autowired
private lateinit var jwsTokenService: JWSTokenService
@Autowired
private lateinit var clientDetailsService: ClientDetailsService
@Autowired
private lateinit var userService: UserService
@Autowired
private lateinit var tokenServices: DefaultTokenServices
@Autowired
private lateinit var tokenRequestFactory: OAuth2RequestFactory
fun verifyToken(token: String): OAuth2AccessToken? {
val claimSet = jwsTokenService.parseToken(token)
val userDetails = userService.loadUserByUsername(claimSet.subject)
val client = clientDetailsService.loadClientByClientId(DEFAULT_SERVER_CLIENT)
val parameters = HashMap<String, String>()
val authentication = UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities)
return tokenServices.createAccessToken(OAuth2Authentication(
tokenRequestFactory.createOAuth2Request(client, TokenRequest(parameters, client.clientId, listOf("read", "write"), "password")),
authentication
))
}
}
JWSTokenService
: its a self implemented class that encodes and decodes the exchanging token between google oauth and mine.
ClientDetailsService
: bean declared as as part of the authorization server. Comes from my database
override fun configure(clients: ClientDetailsServiceConfigurer) {
clients.jdbc(datasource)
}
UserService
: just a user service that extends UserDetailsService
to obtain my users from the database
DefaultTokenServices
: implemented as a primary bean as follows
@Bean
@Primary
fun tokenServices(): DefaultTokenServices {
val defaultTokenServices = DefaultTokenServices()
defaultTokenServices.setTokenStore(tokenStore())
defaultTokenServices.setSupportRefreshToken(true)
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter())
return defaultTokenServices
}
OAuth2RequestFactory
: implemented as a bean as follows
@Bean
fun oauthRequestFactory(clientsDetails: ClientDetailsService): OAuth2RequestFactory {
return DefaultOAuth2RequestFactory(clientsDetails)
}
With all this dependencies, what I need to do to generate a token that gets stored into the database and follows the same flows as the other ones without providing a password is:
- Parse the jws token and verify its validity
- Load the user that was authenticated with google
- Generate an
Authentication
using the UsernamePasswordAuthenticationToken
class. This is the key part, call DefaultTokenServices#createAccessToken
to obtain a new token. It needs some arguments to execute the request:
OAuth2Request
: it can be created with the OAuth2RequestFactory
- The
Authentication
created previously
- We need to generate a
TokenRequest
with the client that is triggering this token request. In my case I have that hardcoded
Summary
So to recap how to create a token manually:
- We need to ask the token services to give us a token
- For that we need to provide the authentication details and a client who does the request
- With those 2 we can obtain a new token and serve it normally