복붙노트

[SPRING] OAuth2RestTemplate을 사용하는 방법?

SPRING

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 API에 연결할 수 있습니다.

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

이제 내 질문은이 OAuth2 보안 REST API를 소비 할 수있는 두 번째 응용 프로그램을 어떻게 구현합니까? 사용자 이름과 비밀번호 (예 : 로그인 양식)를 입력하고 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);

하지만 이것은 단지 작동하지 않습니다 : (

어떤 아이디어라도 크게 높이 평가되거나 작업 예제 및 자습서에 대한 링크가 크게 제공됩니다.

해결법

  1. ==============================

    1.Oauth 클라이언트를 작성하기위한 예제는 https://github.com/spring-projects/spring-security-oauth에서 찾을 수 있습니다.

    Oauth 클라이언트를 작성하기위한 예제는 https://github.com/spring-projects/spring-security-oauth에서 찾을 수 있습니다.

    귀하의 경우에는 기본 클래스 또는 기본 클래스를 모든 것에 사용할 수 없으며 OAuth2ProtectedResourceDetails 구현의 여러 클래스가 있습니다. 구성은 Oauth 서비스를 어떻게 구성했는지에 따라 다르지만 컬 연결을 가정 할 때 다음과 같이 권합니다.

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

    설정 클래스에서 @ EnableOAuth2Client를 잊지 마세요. 또한 여러분이 사용하고있는 url이 가장 먼저 컬하고, 디버거로 추적하려고 시도 할 것을 제안합니다. 많은 예외가 단지 소비되고 적절한 보안을 인쇄하지 않기 때문입니다. 이유 때문에 문제가있는 곳을 찾기가 거의 어렵습니다. 디버그가 활성화 된 로거를 사용해야합니다. 행운을 빕니다

    github https://github.com/mariubog/oauth-client-sample에 샘플 springboot 앱을 업로드했습니다. 시나리오에 맞는 샘플을 찾을 수 없어 상황을 묘사 할 수 있습니다.

  2. ==============================

    2.나는 당신이 접근 토큰을 원한다면 다른 접근법을 가지고 있으며 헤더에있는 접근 토큰을 가진 다른 자원 시스템을 호출한다.

    나는 당신이 접근 토큰을 원한다면 다른 접근법을 가지고 있으며 헤더에있는 접근 토큰을 가진 다른 자원 시스템을 호출한다.

    Spring Security에는 자동 보안 기능이 있습니다. oauth2 속성은 모든 요청에 ​​대해 application.yml 파일에서 액세스하며 모든 요청에는 Principal을 통해 사용자 정보를 읽고 끌어 오는 SESSIONID가 있으므로 OAuthUser에 Principal을 주입하고 accessToken을 가져 와서 호출해야합니다. 리소스 서버

    이것은 귀하의 인증 서버에 따라 귀하의 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. ==============================

    3.@mariubog (https://stackoverflow.com/a/27882337/1279002)의 대답에서 예제와 같이 암호 부여 유형을 사용하고 있었지만 클라이언트 인증 체계를 설정해야했습니다. 범위는 암호 용 끝점에서 지원되지 않으므로 ResourceOwnerPasswordResourceDetails 개체가 생성자에서이 자체를 설정하므로 부여 유형을 설정할 필요가 없습니다.

    @mariubog (https://stackoverflow.com/a/27882337/1279002)의 대답에서 예제와 같이 암호 부여 유형을 사용하고 있었지만 클라이언트 인증 체계를 설정해야했습니다. 범위는 암호 용 끝점에서 지원되지 않으므로 ResourceOwnerPasswordResourceDetails 개체가 생성자에서이 자체를 설정하므로 부여 유형을 설정할 필요가 없습니다.

    ...

    public ResourceOwnerPasswordResourceDetails() {
        setGrantType("password");
    }
    

    ...

    내게 가장 중요한 점은 client_id와 client_secret이 resource.setClientAuthenticationScheme (AuthenticationScheme.form) 인 경우 본문에 게시 할 양식 객체에 추가되지 않았다는 것입니다. 설정되지 않았습니다.

    다음 위치에서 스위치를 참조하십시오.      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();
        }
    }
    
  4. from https://stackoverflow.com/questions/27864295/how-to-use-oauth2resttemplate by cc-by-sa and MIT license