2017-01-25 10 views
4

oauth2 spring-guidesプロジェクトのログアウトフローを参照すると、ユーザが最初にユーザ/パスワードを使用して認証されると、ログアウト後に次回に認証情報が尋ねられません。SpringセキュリティOAuth2 AngularJS |ログアウトフロー

ログアウト後に毎回ユーザー名とパスワードを確認する方法を教えてください。

これは私が実装しようとしているものです: - 自動承認を得て、 "authorization_code" 許可タイプ を使ってJWTトークンを発行

  • のOAuth2サーバー。これは、 ユーザ名/パスワードを収集するhtml/angularjs形式です。

  • @EnableSSOを使用します。すべてのエンドポイントが認証された つまり、ユーザがが/ uaaサーバに行くためにクリックする不正なリンク先ページ/ui /リンクはありません。したがって、http://localhost:8080 を押すとすぐにhttp://localhost:9999/uaaにリダイレクトされ、ユーザ名/パスワードを収集するためのカスタムフォーム が表示されます。

  • リソースサーバー - @EnableResourceServerを使用します。プレーン&単純なREST API。

上記のアプローチでは、私はログアウトの流れを練習することができません。 UIアプリケーションへのHTTP POST /ログアウトにより、UIアプリケーションのセッション/認証がクリアされますが、ユーザー名のパスワードを再度尋ねることなく、ユーザーは自動的に再度ログインします(すべてのスコープに対して自動承認を選択したため)。

ログとネットワークの呼び出しを見ると、「oauth dance」はすべてユーザーに再度ユーザー名とパスワードを要求されることなく正常に処理されているように見え、認証サーバーはクライアントに対して最後に発行された認証トークンorg.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices?を使用して)。

コード/トークンが要求されるたびに認証サーバーにユーザー名とパスワードを要求するようにするにはどうすればよいですか。

または、指定したシナリオでログアウトを実装する最良の方法は何ですか。あなたが述べたよう

(、私の要件にやや近い作り直すのUIApplicationからpermitAll()一部を除去し、言及したブートプロジェクトの認証サーバにautoApprovalを設定する。)

github issue

+1

私が知ることができる簡単な解決策は、認証サーバーでserver.session.timeoutを低い値(30秒)に設定し、 "/ me"エンドポイントをリソースサーバーに移動することでした。 –

+0

セッションタイムアウトを減らす方法はありますか? – Cataclysm

答えて

2

は、私もエラーに直面し、質問 Spring Boot OAuth2 Single Sign Offの解決策を見ました。私はこれが唯一のグローバルな真実の解決策であるということではありません。

しかしシナリオでは、

  • 認証サーバは、フォームにログインしていると、ブラウザあなたがログアウト処理を終了した後、まだトークンを取り消す(認証サーバ
  • とのセッションを維持
  • それから、認証されたのです、クッキーを削除...) と再ログインを再び
  • 認証サーバにしようとは、ログインフォームを送信すると、自動的にあなたがthis答えが説明するように、認証サーバーのセッションから認証情報を削除する必要が

に署名しません。スニペット以下

私は、クライアントアプリケーション

@Component 
public class CustomLogoutHandler implements LogoutHandler { 

    private static Logger logger = Logger.getLogger(CustomLogoutHandler.class); 

    @Value("${auth-server}/invalidateTokens") 
    private String logoutUrl; 

    @Override 
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { 

     logger.debug("Excution CustomLogoutHandler for " + authentication.getName()); 
     Object details = authentication.getDetails(); 
     if (details.getClass().isAssignableFrom(OAuth2AuthenticationDetails.class)) { 

      String accessToken = ((OAuth2AuthenticationDetails) details).getTokenValue(); 
      RestTemplate restTemplate = new RestTemplate(); 

      MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); 
      params.add("access_token", accessToken); 

      HttpHeaders headers = new HttpHeaders(); 
      headers.add("Authorization", "bearer " + accessToken); 

      HttpEntity<Object> entity = new HttpEntity<>(params, headers); 

      HttpMessageConverter<?> formHttpMessageConverter = new FormHttpMessageConverter(); 
      HttpMessageConverter<?> stringHttpMessageConverternew = new StringHttpMessageConverter(); 
      restTemplate.setMessageConverters(Arrays.asList(new HttpMessageConverter[] { formHttpMessageConverter, stringHttpMessageConverternew })); 
      try { 
       ResponseEntity<String> serverResponse = restTemplate.exchange(logoutUrl, HttpMethod.POST, entity, String.class); 
       logger.debug("Server Response : ==> " + serverResponse); 
      } catch (HttpClientErrorException e) { 
       logger.error("HttpClientErrorException invalidating token with SSO authorization server. response.status code: " + e.getStatusCode() + ", server URL: " + logoutUrl); 
      } 
     } 
     authentication.setAuthenticated(false); 
     Authentication auth = SecurityContextHolder.getContext().getAuthentication(); 
     new SecurityContextLogoutHandler().logout(request, response, auth); 

    } 

} 

ためのソリューション

クライアント(あなたのケースではUIアプリケーション)アプリケーションのWebSecurityConfig

... 
@Value("${auth-server}/ssoLogout") 
private String logoutUrl; 
@Autowired 
private CustomLogoutHandler logoutHandler; 
... 
    @Override 
    public void configure(HttpSecurity http) throws Exception { 
     // @formatter:off 
     http.antMatcher("/**") 
      .authorizeRequests() 
      .antMatchers("/", "/login").permitAll() 
      .anyRequest().authenticated() 
     .and() 
      .logout() 
       .logoutSuccessUrl(logoutUrl) 
       .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) 
       .addLogoutHandler(logoutHandler) 
     .and()  
      .csrf() 
       .csrfTokenRepository(csrfTokenRepository()) 
     .and() 
      .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class); 
     // @formatter:on 
    } 

カスタムログアウトハンドラの設定でしたどのようにしている私は、JDBCを使用tokenStore、トークンを取り消す必要があります。認証サーバー側では、ログアウトプロセスを処理するコントローラを追加しました

@Controller 
public class AuthenticationController { 

    private static Logger logger = Logger.getLogger(AuthenticationController.class); 

    @Resource(name = "tokenStore") 
    private TokenStore tokenStore; 

    @Resource(name = "approvalStore") 
    private ApprovalStore approvalStore; 

    @RequestMapping(value = "/invalidateTokens", method = RequestMethod.POST) 
    public @ResponseBody Map<String, String> revokeAccessToken(HttpServletRequest request, HttpServletResponse response, @RequestParam(name = "access_token") String accessToken, Authentication authentication) { 
     if (authentication instanceof OAuth2Authentication) { 
      logger.info("Revoking Approvals ==> " + accessToken); 
      OAuth2Authentication auth = (OAuth2Authentication) authentication; 
      String clientId = auth.getOAuth2Request().getClientId(); 
      Authentication user = auth.getUserAuthentication(); 
      if (user != null) { 
       Collection<Approval> approvals = new ArrayList<Approval>(); 
       for (String scope : auth.getOAuth2Request().getScope()) { 
        approvals.add(new Approval(user.getName(), clientId, scope, new Date(), ApprovalStatus.APPROVED)); 
       } 
       approvalStore.revokeApprovals(approvals); 
      } 
     } 
     logger.info("Invalidating access token :- " + accessToken); 
     OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken); 
     if (oAuth2AccessToken != null) { 
      if (tokenStore instanceof JdbcTokenStore) { 
       logger.info("Invalidating Refresh Token :- " + oAuth2AccessToken.getRefreshToken().getValue()); 
       ((JdbcTokenStore) tokenStore).removeRefreshToken(oAuth2AccessToken.getRefreshToken()); 
       tokenStore.removeAccessToken(oAuth2AccessToken); 
      } 
     } 
     Map<String, String> ret = new HashMap<>(); 
     ret.put("removed_access_token", accessToken); 
     return ret; 
    } 

    @GetMapping("/ssoLogout") 
    public void exit(HttpServletRequest request, HttpServletResponse response) throws IOException { 
     new SecurityContextLogoutHandler().logout(request, null, null); 
     // my authorization server's login form can save with remember-me cookie 
     Cookie cookie = new Cookie("my_rememberme_cookie", null); 
     cookie.setMaxAge(0); 
     cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/"); 
     response.addCookie(cookie); 
     response.sendRedirect(request.getHeader("referer")); 
    } 

} 

は、認証サーバーのSecurityConfigで、私はこれがあなたのために少しを助けることを願ってい

http 
    .requestMatchers() 
     .antMatchers(
     "/login" 
     ,"/ssoLogout" 
     ,"/oauth/authorize" 
     ,"/oauth/confirm_access"); 

としてこのURLを許可する必要があるかもしれません。

+1

JWTを使うと、authserver上にセッションがありません。ステートレスです。言い換えれば、authserverがJWTトークンを発行すると、それを無効にする唯一の方法は、あなたが設定した有効期限です。クライアントアプリケーションに有効なトークンがある場合、有効期限が来るまでそれを使用し続けます。その後、リソースサーバーはトークンがもはや有効でないことを確認し、クライアントを送信してauthserverで新しいトークンを取得します。したがって、私がこのユースケースについて考えることができるのは、クライアントアプリケーションのトークンを忘れてauthserverから別のトークンを取得する方法を見つけることだけです。 –

+0

はい、あなたが説明したように、私はJWTトークンを無効にすることはできません。 ** JDBCトークンストア**を使用しましたが、JWTをまだ使用しているかどうかはわかりません。私が意味するセッションは、認証サーバーのログインページのブラウザーセッションでした。正常にログアウトした後に再度ログインしようとすると、トークンを取り消して承認を取り消しても、ブラウザは認証サーバーのログインフォームページを発行しません。 – Cataclysm

+0

@JuanCarlosMendoza authserverの場合、 'sessionCreationPolicy(SessionCreationPolicy.STATELESS)'でセッション管理を作成することはできません。 – Cataclysm

2

JWTトークンを使用しているので、本当にそれらを取り消すことはできません。 回避策として、ログアウトコールのタイムスタンプとユーザIDを保存するログアウト休止エンドポイントを設定することができます。

その後、ログアウト時刻とJWTトークンの発行時刻を比較して、API呼び出しを許可するかどうかを判断できます。

1

私は、クライアントアプリケーションからログアウトし、プログラム上でauthserverでログアウトしたときにコントローラにリダイレクトすることがそのトリックであることを認識しました。これは、クライアントアプリケーションに私の設定です:

@Configuration 
@EnableOAuth2Sso 
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { 

    @Value("${auth-server}/exit") 
    private String logoutUrl; 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .logout() 
      .logoutSuccessUrl(logoutUrl) 
      .and().authorizeRequests().anyRequest().authenticated(); 
    } 
} 

、これは私のauthserverに私の構成です(/終了エンドポイントを扱うだけのコントローラです):

@Controller 
public class LogoutController { 
    public LogoutController() { 
    } 

    @RequestMapping({"/exit"}) 
    public void exit(HttpServletRequest request, HttpServletResponse response) { 
     (new SecurityContextLogoutHandler()).logout(request, null, null); 

     try { 
      response.sendRedirect(request.getHeader("referer")); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

Hereは、その番組のサンプルアプリですJWTを使った完全実装。それをチェックし、それがあなたを助けるかどうか私達に知らせてください。

+0

JWTの場合*(とOPの質問)*については、あなたの答えは十分だと思います。私のoauth2では、JWTトークンベースのプロジェクトで、ユーザーのautheticationを削除することはできますが、再度ログインしようとするとauthserverはログインフォームを発行しませんでした。今すぐあなたの答えはこの解決策を修正します。今私は** JDBC tokenStore **と一緒に作業しています。私は私の答えとして設定しました。ありがとうございました。バウンティ報酬はあなたのものです。 – Cataclysm

関連する問題