web-dev-qa-db-ja.com

OAuth2RestTemplateの使用方法

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 AP​​Iに接続できます

http://localhost:8082/app/helloworld/?access_token=**4855f557-c6ee-43b7-8617-c24591965206**

ここで私の質問は、このOAuth2で保護されたREST AP​​Iを使用できる2番目のアプリケーションをどのように実装するかです。ユーザー名とパスワード(ログインフォームから取得するなど)を入力すると、REST AP​​Iからデータを取得するために再利用できるトークンが生成される実際の例は見つかりませんでした。 。

私は現在、次のオブジェクトで何かを試しました:

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);

しかし、これは機能していません:(

どんなアイデアでも大歓迎、または実例やチュートリアルへのリンクは大歓迎です。

36
Joachim Seminck

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アプリをアップロードしました。

40
mariubog

@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();
    }
}
3
theINtoy

アクセストークンが必要で、ヘッダーにアクセストークンを使用して他のリソースシステムを呼び出す場合、別のアプローチがあります

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


}
3

私の簡単な解決策。私見それは最もきれいです。

最初に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>
0
Koroslak