欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Spring-Security自定義登陸錯誤提示信息

 更新時間:2021年12月20日 10:29:09   作者:doinbb  
這篇文章主要介紹了Spring-Security自定義登陸錯誤提示信息,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

實現效果如圖所示:

首先公布實現代碼:

一. 自定義實現

import.org.springframework.security.core.userdetails.UserDetailsService類

并且拋出BadCredentialsException異常,否則頁面無法獲取到錯誤信息。

 
@Slf4j
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService { 
    @Autowired
    private PasswordEncoder passwordEncoder; 
    @Autowired
    private UserService userService; 
    @Autowired
    private PermissionService permissionService; 
    private String passwordParameter = "password"; 
    @Override
    public UserDetails loadUserByUsername(String username) throws AuthenticationException {
        HttpServletRequest request = ContextHolderUtils.getRequest();
        String password = request.getParameter(passwordParameter);
        log.error("password = {}", password);
 
        SysUser sysUser = userService.getByUsername(username);
        if (null == sysUser) {
            log.error("用戶{}不存在", username);
            throw new BadCredentialsException("帳號不存在,請重新輸入");
        }
        // 自定義業(yè)務邏輯校驗
        if ("userli".equals(sysUser.getUsername())) {
            throw new BadCredentialsException("您的帳號有違規(guī)記錄,無法登錄!");
        }
        // 自定義密碼驗證
        if (!password.equals(sysUser.getPassword())){
            throw new BadCredentialsException("密碼錯誤,請重新輸入");
        }
        List<SysPermission> permissionList = permissionService.findByUserId(sysUser.getId());
        List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(permissionList)) {
            for (SysPermission sysPermission : permissionList) {
                authorityList.add(new SimpleGrantedAuthority(sysPermission.getCode()));
            }
        }
 
        User myUser = new User(sysUser.getUsername(), passwordEncoder.encode(sysUser.getPassword()), authorityList); 
        log.info("登錄成功!用戶: {}", myUser); 
        return myUser;
    }
}

二. 實現自定義登陸頁面

前提是,你們已經解決了自定義登陸頁面配置的問題,這里不做討論。

通過 thymeleaf 表達式獲取錯誤信息(我們選擇thymeleaf模板引擎)

<p style="color: red" th:if="${param.error}"   th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}"></p>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>XX相親網</title>
    <meta name="description" content="Ela Admin - HTML5 Admin Template">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body class="mui-content">
<div id="d1">
    <div class="first">
        <img class="hosp" th:src="@{/images/dashboard/hospital.png}"/>
        <div class="hospital">XX相親網</div>
    </div>
    <div class="sufee-login d-flex align-content-center flex-wrap">
        <div class="container">
            <div class="login-content">
                <div class="login-logo">
                    <h1 style="color: #385978;font-size: 24px">XX相親網</h1>
                    <h1 style="color: #385978;font-size: 24px">登錄</h1>
                </div>
                <div class="login-form">
                    <form th:action="@{/login}" method="post">
                        <div class="form-group">
                            <input type="text" class="form-control" name="username" placeholder="請輸入帳號">
                        </div>
                        <div class="form-group">
                            <input type="password" class="form-control" name="password" placeholder="請輸入密碼">
                        </div>
                        <div>
                            <button type="submit" class="button-style">
                                <span class="in">登錄</span>
                            </button>
                        </div>
                        <p style="color: red" th:if="${param.error}"             
                           th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}">
                        </p>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

Spring-Security登陸表單提交過程

當用戶從登錄頁提交賬號密碼的時候,首先由

org.springframework.security.web.authentication包下的UsernamePasswordAuthenticationFilter類attemptAuthentication()

方法來處理登陸邏輯。

 
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		} 

		String username = obtainUsername(request);
		String password = obtainPassword(request); 
		if (username == null) {
			username = "";
		}
 
		if (password == null) {
			password = "";
		}
 
		username = username.trim(); 
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password); 
		// Allow subclasses to set the "details" property
		setDetails(request, authRequest); 
		return this.getAuthenticationManager().authenticate(authRequest);
	}

1. 該類內部默認的登錄請求url是"/login",并且只允許POST方式的請求。

2. obtainUsername()方法參數名為"username"和"password"從HttpServletRequest中獲取用戶名和密碼(由此可以找到突破口,我們可以在自定義實現的loadUserByUsername方法中獲取到提交的賬號和密碼,進而檢查正則性)。

3. 通過構造方法UsernamePasswordAuthenticationToken,將用戶名和密碼分別賦值給principal和credentials。

    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
        super((Collection)null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }

super(null)調用的是父類的構造方法,傳入的是權限集合,因為目前還沒有認證通過,所以不知道有什么權限信息,這里設置為null,然后將用戶名和密碼分別賦值給principal和credentials,同樣因為此時還未進行身份認證,所以setAuthenticated(false)。

到此為止,用戶提交的表單信息已加載完成,繼續(xù)往下則是校驗表單提交的賬號和密碼是否正確。

那么異常一下是如何傳遞給前端的呢

前面提到用戶登錄驗證的過濾器是UsernamePasswordAuthenticationFilter,它繼承自AbstractAuthenticationProcessingFilter。

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException { 
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
 
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response); 
			return;
		}
 
		if (logger.isDebugEnabled()) {
			logger.debug("Request is to process authentication");
		}
 
		Authentication authResult; 
		try {
			authResult = attemptAuthentication(request, response);
			if (authResult == null) {
				// return immediately as subclass has indicated that it hasn't completed
				// authentication
				return;
			}
			sessionStrategy.onAuthentication(authResult, request, response);
		}
		catch (InternalAuthenticationServiceException failed) {
			logger.error(
					"An internal error occurred while trying to authenticate the user.",
					failed);
			unsuccessfulAuthentication(request, response, failed); 
			return;
		}
		catch (AuthenticationException failed) {
			// Authentication failed
			unsuccessfulAuthentication(request, response, failed); 
			return;
		}
 
		// Authentication success
		if (continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		} 
		successfulAuthentication(request, response, chain, authResult);
	}

從代碼片段中看到Spring將異常捕獲后交給了unsuccessfulAuthentication這個方法來處理。

unsuccessfulAuthentication又交給了failureHandler(AuthenticationFailureHandler)來處理,然后追蹤failureHandler

	protected void unsuccessfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException failed)
			throws IOException, ServletException {
		SecurityContextHolder.clearContext();
 
		if (logger.isDebugEnabled()) {
			logger.debug("Authentication request failed: " + failed.toString(), failed);
			logger.debug("Updated SecurityContextHolder to contain null Authentication");
			logger.debug("Delegating to authentication failure handler " + failureHandler);
		}
 
		rememberMeServices.loginFail(request, response); 
		failureHandler.onAuthenticationFailure(request, response, failed);
	}

Ctrl + 左鍵 追蹤failureHandler引用的類是,SimpleUrlAuthenticationFailureHandler。

	private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
	private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

找到SimpleUrlAuthenticationFailureHandler類中的,onAuthenticationFailure()方法。

	public void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {
 
		if (defaultFailureUrl == null) {
			logger.debug("No failure URL set, sending 401 Unauthorized error");
 
			response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
					"Authentication Failed: " + exception.getMessage());
		}
		else {
			saveException(request, exception);
 
			if (forwardToDestination) {
				logger.debug("Forwarding to " + defaultFailureUrl);
 
				request.getRequestDispatcher(defaultFailureUrl)
						.forward(request, response);
			}
			else {
				logger.debug("Redirecting to " + defaultFailureUrl);
				redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
			}
		}
	}

追蹤到saveException(request, exception)的內部實現。

	protected final void saveException(HttpServletRequest request,
			AuthenticationException exception) {
		if (forwardToDestination) {
			request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
		}
		else {
			HttpSession session = request.getSession(false);
 
			if (session != null || allowSessionCreation) {
				request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,
						exception);
			}
		}
	}

此處的

request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); 

就是存儲到session中的錯誤信息,key就是

public static final String AUTHENTICATION_EXCEPTION =
"SPRING_SECURITY_LAST_EXCEPTION";

因此我們通過thymeleaf模板引擎的表達式可獲得session的信息。

獲取方式

<p style="color: red" th:if="${param.error}" 
th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}">
</p>

需要注意:saveException保存的是Session對象所以需要使用${SPRING_SECURITY_LAST_EXCEPTION.message}獲取。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 解決SpringBoot跨域的三種方式

    解決SpringBoot跨域的三種方式

    前后端分離是目前的趨勢,解決跨域問題也是老生常談的話題了,我們了解一下什么是域和跨域。域:協(xié)議 + 域名 + 端口;三者完全相同則為同域,反之有其一不同均為不同域。跨域請求:當前【發(fā)起請求】的域和【請求指向】的域屬于不同域時,該次請求稱之為跨域請求
    2021-06-06
  • 深入淺出分析Java抽象類和接口【功能,定義,用法,區(qū)別】

    深入淺出分析Java抽象類和接口【功能,定義,用法,區(qū)別】

    這篇文章主要介紹了Java抽象類和接口,結合實例形式深入淺出的分析了java抽象類與接口的功能功能,定義,用法及區(qū)別,需要的朋友可以參考下
    2017-08-08
  • Java?常量池詳解之class文件常量池?和class運行時常量池

    Java?常量池詳解之class文件常量池?和class運行時常量池

    這篇文章主要介紹了Java?常量池詳解之class文件常量池?和class運行時常量池,常量池主要存放兩大類常量:字面量,符號引用,本文結合示例代碼對java class常量池相關知識介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • Java索引越界異常Exception java.lang.IndexOutOfBoundsException的解決

    Java索引越界異常Exception java.lang.IndexOutOfBoundsException

    本文主要介紹了Java索引越界異常Exception java.lang.IndexOutOfBoundsException的解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • Java五子棋簡單實現代碼舉例

    Java五子棋簡單實現代碼舉例

    Java五子棋游戲是一種經典的兩人對戰(zhàn)棋類游戲,它基于簡單的規(guī)則,即任何一方的棋子在棋盤上形成連續(xù)的五個,無論是橫、豎還是斜線,都將獲勝,這篇文章主要介紹了Java五子棋實現的相關資料,需要的朋友可以參考下
    2024-10-10
  • 基于FlashPaper實現JSP在線閱讀代碼示例

    基于FlashPaper實現JSP在線閱讀代碼示例

    這篇文章主要介紹了基于FlashPaper實現JSP在線閱讀代碼示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-10-10
  • Google Kaptcha 框架實現登錄驗證碼功能(SSM 和 SpringBoot)

    Google Kaptcha 框架實現登錄驗證碼功能(SSM 和 SpringBoot)

    這篇文章主要介紹了Google Kaptcha 實現登錄驗證碼(SSM 和 SpringBoot)功能,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-12-12
  • SpringBoot項目整合jasypt實現過程詳解

    SpringBoot項目整合jasypt實現過程詳解

    這篇文章主要介紹了SpringBoot項目整合jasypt實現過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • Java super關鍵字的用法詳解

    Java super關鍵字的用法詳解

    在JAVA類中使用super來引用父類的成分,用this來引用當前對象,如果一個類從另外一個類繼承,我們new這個子類的實例對象的時候,這個子類對象里面會有一個父類對象。怎么引用里面的父類對象呢?用super來引用,this指當前對象的引用,super是當前對象里面的父對象的引用
    2021-11-11
  • Springboot報錯java.lang.NullPointerException: null問題

    Springboot報錯java.lang.NullPointerException: null問題

    這篇文章主要介紹了Springboot報錯java.lang.NullPointerException: null問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11

最新評論