2017-11-11 5 views
0

私はプログラムによる手動のユーザーログインを実装するためにスプリングセキュリティを使用しています。私はユーザーのアイデンティティを積極的に確立し、ログインしたいというシナリオを持っています。私はパスワードを知らないので、フォームをURLに送信する通常のログインコードパスを使用できません。サーブレットFilterで傍受し、すべての認証+セッション魔法を実行します。スプリングセキュリティマニュアルのログインのベストプラクティス

私が検索した、そしてほとんどの人が自分のAuthenticationオブジェクトを作成しているようだし、その後経由でおよそ春を告げる:この作品、

実際
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(user, "", user.getAuthorities()); 
SecurityContextHolder.getContext().setAuthentication(authentication); 

。 Springはそれを私のためにセッションに入れて、後続のHTTPリクエストが認証ステータスを維持するようにします。

しかし、これは汚れたハックだと感じます。私は手動ログインを達成するために、コントローラ内部でsetAuthentication()の使用に関連する問題の具体的な例を与えることを願っていますいくつかの詳細を提示します:

アイデアを与えるために、私の設定は次のとおりです。

httpSecurity 
    .authorizeRequests() 
    .antMatchers("/test/**").permitAll() 
    .antMatchers("/admin/**", "/api/admin/**").hasRole("USER_SUPER_ADMIN") 
    .and() 
    .formLogin() 
    .loginPage("/sign-in?sp") 
    .loginProcessingUrl("/api/auth/sign-in") 
    .successHandler(createLoginSuccessHandler()) 
    .failureHandler(createLoginFailureHandler()) 
    .permitAll() 
    .and() 
    .logout() 
    .logoutUrl("/api/auth/sign-out") 
    .logoutSuccessHandler(createLogoutSuccessHandler()) 
    .and() 
    .sessionManagement() 
    .maximumSessions(1) 
    .maxSessionsPreventsLogin(true) 
    .sessionRegistry(sessionRegistry) 
; 

キー上記のポイント:
- フォームのログインにカスタム成功と失敗のハンドラーを使用する
- ユーザーあたりの最大同時セッションの動作を設定したい
- 私は春のデフォルトのセッション固定保護を維持したいログイン)。
- 私はセッションレジストリを使用したい
- ...詳細、私はそれを設定しましたか?

私はスプリングを使ってフォームのログインを処理する方法を確認しました。予想どおり、Springは私のHttpSecurity configがフォームログインを使用するときに指示したすべてのことを行います。しかし、SecurityContextHolder.getContext().setAuthentication()で自分のカスタム/手動ログインを行うと、それはありません。これは、Springがサーブレットの内部にあるauth + sessionの内容をすべて処理するためです。Filterと私のプログラムコードで実際にはFilterを呼び出すことはできません。さて、不足している振る舞いを自分自身で追加して、コードを複製してみることができます.FilterはConcurrentSessionControlAuthenticationStrategyChangeSessionIdAuthenticationStrategy、およびRegisterSessionAuthenticationStrategyを使用しています。これらのオブジェクトを自分で作成し、設定し、カスタムログイン後に呼び出すことができます。しかし、それはそのような形でそれを複製するのはちょっと残念です。さらに、私は行方不明の他の行動がある - 私は、フォームのログインコードパスを使用すると、その春は、私は私のカスタムログインを行うときにトリガされないいくつかのログインイベントをトリガ気づいた。おそらく私が紛失しているか、理解していない他のものがあります。全体のプロセスはかなり複雑です。

私は間違った方法でこれに近づいているように感じます。 私は春が私にとって大切なものを迂回しないように、別の戦略を使用すべきですか?多分私はこのカスタムログインを達成するために私自身のAuthenticationProviderを作るべきですか?

*明確にするために、私のコードは多かれ少なかれ動作します。しかし、私は貧しい戦略を使ってそれを達成したように感じます。なぜなら、私にとっては春がやる多くのものを複製するコードを書く必要があったからです。さらに、私のコードは、春のことを完全に再現するわけではありません。プログラムでログインを達成するには、より良い方法が必要です。

+1

を使用するには、AuthenticationProvider

@Component public class AccountVerificationAuthenticationProvider implements AuthenticationProvider { @Autowired private AppAuthenticatedUserService appAuthenticatedUserService; @Autowired private AuthService authService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // This will look in the user's session to get their username, and to make sure the flag is set to allow login without password on this request. UserAccount userAccount = authService.getUserAccountFromRecentAccountVerificationProcess(); if (userAccount == null) { // Tell spring we can't process this AuthenticationProvider obj. // Spring will continue, and try another AuthenticationProvider, if it can. return null; } // A service to create a custom UserDetails object for this user. UserDetails appAuthenticatedUser = appAuthenticatedUserService.create(userAccount.getEmail(), "", true); PreAuthenticatedAuthenticationToken authenticationToken = new PreAuthenticatedAuthenticationToken(appAuthenticatedUser, "", appAuthenticatedUser.getAuthorities()); authenticationToken.setAuthenticated(true); return authenticationToken; } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } } 

コンフィグ春のセキュリティそれは広すぎるようだが。問題に関する適切な情報を提供するためにコンテンツを減らすことができますか?[MCVE](https://stackoverflow.com/help/mcve)を投稿してください。 –

+1

@dur私のカスタム(手動)ログインは、最初のアカウント作成直後に使用されます。彼らに電子メールを送り、彼らのアカウントを確認するためのリンクをクリックさせる。リンクをクリックすると、資格情報を再入力せずにログインします。このシナリオでは私はパスワードを持っていないので、 'ActiveDirectoryLdapAuthenticationProvider'のような「通常の」春の認証メカニズムを使うことはできません。パスワードを忘れてしまった場合でも、基本的には同じことをやります。パスワードを変更したりリセットしたりすることなく、アカウントのアクセスを制限することができます。これは私のユースケースにとって非常に重要です。 – goat

+0

@dur、さらに、*パスワードを持っていても、春になると私のためにすべてのことをやり遂げることはできません。なぜなら、春はサーブレット 'Filter'の魔法ですアプリケーションコード(コントローラなど)が実行される前に私は、ログイン状態とそれに関連付けられたセッションのすべてをプログラマチックにリストアップする呼び出しを行う必要があります。 – goat

答えて

1

カスタムWeb認証のためにカスタム認証フィルタの組み合わせを実装する必要があります(例AbstractAuthenticationProcessingFilterまたは単にGenericFilterBean用)、カスタム認証プロバイダ(AuthenticationProvider)または/およびカスタム認証トークン(AbstractAuthenticationToken)。

例えば、出典:Spring Security Kerberosを参照してください。

も参照してください:

1

私はdurのアドバイスを実装する方法について詳しく説明したかったです。私のシナリオでは、私は唯一のカスタムを使用したAuthenticationProvider。 代わりに、このような多くの作業のように思えた、AbstractAuthenticationProcessingFilterを拡張として、カスタムサーブレットFilterを作成するのではなく、私が代わりに次の方法を使用することを選択した:私は私が持っていたことを確信していた私のコード内のポイントで

  • をユーザーを特定し、それらを「ログイン」したいと思ったとき、私はユーザーのセッションにフラグをつけて、次の要求でログインする必要があることをマークしました。ユーザー名など必要なアイデンティティ/ 。
  • 次に、ブラウザクライアントにloginProcessingUrl(フォームベースのログインに使用する春のセキュリティを設定したのと同じもの)のhttp投稿を作成して、usernamepasswordのフォームparamsを送信するように指示しました。実際の値を送信する必要がありません - fooのようなダミーの値は問題ありません。
  • ユーザーが投稿要求を行うと(たとえば/login)、springは私のカスタムAuthenticationProviderを呼び出します。このユーザーのセッションでは、フラグがチェックされ、ユーザー名が収集されます。次に、Authenticationオブジェクト(たとえば、PreAuthenticatedAuthenticationToken)を作成して返します。このオブジェクトは、ユーザーを識別します。
  • 残りはSpringが処理します。 。

    • コール任意のカスタムの成功と失敗:ユーザーは、現在ログインしている

    をこのようにそれを行うことによって、あなたは「通常の」ログインを行う方法、及びその春意志まだ自動的内にとどまりますフォームのログイン用に設定したハンドラ。これは、ログイン時にクエスチョンや更新などの特定の作業を行うためにその場所を使用すると便利です。

  • あなたが使用している可能性のある最大同時セッション数を尊重します。
  • あなたは春のデフォルトのセッション固定攻撃防御(ログイン時にセッションIDを変更する)を保つことになります。
  • プロパティファイルでserver.session.timeoutなどのカスタムセッションタイムアウトを設定した場合、springはそれを使用します。おそらくこの時点で行われる他のセッション設定属性もあります。
  • springの "remember me"機能を有効にすると、動作します。
  • ユーザーのセッションをSessionRegistryに保存するなど、他のスプリングコンポーネントで使用されるログインイベントが発生します。私はイベントがアクチュエータのような春の他の部分や監査のためにも使われていると思います。私が最初にちょうど全くあなたのアプリを破る...か微妙なセキュリティを引き起こす可能性が、代わりにカスタムAuthenticationProviderの、上記の弾丸のどれも私のために行われなかった、私のユーザーがログインするために一般的に推奨さSecurityContextHolder.getContext().setAuthentication(authentication)をやってみました

バグ - どちらも良いことではありません。

ここで私が言ったことを固化するのに役立ついくつかのコードです:

カスタムプロバイダ

// In your WebSecurityConfigurerAdapter 
@Configuration 
@EnableWebSecurity 
public class AppLoginConfig extends WebSecurityConfigurerAdapter { 
    @Autowired 
    private AccountVerificationAuthenticationProvider accountVerificationAuthenticationProvider; 

    @Override 
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { 
     // Spring will try these auth providers in the order we register them. 
     // We do the accountVerificationAuthenticationProvider provider first, since it doesn't need to do any IO to check, 
     // so it's very fast. Only if that one rejects, do we then try the active directory one. 
     authenticationManagerBuilder 
      .authenticationProvider(accountVerificationAuthenticationProvider) 
      // I'm using ActiveDirectory, but whatever you want to use here should work. 
      .authenticationProvider(activeDirectoryLdapAuthenticationProvider); 
    } 

    @Override 
    protected void configure(HttpSecurity httpSecurity) throws Exception { 
     ... 
    } 
} 
関連する問題