自分で(通常のプロセスではなく)アクセストークンを作成したい状況があります。私はこのようなものを考え出しました:
@Inject
private DefaultTokenServices defaultTokenServices;
...
OAuth2Authentication auth = xxx;
OAuth2AccessToken token = defaultTokenServices.createAccessToken(auth);
唯一の問題は、OAuth2Authenticationの作成方法がわからないことです(私のコードではxxxの部分)。ユーザーとクライアントの情報があり、このトークンを付与する機関を知っています。
ここでは、使用しているフローに基づいてユースケースが若干異なる場合があります。これは、パスワード付与フローで機能します。トークンストア、トークンエンハンサーなどのカスタムクラスがいくつかあります。しかし、それは実際には、独自のニーズに合わせて変更されたスプリングクラスの拡張バージョンにすぎません。
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);
TokenEndpointインターフェースを使用してトークンを生成する方法を次に示します(RESTサービスを公開するために使用):
@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);
}
別の方法として、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();
}
私のソリューションはモップソーの答えに基づいていますが、代わりに以下を使用します:
_return tokenEndpoint.getAccessToken(principal, parameters);
_
私が使用した:
_tokenEndpoint.postAccessToken(principal, parameters);
_
どうして? tokenEndpoint.getAccessToken(principal, parameters)
を使用すると、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();
}
_
これは私のために働きました:
@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;
}
Oauth2Configuration:
@Bean
DefaultOAuth2RequestFactory defaultOAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService);
}
Oauth2Configurationの残りの部分は、記事のようになります。
http://stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/
ここにリストされているすべての実装に問題があったため、ようやくステートレスサーバー、oauth2、およびgoogle socialを使用して独自に実装できました。チュートリアルの最後の欠けている部分 here
私の問題は、google oauthを実行した後、10秒の持続時間トークンを長期間トークンに交換する必要があることです。そのためには、JWTトークンを生成し、それを自分で生成した実際のアクセストークンと交換する必要があります。
@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
:google oauthとmineの間の交換トークンをエンコードおよびデコードする自己実装クラスです。ClientDetailsService
:認可サーバーの一部として宣言されたBean。私のデータベースからのもの
オーバーライドfun configure(clients:ClientDetailsServiceConfigurer){clients.jdbc(datasource)}
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)
}
このすべての依存関係がある場合、データベースに格納され、パスワードを指定せずに他のフローと同じフローに従うトークンを生成するために私がする必要があるのは、次のとおりです。
Authentication
クラスを使用してUsernamePasswordAuthenticationToken
を生成します。これは重要な部分です。DefaultTokenServices#createAccessToken
を呼び出して新しいトークンを取得します。リクエストを実行するには、いくつかの引数が必要です。OAuth2Request
:OAuth2RequestFactory
で作成できますAuthentication
TokenRequest
を生成する必要があります。私の場合、それはハードコードされていますしたがって、トークンを手動で作成する方法を要約すると:
スプリングブート2.2.2プロジェクトでは、次のコードを使用してパスワードフローのサーバー側を実行しています。authorizedClientManager.setContextAttributesMapper
はコンテキスト内の特定の属性を想定しているため、PasswordOAuth2AuthorizedClientProvider
を指定する必要がありました。お役に立てば幸いです。
構成(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:
配線:
@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;
}
}
サービス:
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();
}
}