2016-12-22 18 views
1

次のように我々が探しているシナリオは次のとおりです。RESTエンドポイント認証のSpring Security予期しない動作?

  1. クライアントは200 OK
  2. クライアントが保持トークンのログインを返す必要がRESTのログインURL(春のセキュリティを使用して)
  3. 春microserviceにRESTと接続トークン
  4. クライアントは、同じトークンを使用して他のRESTエンドポイントを呼び出します。

しかし、クライアントが302Locationというヘッダーをトークンとともに取得していることがわかります。したがって、認証は行いますが、望ましくないHTTP応答のステータスコードとヘッダーが付いています。

ザ・春のセキュリティ設定は、次のようになります。

@Configuration 
@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .csrf() 
       .disable() // Refactor login form 
       // See https://jira.springsource.org/browse/SPR-11496 
      .headers() 
       .addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN)) 
       .and() 
      .formLogin() 
       .loginPage("/signin") 
       .permitAll() 
       .and() 
      .logout() 
       .logoutUrl("/signout") 
       .permitAll() 
       .and() 
      .authorizeRequests() 
       .antMatchers("/", "/home").permitAll() 
       .anyRequest().authenticated(); 
... 
} 

私はインターセプタとフィルタを追加しようとしたが、302及び場所は春の側に設定して、コメントを追加している場所を確認することはできません。 (「200 OK予想通り

Server=Apache-Coyote/1.1 
X-Content-Type-Options=nosniff 
X-XSS-Protection=1; mode=block 
Cache-Control=no-cache, no-store, max-age=0, must-revalidate 
Pragma=no-cache 
Expires=0 
X-Frame-Options=DENY, SAMEORIGIN 
Set-Cookie=JSESSIONID=D1C1F1CE1FF4E1B3DDF6FA302D48A905; Path=/; HttpOnly 
Location=http://ec2-35-166-130-246.us-west-2.compute.amazonaws.com:8108/ <---- ouch 
Content-Length=0 
Date=Thu, 22 Dec 2016 20:15:20 GMT 

任意の提案は、どのようにそれを動作させるために: しかし、Locationヘッダはレスポンスヘッダーに表示されないが(春SecurityヘッダーLINKの残りの部分と一緒に)クライアント側で受信しました"、ロケーションヘッダーとトークンはありません)?

注:Springブート、Springセキュリティ、UIなし、RESTエンドポイントを呼び出すクライアントコードのみを使用します。

+0

私たちがやっていることは、アプリケーションがPOSTとクレデンシャルでログインし、ログイントークンを受け取ったときにurlを持つことです。なぜ私は何かがあるはずではないのですか? – Roy

+0

//クライアントの呼び出し: 新しいRestTemplate()。実行(SIGIN_URL、HttpMethod.POST、 \t新しいRequestCallback(){... }、 \t新しいResponseExtractor ... { \tパブリックオブジェクトextractData(...){ //ステータスコードは302 \t headersToUpdate.add( "クッキー"、response.getHeaders()。getFirstと(「セット - ですCookie ")); \t戻り値: (UIとボタンはありません) – Roy

答えて

-1

headers().defaultsDisabled()を使用して、その方法をチェーンして、必要な特定のヘッダーを追加できます。

+1

試してみましたが位置はまだ表示されています – Roy

0

これは、ブラウザにログインページにリダイレクトするように指示する302応答です。何が起こると思いますか? 302応答にはLocationヘッダーが必要です。

+0

クライアントのconne ctsから/ signinはログインURLです。だから私は実際に200を見ることを期待している。 私は何かが不足している? もっと情報を共有できますか? – Roy

+0

おそらく私は再考するでしょう - 200のステータスコードとロケーションヘッダを得るために私は何を変えなければなりませんか? – Roy

+1

@ロイあなたはできません。ブラウザのための '/ signin' URLか、APIクライアントのログインエンドポイントは何らかのトークンを取得するのですか?トークンを取得する場合、クライアントがトークンを取得した後、このトークンはどのように使用されるのですか? – Strelok

0

残りのAPIが必要な場合は、http.formLogin()を使用しないでください。 hereのようにフォームベースのログインを生成します。

代わりに、春UsernamePasswordAuthenticationFilter拡張するクラス、AuthTokenFilterを作成します。この構成

httpSecurity 
       .csrf() 
        .disable() 
       .exceptionHandling() 
        .authenticationEntryPoint(authenticationEntryPoint) 
       .and() 
       .sessionManagement() 
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
       .and() 
       .authorizeRequests() 
        .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() 
        .antMatchers("/login").permitAll() 
        .anyRequest().authenticated() 
       .and() 
       .logout() 
        .disable() 
       .addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class); 

を持っており、すべての要求で認証トークンをチェックし、それに応じてSecurityContextHolderを設定doFilterメソッドをオーバーライドすることができます。

@Override 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws IOException, ServletException { 
     HttpServletResponse resp = (HttpServletResponse) response; 
     resp.setHeader("Access-Control-Allow-Origin", "*"); 
     resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); 
     resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, " + tokenHeader); 

     HttpServletRequest httpRequest = (HttpServletRequest) request; 
     String authToken = httpRequest.getHeader(tokenHeader); 
     String username = this.tokenUtils.getUsernameFromToken(authToken); // Create some token utility class to manage tokens 

     if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { 

      UsernamePasswordAuthenticationToken authentication = 
          new UsernamePasswordAuthenticationToken(-------------); 
      // Create an authnetication as above and set SecurityContextHolder 
      authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); 
     SecurityContextHolder.getContext().setAuthentication(authentication); 
     } 
     chain.doFilter(request, response); 
} 

そして資格情報をチェックし、トークンを返し/login URL、にマッピングAuthenticationControllerを作成します。

/* 
* Perform the authentication. This will call Spring UserDetailsService's loadUserByUsername implicitly 
* BadCredentialsException is thrown if username and password mismatch 
*/ 
Authentication authentication = this.authenticationManager.authenticate(
    new UsernamePasswordAuthenticationToken(
      authenticationRequest.getUsername(), 
      authenticationRequest.getPassword() 
    ) 
); 
SecurityContextHolder.getContext().setAuthentication(authentication);   
UserDetailsImp userDetails = (UserDetailsImp) authentication.getPrincipal(); 
// Generate token using some Token Utils class methods, using this principal 

loadUserByUsernameUserDetailsServiceUserDetailsは、より良く理解するためにSpring security docs }

を参照してください理解するために、徹底的にリンクおよびそれ以降の章の上お読みください。

+0

@ Ramanujan、私は最初の作業基準を持っています。あなたが送ったリンクを読んで、コードを見直して更新します。ありがとう! – Roy

+0

はまだ長くはっきりしない文書で苦労しています。また、ログアウトの処理方法についてアドバイスを追加することもできます(たとえば、リダイレクトなどを使用せずにauthTokenを無効にするなど)。 ありがとう! – Roy

0

カスタムAuthenticationSuccessHandlerを実装し、メソッド "onAuthenticationSuccess"をオーバーライドして、必要に応じて応答ステータスを変更できます。

例:

@Override 
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 
     Authentication authentication) throws IOException, ServletException { 
    ObjectMapper mapper = new ObjectMapper(); 
    Map<String, String> tokenMap = new HashMap<String, String>(); 
    tokenMap.put("token", accessToken.getToken()); 
    tokenMap.put("refreshToken", refreshToken.getToken()); 
    response.setStatus(HttpStatus.OK.value()); 
    response.setContentType(MediaType.APPLICATION_JSON_VALUE); 
    mapper.writeValue(response.getWriter(), tokenMap); 
} 
1

http.formLogin()

は、フォームベースのログインのために設計されています。したがって、認証されずに保護されたリソースにアクセスしようとすると、応答の302ステータスとLocationヘッダーが必要になります。あなたの要件/シナリオに基づいて

  1. クライアントは、認証のためのHTTP Basicを使用して

はあなたが考えたRESTのログインURLにRESTで接続されていますか? HTTP Basicを使用して

http.httpBasic()

、ユーザー名/パスワードで認証ヘッダを読み込むことができ、BasicAuthenticationFilterは、資格情報を認証し、それに応じたSecurityContextを移入の世話をします。

クライアント側ではAngular、バックエンドではSpring Boot-Spring Securityを使用しています。working exampleです。

​​を見ると、login()ファンクションを提供するsecurityServiceファクトリが表示されます。 BasicAuthenticationFilter資格情報を抽出し、最終的にユーザを認証することによって、この要求を処理する

Authorization : Basic base64Encoded(username:passsword)

と:この機能は、例えば、HTTP基本フォーマットの通り、ユーザ名/パスワードを使用して移入Authorizationヘッダと/principalエンドポイントを呼び出しますSecurityContextに認証されたプリンシパルを入力します。認証が成功した後、要求は、SecurityController.currentPrincipalにマップされた宛先エンドポイント/principalに進み、単に認証されたプリンシパルのjson表現を返します。あなたの残りの要件について

  • クライアントがトークン
  • 保ちOK 200とトークンのログインを返さなければなりません(春のセキュリティを使用して)
  • 春microservice <オール= "2" スタート>をクライアントは同じトークンを使用して他のRESTエンドポイントを呼び出します。
  • セキュリティ/ログイントークンを生成し、それをユーザー情報の代わりに返すことができます。ただし、セキュリティトークンで保護する必要があるさまざまなMicroservicesに複数のRESTエンドポイントが配置されている場合は、Spring Security OAuthを参照することを強くお勧めします。独自のSTS(セキュリティトークンサービス)を構築することは、非常に複雑で複雑になる可能性があるため、推奨されません。

    関連する問題