溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Security登錄認證的流程是什么

發(fā)布時間:2022-01-10 14:18:03 來源:億速云 閱讀:135 作者:iii 欄目:開發(fā)技術

這篇文章主要介紹“Security登錄認證的流程是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Security登錄認證的流程是什么”文章能幫助大家解決問題。

一、前言:流程圖:

Security登錄認證的流程是什么

二、前臺發(fā)送請求

用戶向/login接口使用POST方式提交用戶名、密碼。/login是沒指定時默認的接口

三、請求到達UsernamePasswordAuthenticationFilter過濾器

請求首先會來到:????UsernamePasswordAuthenticationFilter

/**
UsernamePasswordAuthenticationFilter:處理身份驗證表單提交
以及將請求信息封裝為Authentication 然后返回給上層父類,
父類再通過 SecurityContextHolder.getContext().setAuthentication(authResult); 將驗證過的Authentication 保存至安全上下文中
 */
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";

	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

	private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login",
			"POST");

    //可以通過對應的set方法修改
	private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
	private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;

	private boolean postOnly = true;

       //  初始化一個用戶密碼 認證過濾器  默認的登錄uri 是 /login 請求方式是POST
	public UsernamePasswordAuthenticationFilter() {
		super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
	}

	public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
		super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
	}

	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		if (this.postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
		String username = obtainUsername(request);
		username = (username != null) ? username : "";
		username = username.trim();
		String password = obtainPassword(request);
		password = (password != null) ? password : "";
       	//把賬號名、密碼封裝到一個認證Token對象中,這是一個通行證,但是此時的狀態(tài)時不可信的,通過認證后才會變?yōu)榭尚诺?
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
		// Allow subclasses to set the "details" property
        //記錄遠程地址,如果會話已經(jīng)存在(它不會創(chuàng)建),還將設置會話 ID
		setDetails(request, authRequest);
        //使用 父類中的 AuthenticationManager 對Token 進行認證 
		return this.getAuthenticationManager().authenticate(authRequest);
	}

	/**
		obtainUsername和obtainPassword就是方便從request中獲取到username和password
		實際上如果在前后端分離的項目中 我們大都用不上????  因為前端傳過來的是JSON數(shù)據(jù),我們通常是使用JSON工具類進行解析
	 */
	@Nullable
	protected String obtainPassword(HttpServletRequest request) {
		return request.getParameter(this.passwordParameter);
	}
	@Nullable
	protected String obtainUsername(HttpServletRequest request) {
		return request.getParameter(this.usernameParameter);
	}

	/**
		提供以便子類可以配置放入身份驗證請求的詳細信息
	 */
	protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
		authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
	}
	
    /**
	...省略一些不重要的代碼 set get
	*/
}

四、制作UsernamePasswordAuthenticationToken

將獲取到的數(shù)據(jù)制作成一個令牌UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

之前我們在圖中講了我們實際封裝的是一個Authentication對象,UsernamePasswordAuthenticationToken是一個默認實現(xiàn)類。

我們簡單看一下他們的結構圖:

Security登錄認證的流程是什么

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    // 這里就是用戶名和密碼 自定義時 根據(jù)自己需求進行重寫
	private final Object principal;
	private Object credentials;

	/**
//把賬號名、密碼封裝到一個認證UsernamePasswordAuthenticationToken對象中,這是一個通行證,但是此時的狀態(tài)時不可信的,
//我們在這也可以看到 權限是null, setAuthenticated(false);是表示此刻身份是未驗證的 所以此時狀態(tài)是不可信的
	 */
	public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);
	}

	/**	這個時候才是可信的狀態(tài) */
	public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
			Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		this.credentials = credentials;
		super.setAuthenticated(true); // must use super, as we override
	}
	// ...
}

目前是處于未授權狀態(tài)的。我們后面要做的就是對它進行認證授權。

五、父類中的 AuthenticationManager 對Token 進行認證

AuthenticationManager是身份認證器,認證的核心接口

我們繼續(xù)對return this.getAuthenticationManager().authenticate(authRequest);進行分析.

//我們可以看到 AuthenticationManager 實際上就是一個接口,所以它并不做真正的事情,只是提供了一個標準,我們就繼續(xù)去看看它的實現(xiàn)類,看看是誰幫它做了事。
public interface AuthenticationManager {
    //嘗試對傳遞的Authentication對象進行身份Authentication ,如果成功則返回完全填充的Authentication對象(包括授予的權限)。
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

六、我們找到了AuthenticationManager 實現(xiàn)類ProviderManager

我們找到ProviderManager實現(xiàn)了AuthenticationManager。(但是你會發(fā)現(xiàn)它也不做事,又交給了別人做????)

ProviderManager并不是自己直接對請求進行驗證,而是將其委派給一個 AuthenticationProvider列表。列表中的每一個 AuthenticationProvider將會被依次查詢是否需要通過其進行驗證,每個 provider的驗證結果只有兩個情況:拋出一個異?;蛘咄耆畛湟粋€ Authentication對象的所有屬性。

在這個閱讀中,我刪除了許多雜七雜八的代碼,一些判斷,異常處理,我都去掉了,只針對最重要的那幾個看。

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {

    //省略了一些代碼
    private List<AuthenticationProvider> providers = Collections.emptyList();
    
	/**
	 * 嘗試對傳遞的Authentication對象進行身份Authentication 。AuthenticationProvider的列表將被連續(xù)嘗試,
	 * 直到AuthenticationProvider表明它能夠驗證所傳遞的Authentication對象的類型。 然后將嘗試使用該AuthenticationProvider 。
	 */
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		Authentication result = null;
		Authentication parentResult = null;
		int currentPosition = 0;
		int size = this.providers.size();
 
        //我們遍歷AuthenticationProvider 列表中每個Provider依次進行認證
       // 不過你會發(fā)現(xiàn) AuthenticationProvider 也是一個接口,它的實現(xiàn)類才是真正做事的人 ,下文有
		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {
				continue;
			}
			//...
			try {
                //provider.authenticate()
                //參數(shù):身份驗證 - 身份驗證請求對象。
                //返回:一個完全經(jīng)過身份驗證的對象,包括憑據(jù)。 如果AuthenticationProvider無法支持對傳遞的Authentication對象進行身份驗證,則可能返回null  ,我們接著看它的實現(xiàn)類是什么樣子的
				result = provider.authenticate(authentication);
				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException | InternalAuthenticationServiceException ex) {
			//....
			}
		}
        // 如果 AuthenticationProvider 列表中的Provider都認證失敗,且之前有構造一個 AuthenticationManager 實現(xiàn)類,那么利用AuthenticationManager 實現(xiàn)類 繼續(xù)認證
		if (result == null && this.parent != null) {
			// Allow the parent to try.
			try {
				parentResult = this.parent.authenticate(authentication);
				result = parentResult;
			}
			catch (ProviderNotFoundException ex) {
			// ...
			}
		}
         //認證成功
		if (result != null) {
			if (eraseCredentialsAfterAuthentication
					&& (result instanceof CredentialsContainer)) {
				// Authentication is complete. Remove credentials and other secret data
				// from authentication
				//成功認證后刪除驗證信息
				((CredentialsContainer) result).eraseCredentials();
			}
            //發(fā)布登錄成功事件
			eventPublisher.publishAuthenticationSuccess(result);
			return result;
		}
		// 沒有認證成功,拋出異常
		if (lastException == null) {
			lastException = new ProviderNotFoundException(messages.getMessage(
					"ProviderManager.providerNotFound",
					new Object[] { toTest.getName() },
					"No AuthenticationProvider found for {0}"));
		}
		prepareException(lastException, authentication);
		throw lastException;
	}
}

七、AuthenticationProvider接口

public interface AuthenticationProvider {

	/**
	認證方法
	參數(shù):身份驗證 - 身份驗證請求對象。
	返回:一個完全經(jīng)過身份驗證的對象,包括憑據(jù)。
	 */
	Authentication authenticate(Authentication authentication) throws AuthenticationException;

	/**
		該Provider是否支持對應的Authentication
		如果此AuthenticationProvider支持指定的Authentication對象,則返回true 。	
	 */
	boolean supports(Class<?> authentication);

}

注意boolean supports(Class<?> authentication);方式上完整JavaDoc的注釋是:

如果有多個 AuthenticationProvider都支持同一個Authentication 對象,那么第一個 能夠成功驗證Authentication的 Provder 將填充其屬性并返回結果從而覆蓋早期支持的 AuthenticationProvider拋出的任何可能的 AuthenticationException。一旦成功驗證后,將不會嘗試后續(xù)的 AuthenticationProvider。如果所有的 AuthenticationProvider都沒有成功驗證 Authentication,那么將拋出最后一個Provider拋出的AuthenticationException。(AuthenticationProvider可以在Spring Security配置類中配置)

機譯不是很好理解,我們翻譯成通俗易懂點:

當然有時候我們有多個不同的 AuthenticationProvider,它們分別支持不同的 Authentication對象,那么當一個具體的 AuthenticationProvier傳進入 ProviderManager的內(nèi)部時,就會在 AuthenticationProvider列表中挑選其對應支持的provider對相應的 Authentication對象進行驗證

這個知識和實現(xiàn)多種登錄方式相關聯(lián),我簡單的說一下我的理解。

我們這里講解的是默認的登錄方式,用到的是UsernamePasswordAuthenticationFilter和UsernamePasswordAuthenticationToken以及后文中的DaoAuthenticationProvider這些,來進行身份的驗證,但是如果我們后期需要添加手機短信驗證碼登錄或者郵件驗證碼或者第三方登錄等等。

那么我們也會重新繼承AbstractAuthenticationProcessingFilter、AbstractAuthenticationToken、AuthenticationProvider進行重寫,因為不同的登錄方式認證邏輯是不一樣的,AuthenticationProvider也會不一樣,我們使用用戶名和密碼登錄,Security 提供了一個 AuthenticationProvider的簡單實現(xiàn) DaoAuthenticationProvider,它使用了一個 UserDetailsService來查詢用戶名、密碼和 GrantedAuthority,實際使用中我們都會實現(xiàn)UserDetailsService接口,從數(shù)據(jù)庫中查詢相關用戶信息,AuthenticationProvider的認證核心就是加載對應的 UserDetails來檢查用戶輸入的密碼是否與其匹配。

流程圖大致如下:

Security登錄認證的流程是什么

八、DaoAuthenticationProvider

AuthenticationProvider它的實現(xiàn)類、繼承類很多,我們直接看和User相關的,會先找到AbstractUserDetailsAuthenticationProvider這個抽象類。

我們先看看這個抽象類,然后再看它的實現(xiàn)類,看他們是如何一步一步遞進的。

/**
一個基本的AuthenticationProvider ,它允許子類覆蓋和使用UserDetails對象。 該類旨在響應UsernamePasswordAuthenticationToken身份驗證請求。
驗證成功后,將創(chuàng)建UsernamePasswordAuthenticationToken并將其返回給調(diào)用者。 令牌將包括用戶名的String表示或從身份驗證存儲庫返回的UserDetails作為其主體。
 */
public abstract class AbstractUserDetailsAuthenticationProvider
		implements AuthenticationProvider, InitializingBean, MessageSourceAware {

    //...省略了一些代碼
	private UserCache userCache = new NullUserCache();

	private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();

    
	//認證方法
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
				() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
						"Only UsernamePasswordAuthenticationToken is supported"));
        //判斷用戶名是否為空
		String username = determineUsername(authentication);
		boolean cacheWasUsed = true;
        //先查緩存
		UserDetails user = this.userCache.getUserFromCache(username);
		if (user == null) {
			cacheWasUsed = false;
			try {
				user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
			}
			catch (UsernameNotFoundException ex) {
				this.logger.debug("Failed to find user '" + username + "'");
				if (!this.hideUserNotFoundExceptions) {
					throw ex;
				}
				throw new BadCredentialsException(this.messages
						.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
			}
			Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
		}
		try {
            //一些檢查
			this.preAuthenticationChecks.check(user);
			additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
		}
		catch (AuthenticationException ex) {
			if (!cacheWasUsed) {
				throw ex;
			}
			// There was a problem, so try again after checking
			// we're using latest data (i.e. not from the cache)
			cacheWasUsed = false;
            //retrieveUser 是個沒有抽象的方法 稍后我們看看它的實現(xiàn)類是如何實現(xiàn)的
			user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
            //一些檢查信息 用戶是否可用什么的
			this.preAuthenticationChecks.check(user);
			additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
		}
		this.postAuthenticationChecks.check(user);
		if (!cacheWasUsed) {
			this.userCache.putUserInCache(user);
		}
		Object principalToReturn = user;
		if (this.forcePrincipalAsString) {
			principalToReturn = user.getUsername();
		}
        //創(chuàng)建一個成功的Authentication對象。
		return createSuccessAuthentication(principalToReturn, authentication, user);
	}

	private String determineUsername(Authentication authentication) {
		return (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
	}

	/**
		創(chuàng)建一個成功的Authentication對象。 這個也允許字類進行實現(xiàn)。
		如果要給密碼加密的話,一般字類都會重新進行實現(xiàn)
	 */
	protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
			UserDetails user) {
		//身份信息在這里也加入進去了
		UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
				authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
		result.setDetails(authentication.getDetails());
		this.logger.debug("Authenticated user");
		return result;
	}



	/**
允許子類從特定于實現(xiàn)的位置實際檢索UserDetails ,如果提供的憑據(jù)不正確,則可以選擇立即拋出AuthenticationException (如果需要以用戶身份綁定到資源以獲得或生成一個UserDetails )
	 */
	protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException;

    //...
}

DaoAuthenticationProvider:真正做事情的人

/**
從UserDetailsService檢索用戶詳細信息的AuthenticationProvider實現(xiàn)。
 */
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    // ...省略了一些代碼
    
	/** */
	@Override
	protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
		try {
            //UserDetailsService簡單說就是加載對應的UserDetails的接口(一般從數(shù)據(jù)庫),而UserDetails包含了更詳細的用戶信息
            //通過loadUserByUsername獲取用戶信息 ,返回一個 UserDetails 
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
			if (loadedUser == null) {
				throw new InternalAuthenticationServiceException(
						"UserDetailsService returned null, which is an interface contract violation");
			}
			return loadedUser;
		}
		catch (UsernameNotFoundException ex) {
			mitigateAgainstTimingAttack(authentication);
			throw ex;
		}
		catch (InternalAuthenticationServiceException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
		}   
	}
   	// 重新父類的方法,對密碼進行一些加密操作
    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
                                                         UserDetails user) {
        boolean upgradeEncoding = this.userDetailsPasswordService != null
            && this.passwordEncoder.upgradeEncoding(user.getPassword());
        if (upgradeEncoding) {
            String presentedPassword = authentication.getCredentials().toString();
            String newPassword = this.passwordEncoder.encode(presentedPassword);
            user = this.userDetailsPasswordService.updatePassword(user, newPassword);
        }
        return super.createSuccessAuthentication(principal, authentication, user);
    }

	//...
}

九、UserDetailsService和UserDetails接口

UserDetailsService簡單說就是定義了一個加載對應的UserDetails的接口,我們在使用中,大都數(shù)都會實現(xiàn)這個接口,從數(shù)據(jù)庫中查詢相關的用戶信息。

//加載用戶特定數(shù)據(jù)的核心接口。
public interface UserDetailsService {
		//根據(jù)用戶名定位用戶
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetails也是一個接口,實際開發(fā)中,同樣對它也會進行實現(xiàn),進行定制化的使用。

/**
提供核心用戶信息。
出于安全目的,Spring Security 不直接使用實現(xiàn)。 它們只是存儲用戶信息,然后將這些信息封裝到Authentication對象中。 這允許將非安全相關的用戶信息(例如電子郵件地址、電話號碼等)存儲在方便的位置。
 */
public interface UserDetails extends Serializable {
	//返回授予用戶的權限。 
	Collection<? extends GrantedAuthority> getAuthorities();
	String getPassword();
	String getUsername();

	//指示用戶的帳戶是否已過期。 無法驗證過期帳戶
	boolean isAccountNonExpired();

	//指示用戶是被鎖定還是未鎖定。 無法對鎖定的用戶進行身份驗證。
	boolean isAccountNonLocked();

	//指示用戶的憑據(jù)(密碼)是否已過期。 過期的憑據(jù)會阻止身份驗證。
	boolean isCredentialsNonExpired();

	//指示用戶是啟用還是禁用。 無法對禁用的用戶進行身份驗證。
	boolean isEnabled();
}

10、返回過程

1、DaoAuthenticationProvider類下UserDetails retrieveUser()方法中通過this.getUserDetailsService().loadUserByUsername(username);獲取到用戶信息后;

2、將UserDetails返回給父類AbstractUserDetailsAuthenticationProvider中的調(diào)用處(即Authentication authenticate(Authentication authentication)方法中)

3、AbstractUserDetailsAuthenticationProvider拿到返回的UserDetails后,最后返回給調(diào)用者的是return createSuccessAuthentication(principalToReturn, authentication, user); 這里就是創(chuàng)建了一個可信的 UsernamePasswordAuthenticationToken,即身份憑證。

protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
                                                     UserDetails user) {
    UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
                                                                                         authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
    result.setDetails(authentication.getDetails());
    this.logger.debug("Authenticated user");
    return result;
}

4、我們再回到ProviderManagerAuthentication authenticate(Authentication authentication)方法中的調(diào)用處,這個時候我們的用戶信息已經(jīng)是驗證過的,我們接著向上層調(diào)用處返回。

5、回到UsernamePasswordAuthenticationFilter中的return this.getAuthenticationManager().authenticate(authRequest);語句中,這個時候還得繼續(xù)向上層返回

6、返回到AbstractAuthenticationProcessingFilter中,我們直接按ctrl+b看是誰調(diào)用了它。

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
    implements ApplicationEventPublisherAware, MessageSourceAware {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }

    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);
            return;
        }
        try {
            // 這里就是調(diào)用處。
            Authentication authenticationResult = attemptAuthentication(request, response);
            if (authenticationResult == null) {
                // return immediately as subclass has indicated that it hasn't completed
                return;
            }
            // session相關,這里我們不深聊
            //發(fā)生新的身份驗證時執(zhí)行與 Http 會話相關的功能。
            this.sessionStrategy.onAuthentication(authenticationResult, request, response);
            // Authentication success
            if (this.continueChainBeforeSuccessfulAuthentication) {
                chain.doFilter(request, response);
            }
            //看方法名我們就知道 這是我們需要的拉
            //成功驗證省份后調(diào)用
            successfulAuthentication(request, response, chain, authenticationResult);
        }
        catch (InternalAuthenticationServiceException failed) {
            this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
            //驗證失敗調(diào)用
            unsuccessfulAuthentication(request, response, failed);
        }
        catch (AuthenticationException ex) {
            // Authentication failed
            //驗證失敗調(diào)用
            unsuccessfulAuthentication(request, response, ex);
        }
    }
}
//成功身份驗證的默認行為。
	//1、在SecurityContextHolder上設置成功的Authentication對象
	//2、通知配置的RememberMeServices登錄成功
	//3、通過配置的ApplicationEventPublisher觸發(fā)InteractiveAuthenticationSuccessEvent
	//4、將附加行為委托給AuthenticationSuccessHandler 。
//子類可以覆蓋此方法以在身份驗證成功后繼續(xù)FilterChain 。
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                        Authentication authResult) throws IOException, ServletException {
    //將通過驗證的Authentication保存至安全上下文
    SecurityContextHolder.getContext().setAuthentication(authResult);
    if (this.logger.isDebugEnabled()) {
        this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
    }
    this.rememberMeServices.loginSuccess(request, response, authResult);
    if (this.eventPublisher != null) {
        this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
    }
    this.successHandler.onAuthenticationSuccess(request, response, authResult);
}

其實不管是驗證成功調(diào)用或是失敗調(diào)用,大都數(shù)我們在實際使用中,都是需要重寫的,返回我們自己想要返回給前端的數(shù)據(jù)。

關于“Security登錄認證的流程是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識,可以關注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI