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

SpringSecurity身份認證原理解析

 更新時間:2023年09月18日 09:17:27   作者:還沒禿的小菜雞  
這篇文章主要介紹了SpringSecurity身份認證原理解析,身份認證時用戶名和密碼被過濾器獲取到,封裝成 Authentication ,通常情況下是 UsernamePasswordAuthenticationToken 這個實現(xiàn)類,需要的朋友可以參考下

Spring Security身份認證

  1. 用戶名和密碼被過濾器獲取到,封裝成 Authentication ,通常情況下是 UsernamePasswordAuthenticationToken 這個實現(xiàn)類。
  2. AuthenticationManager 身份管理器負責(zé)驗證這個 Authentication
  3. 認證成功后, AuthenticationManager 身份管理器返回一個被填充滿了信息的(包括上面提到的 權(quán)限信息,身份信息,細節(jié)信息,但密碼通常會被移除) Authentication 實例。
  4. SecurityContextHolder 安全上下文容器將第3步填充了信息的 Authentication ,通過 SecurityContextHolder.getContext().setAuthentication(…)方法,設(shè)置到其中。
public class AuthenticationExample {
    private static AuthenticationManager am = new SampleAuthenticationManager();
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            System.out.println("please enter your username:");
            String name = in.readLine();
            System.out.println("please enter your password:");
            String password = null;
            password = in.readLine();
            try {
                // 封裝認證信息,未認證通過
                UsernamePasswordAuthenticationToken request = new UsernamePasswordAuthenticationToken(name, password);
                // 認證邏輯
                Authentication result = am.authenticate(request);
                //當(dāng)前線程綁定認證信息
                SecurityContextHolder.getContext().setAuthentication(result);
                break;
            } catch (AuthenticationException e) {
                System.out.println("Authentication failed: " + e.getMessage());
            }
        }
    }
    static class SampleAuthenticationManager implements AuthenticationManager {
        static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
        static {
            AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
        }
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            // 判斷條件,用戶名和密碼是否相同
            if (authentication.getName().equals(authentication.getCredentials())){
                return new UsernamePasswordAuthenticationToken(authentication.getName(),authentication.getCredentials(),AUTHORITIES);
            }
            throw new BadCredentialsException("Bad Credentials");
        }
    }
}

測試:

在這里插入圖片描述

認證流程

在這里插入圖片描述

SecurityFilterChain 過濾器鏈

Spring Security采用的是filterChain的設(shè)計方式,主要的功能大都由過濾器實現(xiàn),在啟動項目的時候,可以在日志中看到已有的過濾器,可在類似下面的日志里找到 DefaultSecurityFilterChain ,這里面則是SecurityFilterChain

2021-01-07 11:27:30.410  INFO 13880 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@153cd6bb, org.springframework.security.web.context.SecurityContextPersistenceFilter@71f0b72e, org.springframework.security.web.header.HeaderWriterFilter@aa149ed, org.springframework.security.web.authentication.logout.LogoutFilter@2de50ee4, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@151ef57f, org.springframework.security.web.session.ConcurrentSessionFilter@5c73f672, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2f508f3c, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5eed2d86, org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter@36fc05ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@61d84e08, org.springframework.security.web.session.SessionManagementFilter@31ff6309, org.springframework.security.web.access.ExceptionTranslationFilter@10fbbdb, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4e1459ea]

把各個過濾器抽取出來,我們可以看到是這樣,這也是過濾器鏈的先后順序。

  1. WebAsyncManagerIntegrationFilter
  2. SecurityContextPersistenceFilter
  3. HeaderWriterFilter
  4. LogoutFilter
  5. UsernamePasswordAuthenticationFilter
  6. JwtAuthorizationTokenFilter
  7. RequestCacheAwareFilter
  8. SecurityContextHolderAwareRequestFilter
  9. SessionManagementFilter
  10. ExceptionTranslationFilter
  11. FilterSecurityInterceptor

介紹幾個主要的作用

  • SecurityContextPersistenceFilter
  • Filter的入口和出口,它是用來將SecurityContext(認證的上下文,里面有登錄成功后的認證授權(quán)信息)對象持久到Session的Filter,同時會把SecurityContext設(shè)置給SecurityContextHolder方便我們獲取用戶認證授權(quán)信息
  • UsernamePasswordAuthenticationFilter
  • 默認攔截“/login”登錄請求,處理表單提交的登錄認證,將請求中的認證信息包括username,password等封裝成UsernamePasswordAuthenticationToken,然后調(diào)用AuthenticationManager的認證方法進行認證
  • BasicAuthenticationFilter
  • 基本認證,支持httpBasic認證方式的Filter
  • RememberAuthenticationFilter
  • 記住我功能實現(xiàn)的Filter
  • AnonymousAuthenticationFilter
  • 匿名Filter,用來處理匿名訪問的資源,如果用戶未登錄,SecurityContext中沒有Authentication,就會創(chuàng)建匿名的Token(AnonymousAuthenticationToken),然后通過SecurityContextHodler設(shè)置到SecurityContext中。
  • ExceptionTranslationFilter
  • 用來捕獲FilterChain所有的異常,進行處理,但是只會處理 AuthenticationException和AccessDeniedException,異常,其他的異常 會繼續(xù)拋出。
  • FilterSecurityInterceptor

用來做授權(quán)的Filter,通過父類(AbstractSecurityInterceptor.beforeInvocation)調(diào)用AccessDecisionManager.decide方法對用戶進行授權(quán)。

UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter ,顧名思義,是用來處理用戶名密碼登錄的過濾器。所有的Filter核心方法都是 doFilter ,該過濾器的doFilter在其父抽象類中,過濾器只需實現(xiàn) attemptAuthentication 方法即可。

public class UsernamePasswordAuthenticationFilter extends
		AbstractAuthenticationProcessingFilter {
	// ~ Static fields/initializers
	// =====================================================================================
	//從登錄請求中獲取參數(shù):username,password的名字
	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
	private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
	private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
	//默認支持POST登錄
	private boolean postOnly = true;
	//默認攔截/login請求,Post方式
	public UsernamePasswordAuthenticationFilter() {
		super(new AntPathRequestMatcher("/login", "POST"));
	}
	// ~ Methods
	// ========================================================================================================
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
			//判斷請求是否是POST
		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();
		//用戶名和密碼封裝Token
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);
		//設(shè)置details屬性
		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
		//調(diào)用AuthenticationManager().authenticate進行認證,參數(shù)就是Token對象
		return this.getAuthenticationManager().authenticate(authRequest);
	}

AuthenticationManager

請求通過UsernamePasswordAuthenticationFilter調(diào)用AuthenticationManager,默認走的實現(xiàn)類是ProviderManager,它會找到能支持當(dāng)前認證的AuthenticationProvider實現(xiàn)類調(diào)用器authenticate方法執(zhí)行認證,認證成功后會清除密碼,然后拋出AuthenticationSuccessEvent事件

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
		InitializingBean {
		...省略...
		//這里authentication 是封裝了登錄請求的認證參數(shù),
		//即:UsernamePasswordAuthenticationFilter傳入的Token對象
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		AuthenticationException parentException = null;
		Authentication result = null;
		Authentication parentResult = null;
		boolean debug = logger.isDebugEnabled();
		//找到所有的AuthenticationProvider ,選擇合適的進行認證
		for (AuthenticationProvider provider : getProviders()) {
			//是否支持當(dāng)前認證
			if (!provider.supports(toTest)) {
				continue;
			}
```java
		if (debug) {
			logger.debug("Authentication attempt using "
					+ provider.getClass().getName());
		}
		try {
			//調(diào)用provider執(zhí)行認證
			result = provider.authenticate(authentication);
			if (result != null) {
				copyDetails(authentication, result);
				break;
			}
		}
			...省略...
	}
	...省略...
	//result就是Authentication ,使用的實現(xiàn)類依然是UsernamepasswordAuthenticationToken,
	//封裝了認證成功后的用戶的認證信息和授權(quán)信息
	if (result != null) {
		if (eraseCredentialsAfterAuthentication
			&& (result instanceof CredentialsContainer)) {
		// Authentication is complete. Remove credentials and other secret data
		// from authentication
		//這里在擦除登錄密碼
		((CredentialsContainer) result).eraseCredentials();
	}
	// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
	// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
	if (parentResult == null) {
		//發(fā)布事件
		eventPublisher.publishAuthenticationSuccess(result);
	}
	return result;
}

DaoAuthenticationProvider

請求到達AuthenticationProvider,默認實現(xiàn)是DaoAuthenticationProvider,它的作用是根據(jù)傳入的Token中的username調(diào)用UserDetailService加載數(shù)據(jù)庫中的認證授權(quán)信息(UserDetails),然后使用PasswordEncoder對比用戶登錄密碼是否正確

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
		//密碼編碼器
		private PasswordEncoder passwordEncoder;
		//UserDetailsService ,根據(jù)用戶名加載UserDetails對象,從數(shù)據(jù)庫加載的認證授權(quán)信息
		private UserDetailsService userDetailsService;
		//認證檢查方法
		protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		if (authentication.getCredentials() == null) {
			logger.debug("Authentication failed: no credentials provided");
```java
		throw new BadCredentialsException(messages.getMessage(
				"AbstractUserDetailsAuthenticationProvider.badCredentials",
				"Bad credentials"));
	}
	//獲取密碼
	String presentedPassword = authentication.getCredentials().toString();
	//通過passwordEncoder比較密碼,presentedPassword是用戶傳入的密碼,userDetails.getPassword()是從數(shù)據(jù)庫加載到的密碼
	//passwordEncoder編碼器不一樣比較密碼的方式也不一樣
	if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
		logger.debug("Authentication failed: password does not match stored value");
		throw new BadCredentialsException(messages.getMessage(
				"AbstractUserDetailsAuthenticationProvider.badCredentials",
				"Bad credentials"));
	}
}
//檢索用戶,參數(shù)為用戶名和Token對象
protected final UserDetails retrieveUser(String username,
		UsernamePasswordAuthenticationToken authentication)
		throws AuthenticationException {
	prepareTimingAttackProtection();
	try {
		//調(diào)用UserDetailsService的loadUserByUsername方法,
		//根據(jù)用戶名檢索數(shù)據(jù)庫中的用戶,封裝成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);
	}
}
//創(chuàng)建認證成功的認證對象Authentication,使用的實現(xiàn)是UsernamepasswordAuthenticationToken,
//封裝了認證成功后的認證信息和授權(quán)信息,以及賬戶的狀態(tài)等
@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);
}
...省略...

這里提供了三個方法

  • additionalAuthenticationChecks:通過passwordEncoder比對密碼
  • retrieveUser:根據(jù)用戶名調(diào)用UserDetailsService加載用戶認證授權(quán)信息
  • createSuccessAuthentication:登錄成功,創(chuàng)建認證對象Authentication

然而你發(fā)現(xiàn) DaoAuthenticationProvider 中并沒有authenticate認證方法,真正的認證邏輯是通過父類AbstractUserDetailsAuthenticationProvider.authenticate方法完成的

AbstractUserDetailsAuthenticationProvider

public abstract class AbstractUserDetailsAuthenticationProvider implements
		AuthenticationProvider, InitializingBean, MessageSourceAware {
		//認證邏輯
		public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
			//得到傳入的用戶名
			String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
				: authentication.getName();
				//從緩存中得到UserDetails
			boolean cacheWasUsed = true;
			UserDetails user = this.userCache.getUserFromCache(username);
			if (user == null) {
			cacheWasUsed = false;
```java
		try {
			//檢索用戶,底層會調(diào)用UserDetailsService加載數(shù)據(jù)庫中的UserDetails對象,保護認證信息和授權(quán)信息
			user = retrieveUser(username,
					(UsernamePasswordAuthenticationToken) authentication);
		}
		catch (UsernameNotFoundException notFound) {
			...省略...
		}
		try {
			//前置檢查,主要檢查賬戶是否鎖定,賬戶是否過期等
			preAuthenticationChecks.check(user);
			//比對密碼在這個方法里面比對的
			additionalAuthenticationChecks(user,
				(UsernamePasswordAuthenticationToken) authentication);
		}
		catch (AuthenticationException exception) {
		...省略...
		}
		//后置檢查
		postAuthenticationChecks.check(user);
		if (!cacheWasUsed) {
			//設(shè)置UserDetails緩存
			this.userCache.putUserInCache(user);
		}
		Object principalToReturn = user;
		if (forcePrincipalAsString) {
			principalToReturn = user.getUsername();
		}
		//認證成功,創(chuàng)建Auhentication認證對象
		return createSuccessAuthentication(principalToReturn, authentication, user);
}

SecurityContextHolder

認證成功,請求會重新回到UsernamePasswordAuthenticationFilter,然后會通過其父類AbstractAuthenticationProcessingFilter.successfulAuthentication方法將認證對象封裝成SecurityContext設(shè)置到SecurityContextHolder中
protected void successfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain, Authentication authResult)
			throws IOException, ServletException {
```java
	if (logger.isDebugEnabled()) {
		logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
				+ authResult);
	}
	//認證成功,吧Authentication 設(shè)置到SecurityContextHolder
	SecurityContextHolder.getContext().setAuthentication(authResult);
	//處理記住我業(yè)務(wù)邏輯
	rememberMeServices.loginSuccess(request, response, authResult);
	// Fire event
	if (this.eventPublisher != null) {
		eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
				authResult, this.getClass()));
	}
	//重定向登錄成功地址
	successHandler.onAuthenticationSuccess(request, response, authResult);
}

然后后續(xù)請求又會回到SecurityContextPersistenceFilter,它就可以從SecurityContextHolder獲取到SecurityContext持久到SecurityContextRepository(默認實現(xiàn)是HttpSessionSecurityContextRepository基于Session存儲)

到此這篇關(guān)于SpringSecurity身份認證原理解析的文章就介紹到這了,更多相關(guān)SpringSecurity身份認證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC返回圖片的幾種方式(小結(jié))

    SpringMVC返回圖片的幾種方式(小結(jié))

    這篇文章主要介紹了SpringMVC返回圖片的幾種方式(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • java異步編程CompletableFuture使用示例詳解

    java異步編程CompletableFuture使用示例詳解

    這篇文章主要為大家介紹了java異步編程CompletableFuture使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • Java多線程之Worker Thread模式

    Java多線程之Worker Thread模式

    這篇文章主要介紹了Java多線程之Worker Thread模式,Worker的意思是工作的人,在Worker Thread模式中,工人線程Worker thread會逐個取回工作并進行處理,當(dāng)所有工作全部完成后,工人線程會等待新的工作到來,下面文章小編回給大家詳細介紹,需要的朋友可以參考一下
    2021-10-10
  • java中跨域問題解決的幾種方式

    java中跨域問題解決的幾種方式

    這篇文章主要給大家介紹了關(guān)于java中跨域問題解決的幾種方式, 在前后端分離項目中,經(jīng)常會遇到跨域問題,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • 詳細聊一聊java語言中的package和import機制

    詳細聊一聊java語言中的package和import機制

    這篇文章主要給大家介紹了關(guān)于java語言中package和import機制的相關(guān)資料,Java中的package是指將相關(guān)的類組織在一起的一種機制,它可以用來避免命名沖突,也可以方便地管理和維護代碼,需要的朋友可以參考下
    2024-01-01
  • SpringMVC JSON數(shù)據(jù)傳輸參數(shù)超詳細講解

    SpringMVC JSON數(shù)據(jù)傳輸參數(shù)超詳細講解

    有時候參數(shù)的傳遞還需要更多的參數(shù),比如一個獲取用戶信息的請求中既有用戶ID等基本參數(shù),還要求對查詢結(jié)果進行分頁,針對這種場景,一般都會將分頁參數(shù)封裝成一個對象,然后將它和基本參數(shù)一起傳給控制器
    2023-02-02
  • springboot?整合sentinel的示例代碼

    springboot?整合sentinel的示例代碼

    本文主要介紹了springboot?整合sentinel的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 淺析Java自定義注解的用法

    淺析Java自定義注解的用法

    注解為我們在代碼中添加信息提供一種形式化的方法,使我們可以在源碼、編譯時、運行時非常方便的使用這些數(shù)據(jù)。本文主要為大家介紹了Java自定義注解的用法,希望對大家有所幫助
    2023-03-03
  • Spring中的Sentinel熔斷降級原理詳解

    Spring中的Sentinel熔斷降級原理詳解

    這篇文章主要介紹了Spring中的Sentinel熔斷降級原理詳解,熔斷是為了起到保護作用,如果某個目標(biāo)服務(wù)調(diào)用比較慢或者大量的超時,這個時候如果觸發(fā)熔斷機制,則可以保證后續(xù)的請求不會繼續(xù)發(fā)送到目標(biāo)服務(wù)上,而是直接返回降級的邏輯并且快速釋放資源,需要的朋友可以參考下
    2023-09-09
  • SpringCloud實現(xiàn)基于RabbitMQ消息隊列的詳細步驟

    SpringCloud實現(xiàn)基于RabbitMQ消息隊列的詳細步驟

    在Spring Cloud框架中,我們可以利用RabbitMQ實現(xiàn)強大而可靠的消息隊列系統(tǒng),本篇將詳細介紹如何在Spring Cloud項目中集成RabbitMQ,并創(chuàng)建一個簡單的消息隊列,感興趣的朋友一起看看吧
    2024-03-03

最新評論