私は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;
}
}
- その他はカスタマイズ
AuthenticationFailureHandler
とAuthenticationSuccessHandler
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
の方法を見つけることができませんでした。getExtraInformation
getAuthentication
AuthenticationFailureHandler.onAuthenticationFailure
皆さんが理解しているように、私はかなり混乱しており、コメントを受け付けています。
よろしくお願いします。