差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
study:java:rememberme [2025/02/03 09:53] – [webSecurityConfig.xml] bananastudy:java:rememberme [2025/02/07 10:06] (現在) banana
行 2: 行 2:
 ログインを維持してくれるremember-meログイン機能をSpring securityを用いて実装する方法を紹介します。\\ ログインを維持してくれるremember-meログイン機能をSpring securityを用いて実装する方法を紹介します。\\
 実装形態は次節で説明しますが、ここでは、persistence token実装形態を選択しています。 実装形態は次節で説明しますが、ここでは、persistence token実装形態を選択しています。
 +{{keywords>Spring rememeber-me login}}
  
 ====== remember-me実装形態 ====== ====== remember-me実装形態 ======
行 37: 行 38:
  
 ^ライブラリ^説明^ ^ライブラリ^説明^
-|jcl-over-slf4j-1.7.36.jar|Spring security logging関連| +|jcl-over-slf4j-1.7.36.jar|spring security logging関連| 
-|log4j-slf4j-impl-2.17.1.jar|log4j2 bridgeライブラリ+|log4j-slf4j-impl-2.17.1.jar|spring security loggingをlog4j2.xmlにて設定可能にする
-|slf4j-api-1.7.32.jar|loggingライブラリ|+|slf4j-api-1.7.32.jar|spring security logging関連|
 |spring-web-4.3.30.RELEASE.jar|spring-web| |spring-web-4.3.30.RELEASE.jar|spring-web|
 |spring-core-4.3.30.RELEASE.jar|spring-webの依存ライブラリ| |spring-core-4.3.30.RELEASE.jar|spring-webの依存ライブラリ|
行 308: 行 309:
  
 ★1\\ ★1\\
-ログインUrl、認証成功Handlerクラス、認証失敗時の遷移Url等を設定します。\\+ログインUrl、ログイン成功Handlerクラス、認証失敗時の遷移Url等を設定します。\\
 認証成功HandlerクラスであるmySavedRequestAwareAuthenticationSuccessHandlerについては後ほど説明します。\\ 認証成功HandlerクラスであるmySavedRequestAwareAuthenticationSuccessHandlerについては後ほど説明します。\\
  
行 345: 行 346:
 ★10\\ ★10\\
 ★4で設定しているLdapUserDetailsServiceについて定義しています。 ★4で設定しているLdapUserDetailsServiceについて定義しています。
 +
 +===== MySavedRequestAwareAuthenticationSuccessHandler.java =====
 +webSecurityConfig.xmlにて参照名mySavedRequestAwareAuthenticationSuccessHandlerのクラスを以下に示します。
 +<code java>
 +package com.contoso.web.security;
 +
 +import java.io.IOException;
 +
 +import javax.servlet.ServletException;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.security.core.Authentication;
 +import org.springframework.security.ldap.userdetails.LdapUserDetails;
 +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
 +import org.springframework.stereotype.Component;
 +import org.springframework.util.StringUtils;
 +
 +/**
 + * <H3>
 + * Custom implementation of SavedRequestAwareAuthenticationSuccessHandler
 + * </H3>
 + * @author ri-su
 + */
 +@Component(value = "mySavedRequestAwareAuthenticationSuccessHandler")
 +public class MySavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
 +
 + private static final Logger logger = LoggerFactory.getLogger(MySavedRequestAwareAuthenticationSuccessHandler.class);
 + //ログイン画面直アクセスの場合の遷移先
 + private static final String DEFAULT_TARGET_URL = "/sample/Main.do";
 +
 + //***** public method *****
 +
 + /* (non-Javadoc)
 + * @see org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.Authentication)
 + */
 + @Override
 + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
 + throws ServletException, IOException { ★1
 + //authorization
 + doAuthorization(request, authentication);
 + //set defaultTargetUrl
 + setDefaultTargetUrl(request);
 + //delegate to super method
 + super.onAuthenticationSuccess(request, response, authentication);
 + }
 +
 + //***** protected method *****
 + //***** private method *****
 +
 + //authorization
 + private void doAuthorization(final HttpServletRequest request, final Authentication authentication) {
 + //username
 + String _username = ((LdapUserDetails)authentication.getPrincipal()).getUsername(); ★2
 + logger.debug("username: {}", _username);
 +
 +                //do something here. 例)権限付与、セッションにユーザー情報保持
 + }//doAuthorization
 +
 + private void setDefaultTargetUrl(final HttpServletRequest request) {
 + String _targetUrl = "";
 + //REQUEST_CACHEのredirectUrlをログイン画面のhiddenから取得
 + String _cachedUrl = request.getParameter(REDIRECT_URL_HIDDEN_NAME);
 + String _referer = request.getParameter(REFERER_URL_HIDDEN_NAME);
 + logger.debug("cached redirect url: {}", _cachedUrl);
 + logger.debug("referer url: {}", _referer);
 + //determine targetUrl
 + if (StringUtils.hasText(_referer)) {
 + _targetUrl = determineTargetUrlFromReferer(_referer);
 + } else if (StringUtils.hasText(_cachedUrl)) {
 + _targetUrl = _cachedUrl; ★3
 + } else {
 + _targetUrl = DEFAULT_TARGET_URL;
 + }
 + logger.debug("target url: {}", _targetUrl);
 +
 + //set defaultTargetUrl
 + super.setDefaultTargetUrl(_targetUrl);
 + //always redirect to the value of defaultTargetUrl
 + setAlwaysUseDefaultTargetUrl(true);
 + }//setDefaultTargetUrl
 +
 +        //refererUrl(ログアウト時)からtargetUrl判断
 + private String determineTargetUrlFromReferer(String referer) {
 + String _targetUrl = "";
 + //some logic here ★4
 + return _targetUrl;
 + }//determineTargetUrlFromReferer
 +
 + //***** call back method *****
 + //***** getter and setter *****
 +
 +}
 +
 +</code>
 +
 +★1\\
 +onAuthenticationSuccessメソッドをオーバーライドします。\\
 +ここでは、認証成功後の権限付与(authorization)、遷移先などの設定を行います。\\
 +
 +★2\\
 +AuthenticationからLDAP認証のusernameが取得可能です。\\
 +usernameを用いてアプリ側のauthorization処理を実装します。\\
 +
 +★3\\
 +REQUEST_CACHEのredirectUrlをログイン画面のhiddenから取得します。\\
 +REQUEST_CACHEに残っていれば、そこに遷移します。\\
 +
 +★4\\
 +ログアウト時のurlがログイン画面のhiddenから取得できれば、ログアウト時の画面に遷移します。
 +
 +===== RememberMeAuthenticationSuccessHandler.java =====
 +webSecurityConfig.xmlにて参照名rememberMeAuthenticationSuccessHandlerのクラスを以下に示します。
 +<code java>
 +package com.contoso.web.security;
 +
 +import java.io.IOException;
 +
 +import javax.servlet.ServletException;
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +import javax.servlet.http.HttpSession;
 +
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.security.core.Authentication;
 +import org.springframework.security.ldap.userdetails.LdapUserDetails;
 +import org.springframework.security.web.DefaultRedirectStrategy;
 +import org.springframework.security.web.RedirectStrategy;
 +import org.springframework.security.web.WebAttributes;
 +import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
 +import org.springframework.stereotype.Component;
 +
 +/**
 + * <H3>
 + * authentication-success-handler in remember-me
 + * </H3>
 + * @author ri-su
 + */
 +@Component(value = "rememberMeAuthenticationSuccessHandler")
 +public class RememberMeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
 +
 + private static final Logger logger = LoggerFactory.getLogger(RememberMeAuthenticationSuccessHandler.class);
 + private static final String DEFAULT_TARGET_URL = "/sample/Main.do";
 + private static final String LOGON_URL = "/logon.do";
 + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
 +
 + //***** public method *****
 +
 + /* (non-Javadoc)
 + * @see org.springframework.security.web.authentication.AuthenticationSuccessHandler#onAuthenticationSuccess
 + * (javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.Authentication)
 + */
 + @Override
 + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
 + throws IOException, ServletException { ★1
 + handle(request, response, authentication);
 + clearAuthenticationAttributes(request);
 + }
 +
 + //***** protected method *****
 +
 + protected void handle(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
 + //--- determine targetUrl
 + final String _targetUrl = determineTargetUrl(request);
 +
 + if (response.isCommitted()) {
 + logger.debug("Response has already been committed. Unable to redirect to " + _targetUrl);
 + return;
 + }
 + //authorization
 + doAuthorization(request, authentication);
 + redirectStrategy.sendRedirect(request, response, _targetUrl);
 + }//handle
 +
 + /**
 + * determine target URL
 + * <br>
 + * @param request
 + * @return
 + */
 + protected String determineTargetUrl(final HttpServletRequest request) {
 + String _targetUrl = request.getServletPath();
 + logger.debug("targetUrl: {}", _targetUrl);
 + String _redirectUrl = "";
 + if (!Validator.isNullOrBlank(_targetUrl) &&
 + _targetUrl.indexOf(LOGON_URL) != -1) { ★2
 +     _redirectUrl = DEFAULT_TARGET_URL;
 + } else {
 + _redirectUrl = _targetUrl;
 + }
 + return _redirectUrl;
 + }//determineTargetUrl
 +
 +      /**
 +       * Removes temporary authentication-related data which may have been stored in the session
 +       * during the authentication process.
 +       */
 + protected final void clearAuthenticationAttributes(final HttpServletRequest request) {
 + final HttpSession _session = request.getSession(false);
 +
 + if (_session == null) {
 + return;
 + }
 +
 + _session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
 + }//clearAuthenticationAttributes
 +
 + //***** private method *****
 +
 + //authorization
 + private void doAuthorization(final HttpServletRequest request, Authentication authentication) {
 + //username
 + String _username = ((LdapUserDetails)authentication.getPrincipal()).getUsername(); ★3
 + logger.debug("username: {}", _username);
 +
 +        //do something here. 例)権限付与、セッションにユーザー情報保持
 + }//doAuthorization
 +
 + //***** call back method *****
 + //***** getter and setter *****
 +
 +}
 +
 +</code>
 +
 +★1\\
 +onAuthenticationSuccessメソッドをオーバーライドします。\\
 +ここでは、認証成功後の権限付与(authorization)、遷移先などの設定を行います。\\
 +
 +★2\\
 +ログイン画面にアクセスする場合の遷移先を指定します。\\
 +
 +★3\\
 +AuthenticationからLDAP認証のusernameが取得可能です。\\
 +usernameを用いてアプリ側のauthorization処理を実装します。\\をを
 +===== NewLogon.jsp =====
 +Formログイン画面の例を以下に示します。
 +<code>
 +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
 +<%@ include file="/Taglibs.jsp" %>
 +
 +<form name="f" action="${pageContext.request.contextPath}/logon.do" method="POST">
 + <table>
 + <tr>
 + <td><bean:message key="label.mothers" /></td>
 + <td><input type="text" name="username" size="9" maxlength="7"></td>
 + </tr>
 + <tr>
 + <td><bean:message key="label.passWord" /></td>
 + <td><input type="password" name="password" size="20" maxlength="25" /></td>
 + </tr>
 + <tr>
 + <td>Remember Me:</td>
 + <td><input type="checkbox" name="remember-me" /></td>
 + </tr>
 + <tr>
 + <td><input name="submit" type="submit" value="<bean:message key='label.login' />" /></td>
 + </tr>
 + </table>
 + <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
 + <input type="hidden" name="CACHED_REQUEST_URL" value="${sessionScope.SPRING_SECURITY_SAVED_REQUEST.redirectUrl}" />
 + <input type="hidden" name="REFERER_URL" value="${header['Referer']}" />
 +</form>
 +
 +</code>
 +
 +===== NewLogonAction.java =====
 +ログイン画面Actionの例を以下に示します。\\
 +ここではStruts1を利用したActionクラスの例です。
 +<code java>
 +package com.contoso.web;
 +
 +import javax.servlet.http.HttpServletRequest;
 +import javax.servlet.http.HttpServletResponse;
 +
 +import org.apache.struts.action.Action;
 +import org.apache.struts.action.ActionError;
 +import org.apache.struts.action.ActionErrors;
 +import org.apache.struts.action.ActionForward;
 +import org.apache.struts.action.ActionMapping;
 +
 +/**
 + * <H3>
 + * Action for Remember-me Login
 + * </H3>
 + * @author ri-su
 + */
 +public class NewLogonAction extends Action {
 +
 + //***** public method *****
 +
 + @Override
 + public ActionForward execute(ActionMapping mapping, AbstractActionForm form, HttpServletRequest request,
 + HttpServletResponse response) throws Exception
 + if (request.getParameter("error") != null) { ★1
 + ActionErrors _errors = new ActionErrors();
 + _errors.add("username", new ActionError("errors.logon.fail"));
 + saveErrors(request, _errors);
 + return mapping.getInputForward();
 + }
 +
 + return mapping.findForward(SUCCESS);
 + }
 +
 + //***** protected method *****
 + //***** private method *****
 + //***** call back method *****
 + //***** getter and setter *****
 +
 +}
 +
 +</code>
 +
 +★1\\
 +ログインエラー発生時、エラーメッセージをActionErrorにセットして画面に表示します。
  
 ====== 参考情報 ====== ====== 参考情報 ======

QR Code
QR Code study:java:rememberme (generated for current page)