OAuth2RestTemplateオブジェクトを使用して、OAuth2で保護されたRESTサービス(別のプロジェクトで実行され、別のサーバーなどでも想定しています)を使用する方法を理解しようとしています。
f.e.私の休息サービスは:
http://localhost:8082/app/helloworld
->認証されていないため、このURLにアクセスするとエラーが発生します
トークンを要求するには、次の場所に移動します。
http://localhost:8082/app/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=**USERNAME**&password=**PASSWORD**
トークンを受け取った後、次のURL(トークンの挿入例)を使用してREST APIに接続できます
http://localhost:8082/app/helloworld/?access_token=**4855f557-c6ee-43b7-8617-c24591965206**
ここで私の質問は、このOAuth2で保護されたREST APIを使用できる2番目のアプリケーションをどのように実装するかです。ユーザー名とパスワード(ログインフォームから取得するなど)を入力すると、REST APIからデータを取得するために再利用できるトークンが生成される実際の例は見つかりませんでした。 。
私は現在、次のオブジェクトで何かを試しました:
BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails = new BaseOAuth2ProtectedResourceDetails();
baseOAuth2ProtectedResourceDetails.setClientId("restapp");
baseOAuth2ProtectedResourceDetails.setClientSecret("restapp");
baseOAuth2ProtectedResourceDetails.setGrantType("password");
// how to set user name and password ???
DefaultAccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
OAuth2ClientContext oAuth2ClientContext = new DefaultOAuth2ClientContext(accessTokenRequest());
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(baseOAuth2ProtectedResourceDetails,oAuth2ClientContext);
しかし、これは機能していません:(
どんなアイデアでも大歓迎、または実例やチュートリアルへのリンクは大歓迎です。
Oauthクライアントを記述するための例題はここにあります https://github.com/spring-projects/spring-security-oauth
あなたの場合、すべてにデフォルトまたは基本クラスを使用することはできません。OAuth2ProtectedResourceDetailsを実装する複数のクラスがあります。構成はOauthサービスをどのように構成したかによって異なりますが、curl接続からお勧めします:
@EnableOAuth2Client
@Configuration
class MyConfig{
@Value("${oauth.resource:http://localhost:8082}")
private String baseUrl;
@Value("${oauth.authorize:http://localhost:8082/oauth/authorize}")
private String authorizeUrl;
@Value("${oauth.token:http://localhost:8082/oauth/token}")
private String tokenUrl
@Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
List scopes = new ArrayList<String>(2);
scopes.add("write");
scopes.add("read");
resource.setAccessTokenUri(tokenUrl);
resource.setClientId("restapp");
resource.setClientSecret("restapp");
resource.setGrantType("password");
resource.setScope(scopes);
resource.setUsername("**USERNAME**");
resource.setPassword("**PASSWORD**");
return resource;
}
@Bean
public OAuth2RestOperations restTemplate() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(atr));
}
}
@Service
@SuppressWarnings("unchecked")
class MyService {
@Autowired
private OAuth2RestOperations restTemplate;
public MyService() {
restTemplate.getAccessToken();
}
}
Configクラスの@ EnableOAuth2Clientを忘れないでください。また、使用しているURLが最初にcurlで動作していることを確認することをお勧めします。理由のため、問題がどこにあるかを見つけるのは少し難しくなります。デバッグを有効に設定してロガーを使用する必要があります。幸運を
シナリオのサンプルが見つからなかったため、状況を表すために、github https://github.com/mariubog/oauth-client-sample にサンプルのspringbootアプリをアップロードしました。
@mariubogからの回答( https://stackoverflow.com/a/27882337/1279002 )例のようにパスワード付与タイプも使用していましたが、クライアント認証スキームを設定する必要がありました。スコープはパスワードのエンドポイントでサポートされておらず、ResourceOwnerPasswordResourceDetailsオブジェクトがコンストラクターでこれ自体を設定するため、付与タイプを設定する必要はありませんでした。
...
public ResourceOwnerPasswordResourceDetails() {
setGrantType("password");
}
...
私にとって重要なことは、resource.setClientAuthenticationScheme(AuthenticationScheme.form);
が設定されていなかった場合、body_postとclient_secretが本文に投稿するためにフォームオブジェクトに追加されていなかったことです。
次のスイッチを参照してください:org.springframework.security.oauth2.client.token.auth.DefaultClientAuthenticationHandler.authenticateTokenRequest()
最後に、Salesforceエンドポイントに接続するとき、パスワードにパスワードトークンを追加する必要がありました。
@EnableOAuth2Client
@Configuration
class MyConfig {
@Value("${security.oauth2.client.access-token-uri}")
private String tokenUrl;
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String clientSecret;
@Value("${security.oauth2.client.password-token}")
private String passwordToken;
@Value("${security.user.name}")
private String username;
@Value("${security.user.password}")
private String password;
@Bean
protected OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setAccessTokenUri(tokenUrl);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
resource.setUsername(username);
resource.setPassword(password + passwordToken);
return resource;
}
@Bean
public OAuth2RestOperations restTemplate() {
return new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
}
}
@Service
@SuppressWarnings("unchecked")
class MyService {
@Autowired
private OAuth2RestOperations restTemplate;
public MyService() {
restTemplate.getAccessToken();
}
}
アクセストークンが必要で、ヘッダーにアクセストークンを使用して他のリソースシステムを呼び出す場合、別のアプローチがあります
Spring Securityには自動セキュリティが付属しています:oauth2プロパティはすべてのリクエストのapplication.ymlファイルからアクセスし、すべてのリクエストにはSESSIONIDがあり、プリンシパルを介してユーザー情報を読み取り、プルしますリソースサーバー
これはapplication.ymlで、認証サーバーに応じて変更します:
security:
oauth2:
client:
clientId: 233668646673605
clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
@Component
public class OAuthUser implements Serializable {
private static final long serialVersionUID = 1L;
private String authority;
@JsonIgnore
private String clientId;
@JsonIgnore
private String grantType;
private boolean isAuthenticated;
private Map<String, Object> userDetail = new LinkedHashMap<String, Object>();
@JsonIgnore
private String sessionId;
@JsonIgnore
private String tokenType;
@JsonIgnore
private String accessToken;
@JsonIgnore
private Principal principal;
public void setOAuthUser(Principal principal) {
this.principal = principal;
init();
}
public Principal getPrincipal() {
return principal;
}
private void init() {
if (principal != null) {
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal;
if (oAuth2Authentication != null) {
for (GrantedAuthority ga : oAuth2Authentication.getAuthorities()) {
setAuthority(ga.getAuthority());
}
setClientId(oAuth2Authentication.getOAuth2Request().getClientId());
setGrantType(oAuth2Authentication.getOAuth2Request().getGrantType());
setAuthenticated(oAuth2Authentication.getUserAuthentication().isAuthenticated());
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) oAuth2Authentication
.getDetails();
if (oAuth2AuthenticationDetails != null) {
setSessionId(oAuth2AuthenticationDetails.getSessionId());
setTokenType(oAuth2AuthenticationDetails.getTokenType());
// This is what you will be looking for
setAccessToken(oAuth2AuthenticationDetails.getTokenValue());
}
// This detail is more related to Logged-in User
UsernamePasswordAuthenticationToken userAuthenticationToken = (UsernamePasswordAuthenticationToken) oAuth2Authentication.getUserAuthentication();
if (userAuthenticationToken != null) {
LinkedHashMap<String, Object> detailMap = (LinkedHashMap<String, Object>) userAuthenticationToken.getDetails();
if (detailMap != null) {
for (Map.Entry<String, Object> mapEntry : detailMap.entrySet()) {
//System.out.println("#### detail Key = " + mapEntry.getKey());
//System.out.println("#### detail Value = " + mapEntry.getValue());
getUserDetail().put(mapEntry.getKey(), mapEntry.getValue());
}
}
}
}
}
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public boolean isAuthenticated() {
return isAuthenticated;
}
public void setAuthenticated(boolean isAuthenticated) {
this.isAuthenticated = isAuthenticated;
}
public Map<String, Object> getUserDetail() {
return userDetail;
}
public void setUserDetail(Map<String, Object> userDetail) {
this.userDetail = userDetail;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
@Override
public String toString() {
return "OAuthUser [clientId=" + clientId + ", grantType=" + grantType + ", isAuthenticated=" + isAuthenticated
+ ", userDetail=" + userDetail + ", sessionId=" + sessionId + ", tokenType="
+ tokenType + ", accessToken= " + accessToken + " ]";
}
@RestController
public class YourController {
@Autowired
OAuthUser oAuthUser;
// In case if you want to see Profile of user then you this
@RequestMapping(value = "/profile", produces = MediaType.APPLICATION_JSON_VALUE)
public OAuthUser user(Principal principal) {
oAuthUser.setOAuthUser(principal);
// System.out.println("#### Inside user() - oAuthUser.toString() = " + oAuthUser.toString());
return oAuthUser;
}
@RequestMapping(value = "/createOrder",
method = RequestMethod.POST,
headers = {"Content-type=application/json"},
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public FinalOrderDetail createOrder(@RequestBody CreateOrder createOrder) {
return postCreateOrder_restTemplate(createOrder, oAuthUser).getBody();
}
private ResponseEntity<String> postCreateOrder_restTemplate(CreateOrder createOrder, OAuthUser oAuthUser) {
String url_POST = "your post url goes here";
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Authorization", String.format("%s %s", oAuthUser.getTokenType(), oAuthUser.getAccessToken()));
headers.add("Content-Type", "application/json");
RestTemplate restTemplate = new RestTemplate();
//restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<String> request = new HttpEntity<String>(createOrder, headers);
ResponseEntity<String> result = restTemplate.exchange(url_POST, HttpMethod.POST, request, String.class);
System.out.println("#### post response = " + result);
return result;
}
}
私の簡単な解決策。私見それは最もきれいです。
最初にapplication.ymlを作成します
spring.main.allow-bean-definition-overriding: true
security:
oauth2:
client:
clientId: XXX
clientSecret: XXX
accessTokenUri: XXX
tokenName: access_token
grant-type: client_credentials
メインクラスを作成します。Main
@SpringBootApplication
@EnableOAuth2Client
public class Main extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll();
}
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
@Bean
public OAuth2RestTemplate oauth2RestTemplate(ClientCredentialsResourceDetails details) {
return new OAuth2RestTemplate(details);
}
}
次に、コントローラークラスを作成します。Controller
@RestController
class OfferController {
@Autowired
private OAuth2RestOperations restOperations;
@RequestMapping(value = "/<your url>"
, method = RequestMethod.GET
, produces = "application/json")
public String foo() {
ResponseEntity<String> responseEntity = restOperations.getForEntity(<the url you want to call on the server>, String.class);
return responseEntity.getBody();
}
}
Mavenの依存関係
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
</dependencies>