2017-08-22 15 views
0

OAuth 1.0を使用して一部のリソースを保護する外部パートナーがあります。私はこのリソースにアクセスする必要があり、私はSpring BootとSpring Security OAuthを使用してこれを行いたいと思います。私はXML設定を使いたくないので、Java設定ですべてを設定する方法を探しました。これを行う方法の例を示した0​​スレッドが見つかりました。しかし、OAuth 1.0のフローに関するサーバー側のことは私にとっては明らかではありません。SpringセキュリティOAuth 1.0フロー - コンシューマ検証

私のパートナーは、消費者トークン、request_tokenエンドポイント、承認エンドポイント、access_tokenエンドポイントを提供するエンドポイント、OAuthに4つのエンドポイントを提供します。私の現在の設定(下記参照)では、リクエストトークンを取得でき、認証エンドポイントが呼び出されます。しかし、認可エンドポイントは、確認を求めますが、URLをメールアドレスとパスワードをパラメータとして期待すると、資格情報をチェックした後、次を返していません:

oauth_verifier=a02ebdc5433242e2b6e582e17b84e313 

をそしてOAuthのフローが立ち往生場所です。通常の流れのOAuth 1.0に関するいくつかの記事を読んだ後

はこれです:

  1. キー消費者のトークンは/承認のURLにリダイレクトすると尋ねるrequest_tokenエンドポイント
  2. を経由して、消費者のトークンを使用してOAuthのトーク​​ンゲット確認のためのユーザ
  3. 検証者トークンを使用してコンシューマにリダイレクト
  4. 経由でアクセストークンを取得するためのユーザ検証ツールトークンとoauthトークンエンドポイント

まず、手順3と4はわかりません。私はSpring Security OAuth examplesを見つけましたが、アクセスを確認した後、ユーザー/検証者のトークンが消費者に返送される方法はわかりませんでした。誰かがこれがどのように行われたか説明してもらえますか?

2番目:パートナーのエンドポイントが確認を求めずに直ちにoauthベリファイアを返すことを考えれば、この設定でSpring Security OAuthをどのように使用できますか?私は、パートナーの認証エンドポイントを呼び出して何らかの形で私の消費者に検証者を知らせる独自の認証エンドポイントを実装することを考えていましたが、後者の方法をどうやって行うのか分かりません。ここ

コードは、(上述したスレッドの助けを借りて、ConsumerTokenDtoは、それが自明であるようにして残されている)、これまでのところです。

アプリケーション

@SpringBootApplication 
public class Application { 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

エンドポイント

@RestController 
public class Endpoint { 
    @Autowired 
    private OAuthRestTemplate oAuthRestTemplate; 
    private String url = "https://...."; 

    @RequestMapping("/public/v1/meters") 
    public String getMeters() { 
     try { 
      return oAuthRestTemplate.getForObject(URI.create(url), String.class); 
     } catch (Exception e) { 
      LOG.error("Exception", e); 
      return ""; 
     } 
    } 
} 

OAuth configura

@Configuration 
@EnableWebSecurity 
public class OAuthConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private RestTemplateBuilder restTemplateBuilder; 
    private ConsumerTokenDto consumerTokenDto; 

    private static final String ID = "meters"; 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.authorizeRequests().antMatchers("/**").permitAll(); 
     http.addFilterAfter(this.oauthConsumerContextFilter(), SwitchUserFilter.class); 
     http.addFilterAfter(this.oauthConsumerProcessingFilter(), OAuthConsumerContextFilterImpl.class); 
    } 

    private OAuthConsumerContextFilter oauthConsumerContextFilter() { 
     OAuthConsumerContextFilter filter = new OAuthConsumerContextFilter(); 
     filter.setConsumerSupport(this.consumerSupport()); 
     return filter; 
    } 

    private OAuthConsumerProcessingFilter oauthConsumerProcessingFilter() { 
     OAuthConsumerProcessingFilter filter = new OAuthConsumerProcessingFilter(); 
     filter.setProtectedResourceDetailsService(this.prds()); 

     LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<>(); 

     // one entry per oauth:url element in xml 
     map.put(
       new AntPathRequestMatcher("/public/v1/**", null), 
       Collections.singletonList(new SecurityConfig(ID))); 

     filter.setObjectDefinitionSource(new DefaultFilterInvocationSecurityMetadataSource(map)); 

     return filter; 
    } 

    @Bean 
    OAuthConsumerSupport consumerSupport() { 
     CoreOAuthConsumerSupport consumerSupport = new CoreOAuthConsumerSupport(); 
     consumerSupport.setProtectedResourceDetailsService(prds()); 
     return consumerSupport; 
    } 

    @Bean 
    ProtectedResourceDetailsService prds() { 
     InMemoryProtectedResourceDetailsService service = new InMemoryProtectedResourceDetailsService(); 
     Map<String, ProtectedResourceDetails> store = new HashMap<>(); 
     store.put(ID, prd()); 
     service.setResourceDetailsStore(store); 
     return service; 
    } 

    ProtectedResourceDetails prd() { 
     ConsumerTokenDto consumerToken = getConsumerToken(); 
     BaseProtectedResourceDetails resourceDetails = new BaseProtectedResourceDetails(); 
     resourceDetails.setId(ID); 
     resourceDetails.setConsumerKey(consumerToken.getKey()); 
     resourceDetails.setSharedSecret(new SharedConsumerSecretImpl(consumerToken.getSecret())); 
     resourceDetails.setRequestTokenURL("https://.../request_token"); 
     // the authorization URL does not prompt for confirmation but immediately returns an OAuth verifier 
     resourceDetails.setUserAuthorizationURL(
       "https://.../authorize?email=mail&password=pw"); 
     resourceDetails.setAccessTokenURL("https://.../access_token"); 
     resourceDetails.setSignatureMethod(HMAC_SHA1SignatureMethod.SIGNATURE_NAME); 
     return resourceDetails; 
    } 

    // get consumer token from provider 
    private ConsumerTokenDto getConsumerToken() { 
     if (consumerTokenDto == null) { 
      MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); 
      body.add("client", "Client"); 

      HttpHeaders headers = new HttpHeaders(); 
      headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 

      HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers); 

      RestTemplate restTemplate = restTemplateBuilder.setConnectTimeout(1000).setReadTimeout(1000).build(); 
      restTemplate.getInterceptors().add(interceptor); 
      restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())); 
      ResponseEntity<ConsumerTokenDto> response = restTemplate 
        .exchange("https://.../consumer_token", HttpMethod.POST, request, 
          ConsumerTokenDto.class); 

      consumerTokenDto = response.getBody(); 
     } 
     return consumerTokenDto; 
    } 

    // create oauth rest template 
    @Bean 
    public OAuthRestTemplate oAuthRestTemplate() { 
     OAuthRestTemplate oAuthRestTemplate = new OAuthRestTemplate(prd()); 
     oAuthRestTemplate.getInterceptors().add(interceptor); 
     return oAuthRestTemplate; 
    } 
} 

答えて

0

私は解決策を見つけたと思います。私の自身のOAuthConsumerContextFilterを実装して、リダイレクト呼び出しを認証エンドポイントへの直接呼び出しに置き換えるのがトリックです。私は以下の興味深い部分(//!!!!で始まる)にコメントしました。

CustomOAuthConsumerContextFilter

public class CustomOAuthConsumerContextFilter extends OAuthConsumerContextFilter { 

    private static final Logger LOG = LoggerFactory.getLogger(CustomOAuthConsumerContextFilter.class); 

    private RestTemplateBuilder restTemplateBuilder; 


    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) 
      throws IOException, ServletException { 
     HttpServletRequest request = (HttpServletRequest) servletRequest; 
     HttpServletResponse response = (HttpServletResponse) servletResponse; 
     OAuthSecurityContextImpl context = new OAuthSecurityContextImpl(); 
     context.setDetails(request); 

     Map<String, OAuthConsumerToken> rememberedTokens = 
       getRememberMeServices().loadRememberedTokens(request, response); 
     Map<String, OAuthConsumerToken> accessTokens = new TreeMap<>(); 
     Map<String, OAuthConsumerToken> requestTokens = new TreeMap<>(); 
     if (rememberedTokens != null) { 
      for (Map.Entry<String, OAuthConsumerToken> tokenEntry : rememberedTokens.entrySet()) { 
       OAuthConsumerToken token = tokenEntry.getValue(); 
       if (token != null) { 
        if (token.isAccessToken()) { 
         accessTokens.put(tokenEntry.getKey(), token); 
        } else { 
         requestTokens.put(tokenEntry.getKey(), token); 
        } 
       } 
      } 
     } 

     context.setAccessTokens(accessTokens); 
     OAuthSecurityContextHolder.setContext(context); 
     if (LOG.isDebugEnabled()) { 
      LOG.debug("Storing access tokens in request attribute '" + getAccessTokensRequestAttribute() + "'."); 
     } 

     try { 
      try { 
       request.setAttribute(getAccessTokensRequestAttribute(), new ArrayList<>(accessTokens.values())); 
       chain.doFilter(request, response); 
      } catch (Exception e) { 
       try { 
        ProtectedResourceDetails resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(e); 
        String neededResourceId = resourceThatNeedsAuthorization.getId(); 
        //!!!! store reference to verifier here, outside of loop 
        String verifier = null; 
        while (!accessTokens.containsKey(neededResourceId)) { 
         OAuthConsumerToken token = requestTokens.remove(neededResourceId); 
         if (token == null) { 
          token = getTokenServices().getToken(neededResourceId); 
         } 

         // if the token is null OR 
         // if there is NO access token and (we're not using 1.0a or the verifier is not null) 
         if (token == null || (!token.isAccessToken() && 
           (!resourceThatNeedsAuthorization.isUse10a() || verifier == null))) { 
          //no token associated with the resource, start the oauth flow. 
          //if there's a request token, but no verifier, we'll assume that a previous oauth request failed and we need to get a new request token. 
          if (LOG.isDebugEnabled()) { 
           LOG.debug("Obtaining request token for resource: " + neededResourceId); 
          } 

          //obtain authorization. 
          String callbackURL = response.encodeRedirectURL(getCallbackURL(request)); 
          token = getConsumerSupport().getUnauthorizedRequestToken(neededResourceId, callbackURL); 
          if (LOG.isDebugEnabled()) { 
           LOG.debug("Request token obtained for resource " + neededResourceId + ": " + token); 
          } 

          //okay, we've got a request token, now we need to authorize it. 
          requestTokens.put(neededResourceId, token); 
          getTokenServices().storeToken(neededResourceId, token); 
          String redirect = 
            getUserAuthorizationRedirectURL(resourceThatNeedsAuthorization, token, callbackURL); 

          if (LOG.isDebugEnabled()) { 
           LOG.debug("Redirecting request to " + redirect + 
             " for user authorization of the request token for resource " + 
             neededResourceId + "."); 
          } 

          request.setAttribute(
            "org.springframework.security.oauth.consumer.AccessTokenRequiredException", e); 
          //       this.redirectStrategy.sendRedirect(request, response, redirect); 
          //!!!! get the verifier from the authorization URL 
          verifier = this.getVerifier(redirect); 
          //!!!! start next iteration of loop -> now we have the verifier, so the else statement below shoud get executed and an access token retrieved 
          continue; 
         } else if (!token.isAccessToken()) { 
          //we have a presumably authorized request token, let's try to get an access token with it. 
          if (LOG.isDebugEnabled()) { 
           LOG.debug("Obtaining access token for resource: " + neededResourceId); 
          } 

          //authorize the request token and store it. 
          try { 
           token = getConsumerSupport().getAccessToken(token, verifier); 
          } finally { 
           getTokenServices().removeToken(neededResourceId); 
          } 

          if (LOG.isDebugEnabled()) { 
           LOG.debug("Access token " + token + " obtained for resource " + neededResourceId + 
             ". Now storing and using."); 
          } 

          getTokenServices().storeToken(neededResourceId, token); 
         } 

         accessTokens.put(neededResourceId, token); 

         try { 
          //try again 
          if (!response.isCommitted()) { 
           request.setAttribute(getAccessTokensRequestAttribute(), 
             new ArrayList<>(accessTokens.values())); 
           chain.doFilter(request, response); 
          } else { 
           //dang. what do we do now? 
           throw new IllegalStateException(
             "Unable to reprocess filter chain with needed OAuth2 resources because the response is already committed."); 
          } 
         } catch (Exception e1) { 
          resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(e1); 
          neededResourceId = resourceThatNeedsAuthorization.getId(); 
         } 
        } 
       } catch (OAuthRequestFailedException eo) { 
        fail(request, response, eo); 
       } catch (Exception ex) { 
        Throwable[] causeChain = getThrowableAnalyzer().determineCauseChain(ex); 
        OAuthRequestFailedException rfe = (OAuthRequestFailedException) getThrowableAnalyzer() 
          .getFirstThrowableOfType(OAuthRequestFailedException.class, causeChain); 
        if (rfe != null) { 
         fail(request, response, rfe); 
        } else { 
         // Rethrow ServletExceptions and RuntimeExceptions as-is 
         if (ex instanceof ServletException) { 
          throw (ServletException) ex; 
         } else if (ex instanceof RuntimeException) { 
          throw (RuntimeException) ex; 
         } 

         // Wrap other Exceptions. These are not expected to happen 
         throw new RuntimeException(ex); 
        } 
       } 
      } 
     } finally { 
      OAuthSecurityContextHolder.setContext(null); 
      HashMap<String, OAuthConsumerToken> tokensToRemember = new HashMap<>(); 
      tokensToRemember.putAll(requestTokens); 
      tokensToRemember.putAll(accessTokens); 
      getRememberMeServices().rememberTokens(tokensToRemember, request, response); 
     } 
    } 


    private String getVerifier(String authorizationURL) { 
     HttpEntity request = HttpEntity.EMPTY; 

     RestTemplate restTemplate = restTemplateBuilder.setConnectTimeout(1000).setReadTimeout(1000).build(); 
     ResponseEntity<String> response = 
       restTemplate.exchange(authorizationURL, HttpMethod.GET, request, String.class); 
     //!!!! extract verifier from response 
     String verifier = response.getBody().split("=")[1]; 
     return verifier; 
    } 


    void setRestTemplateBuilder(RestTemplateBuilder restTemplateBuilder) { 
     this.restTemplateBuilder = restTemplateBuilder; 
    } 
} 
関連する問題