Java Spring OAuth2 - 在令牌存储中手动创建访问令牌
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18536521/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring OAuth2 - Manually creating an access token in the token store
提问by checklist
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.
唯一的问题是我不确定如何创建 OAuth2Authentication(在我的代码中带有 xxx 的部分)。我有用户和客户信息,我知道我想授予这个令牌的机构。
采纳答案by Michael
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.
在这里,您的用例可能会根据您使用的流程略有不同。这适用于密码授予流程。有一些自定义类,如令牌存储、令牌增强器等。但这实际上只是根据我们自己的需要修改的 spring 类的扩展版本。
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);
回答by Mop So
Here is how to generate a Token using the TokenEndpoint interface (used to expose REST service) :
以下是使用 TokenEndpoint 接口(用于公开 REST 服务)生成 Token 的方法:
@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);
}
回答by Duc Huy Ta
Other way, to manually generate an OAuth2 Accesss Token
we can use an instance of TokenService
其他方式,手动生成一个OAuth2 Accesss Token
我们可以使用一个实例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();
}
回答by Laci
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:
在 Oauth2Configuration 中:
@Bean
DefaultOAuth2RequestFactory defaultOAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService);
}
The rest of the Oauth2Configuration should look like in the article:
Oauth2Configuration 的其余部分应如下文所示:
http://stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/
http://stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/
回答by flags
I based my solution on Mop So's answer but instead of using:
我的解决方案基于 Mop So 的答案,但没有使用:
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
为什么?因为如果你使用tokenEndpoint.getAccessToken(principal, parameters)
endpoing 会抛出一个,HttpRequestMethodNotSupportedException
因为它没有被GET
方法调用。至少,这是发生在我身上的事情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();
}
回答by droidpl
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
我在这里列出的所有实现都遇到了问题,所以我终于设法通过无状态服务器、oauth2 和 google social 获得了自己的实现。它只是这里缺少的教程的最后一部分
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.
我的问题是,在执行 google oauth 后,我需要将一个 10 秒持续时间的令牌交换为一个长期存在的令牌。为此,我需要生成一个 JWT 令牌并将其与我自己生成的真实访问令牌进行交换。
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 databaseoverride fun configure(clients: ClientDetailsServiceConfigurer) { clients.jdbc(datasource) }
UserService
: just a user service that extendsUserDetailsService
to obtain my users from the databaseDefaultTokenServices
: 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) }
JWSTokenService
:它是一个自我实现的类,用于编码和解码 google oauth 和我的之间的交换令牌。ClientDetailsService
: bean 声明为授权服务器的一部分。来自我的数据库覆盖乐趣配置(客户端:ClientDetailsServiceConfigurer){clients.jdbc(数据源)}
UserService
: 只是一个用户服务,扩展UserDetailsService
到从数据库中获取我的用户DefaultTokenServices
: 作为主 bean 实现如下@Bean @Primary fun tokenServices(): DefaultTokenServices { val defaultTokenServices = DefaultTokenServices() defaultTokenServices.setTokenStore(tokenStore()) defaultTokenServices.setSupportRefreshToken(true) defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter()) return defaultTokenServices }
OAuth2RequestFactory
:作为bean实现如下@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 theUsernamePasswordAuthenticationToken
class. This is the key part, callDefaultTokenServices#createAccessToken
to obtain a new token. It needs some arguments to execute the request:OAuth2Request
: it can be created with theOAuth2RequestFactory
- 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
- 解析 jws 令牌并验证其有效性
- 加载通过谷歌认证的用户
Authentication
使用UsernamePasswordAuthenticationToken
类生成一个。这是关键部分,调用DefaultTokenServices#createAccessToken
获取新令牌。它需要一些参数来执行请求:OAuth2Request
: 它可以用OAuth2RequestFactory
- 在
Authentication
先前创建 - 我们需要
TokenRequest
与触发此令牌请求的客户端生成一个。在我的情况下,我有那个硬编码
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
- 我们需要要求令牌服务给我们一个令牌
- 为此,我们需要提供身份验证详细信息和执行请求的客户端
- 有了这 2 个,我们可以获得一个新的令牌并正常提供它
回答by revau.lt
In a spring boot 2.2.2 project I'm using the following code to do a pasword flow server side:
I had to specify authorizedClientManager.setContextAttributesMapper
since PasswordOAuth2AuthorizedClientProvider
is expecting specific attributes in the context. Hope that helps.
在 spring boot 2.2.2 项目中,我使用以下代码来执行密码流服务器端:我必须指定authorizedClientManager.setContextAttributesMapper
因为PasswordOAuth2AuthorizedClientProvider
期望上下文中的特定属性。希望有帮助。
Config (application.yaml):
配置(application.yaml):
spring:
security:
oauth2:
client:
provider:
yourOauthProvider:
user-info-uri: ...
authorization-uri: ...
token-uri: ...
registration:
regId:
clientId: ...
clientSecret: ...
provider: yourOauthProvider
authorization-grant-type: password
redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"
scope:
Wiring:
接线:
@Configuration
public class Oauth2ClientConfig {
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.password()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
authorizedClientManager.setContextAttributesMapper(r -> {
Map<String, Object> m = new HashMap<>();
m.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, r.getPrincipal().getPrincipal());
m.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, r.getPrincipal().getCredentials());
return m;
});
return authorizedClientManager;
}
}
Service:
服务:
class AuthService {
@Autowired
private OAuth2AuthorizedClientManager authorizedClientManager;
public OAuth2AccessToken authenticate(String user, String password) {
Authentication principal = new UsernamePasswordAuthenticationToken(
user,
password);
OAuth2AuthorizeRequest authorizeRequest =
OAuth2AuthorizeRequest.withClientRegistrationId("regId")
.principal(principal)
.build();
OAuth2AuthorizedClient authorizedClient =
this.authorizedClientManager.authorize(authorizeRequest);
return authorizedClient.getAccessToken();
}
}