5

私はspring-security 3.1.4で作業しており、要件があります:

  • 認証の成否を見守る
    • 成功した場合、ユーザーの一般情報をセッション属性に入れます
    • 結果が失敗の場合。
      • 失敗の原因を特定します (アカウントのロック、アカウントの期限切れ、資格情報の期限切れ、ユーザーの無効化、ログイン失敗試行の超過など)
      • login.xhtml にある growl コンポーネントのログイン失敗メッセージを生成します。
      • 失敗イベントに固有のアクションを実行します。たとえば、資格情報が正しくない場合は、db でのログイン失敗試行を増やしたり、資格情報の再定義などのページにリダイレクトしたりします。

私はそのための3つの解決策を調査し、見つけました。

  • すべてのフェーズイベントPhaseListenerで呼び出されるずさんな原因の実装:

public class LoginErrorPhaseListener implements PhaseListener {
  private static final long   serialVersionUID              = -404551400448242299L;

  private static final String MESSAGES_RESOURCE_BUNDLE_NAME = "msgs";
  private static final String ACCESS_DENIED_MESSAGE_KEY     = "accessDeniedMessage";
  private static final String BAD_CREDENTIALS_MESSAGE_KEY   = "badCredentialsMessage";

  @Override
  public void beforePhase(final PhaseEvent arg0) {
    Exception e = (Exception) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(WebAttributes.AUTHENTICATION_EXCEPTION);
      if (e instanceof BadCredentialsException) {
         FacesContext fc = FacesContext.getCurrentInstance();
         ResourceBundle messages = fc.getApplication().getResourceBundle(fc, MESSAGES_RESOURCE_BUNDLE_NAME);
         fc.getExternalContext().getSessionMap().put(WebAttributes.AUTHENTICATION_EXCEPTION, null);
         fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.getString(ACCESS_DENIED_MESSAGE_KEY), messages.getString(BAD_CREDENTIALS_MESSAGE_KEY)));
      }
   }

   @Override
   public void afterPhase(final PhaseEvent arg0) {
   }

   @Override
   public PhaseId getPhaseId() {
      return PhaseId.RENDER_RESPONSE;
   }

}
  • その他はカスタマイズAuthenticationFailureHandlerAuthenticationSuccessHandler

public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

   @Inject
   private UserDao userDao;

   public CustomAuthenticationFailureHandler() {
   }

   public CustomAuthenticationFailureHandler(String defaultFailureUrl) {
      super(defaultFailureUrl);
   }

   @Override
   public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
      super.onAuthenticationFailure(request, response, exception);
      Class exceptionClazz = exception.getClass();

      if (exceptionClazz == UsernameNotFoundException.class) {

      }
      else if (exceptionClazz == AuthenticationCredentialsNotFoundException.class) {

      }
      else if (exceptionClazz == BadCredentialsException.class) {
         UserBean user = (UserBean) exception.getExtraInformation();
         if (user.getLoginAttempts() == 2) {
            userDao.updateUserStates(user.getUsername(), true, false, true, true);
            userDao.resetUserLoginFailedAttempts(user.getUsername());
         }
         else {
            userDao.incrementLoginFailedAttempts(user.getUsername());
         }
      }
      else if (exceptionClazz == AccountStatusException.class) {

      }
      else if (exceptionClazz == AuthenticationServiceException.class) {

      }
      else if (exceptionClazz == InsufficientAuthenticationException.class) {

      }
      else if (exceptionClazz == NonceExpiredException.class) {

      }
      else if (exceptionClazz == PreAuthenticatedCredentialsNotFoundException.class) {

      }
      else if (exceptionClazz == ProviderNotFoundException.class) {

      }
      else if (exceptionClazz == RememberMeAuthenticationException.class) {

      }
      else if (exceptionClazz == SessionAuthenticationException.class) {

      }
   }
}

public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

   @Inject
   private UserDao userDao;

   @Override
   public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
      super.onAuthenticationSuccess(request, response, authentication);
      UserPersonalInfoBean activeUser = (UserPersonalInfoBean) authentication.getPrincipal();
      request.getSession().setAttribute("activeUser", activeUser);
      userDao.resetUserLoginFailedAttempts(activeUser.getUsername());
   }
}
  • そして、私が見つけた最後の方法は、春のコンテキストを実装することです ApplicationListener

@Named
public class BadCredentialsListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {
   private static final long   serialVersionUID              = -404551400448242299L;

   private static final String MESSAGES_RESOURCE_BUNDLE_NAME = "msgs";
   private static final String ACCESS_DENIED_MESSAGE_KEY     = "accessDeniedMessage";
   private static final String BAD_CREDENTIALS_MESSAGE_KEY   = "badCredentialsMessage";

   @Override
   public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) {
      FacesContext fc = FacesContext.getCurrentInstance();
      ResourceBundle messages = fc.getApplication().getResourceBundle(fc, MESSAGES_RESOURCE_BUNDLE_NAME);
      fc.getExternalContext().getSessionMap().put(WebAttributes.AUTHENTICATION_EXCEPTION, null);
      fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.getString(ACCESS_DENIED_MESSAGE_KEY), messages.getString(BAD_CREDENTIALS_MESSAGE_KEY)));
   }

}

私の質問はついにここにあります。私はジュニア開発者であり、私が使用した要件とテクノロジ (jsr330 インジェクション、jsf コンテキストなど) を克服するための最良の方法として、どの方法が効果的/効率的であるかを考えたり考えたりすることはできません。

PhaseListener上記の理由でjsfソリューションをあきらめます。実際には、spring-security アクセスおよび障害ハンドラーは s に似ていますがPhaseListener、より具体的な条件で呼び出されるため、より効率的です。より具体的なイベントは、そのタイプに基づいて例外から取得する必要があります。security-context.xmlただし、それらを定義することでセキュリティモジュールの可読性が向上することに同意する必要があります。子クラスを聞くAbstractAuthenticationFailureEventことは、私には本当にうまく見えます。各イベントは互いに垂直に分離されており、クリーンです。さらに、とメソッドが廃止されたため、 で失敗したユーザーのユーザー名に到達する別AuthenticationExceptionの方法を見つけることができませんでした。getExtraInformationgetAuthenticationAuthenticationFailureHandler.onAuthenticationFailure

皆さんが理解しているように、私はかなり混乱しており、コメントを受け付けています。

よろしくお願いします。

4

1 に答える 1