日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Security认证过程

發布時間:2023/12/15 javascript 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Security认证过程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

Spring Security是一個能夠為基于Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,為應用系統提供聲明式的安全訪問控制功能,減少了為企業系統安全控制編寫大量重復代碼的工作。

類圖

為了方便理解Spring Security認證流程,特意畫了如下的類圖,包含相關的核心認證類

概述

核心驗證器

AuthenticationManager

該對象提供了認證方法的入口,接收一個Authentiaton對象作為參數;

public interface AuthenticationManager {Authentication authenticate(Authentication authentication)throws AuthenticationException; }

ProviderManager

它是 AuthenticationManager 的一個實現類,提供了基本的認證邏輯和方法;它包含了一個 List<AuthenticationProvider> 對象,通過 AuthenticationProvider 接口來擴展出不同的認證提供者(當Spring Security默認提供的實現類不能滿足需求的時候可以擴展AuthenticationProvider 覆蓋supports(Class<?> authentication)方法);

驗證邏輯

AuthenticationManager 接收 Authentication 對象作為參數,并通過 authenticate(Authentication) 方法對其進行驗證;AuthenticationProvider實現類用來支撐對 Authentication 對象的驗證動作;UsernamePasswordAuthenticationToken實現了 Authentication主要是將用戶輸入的用戶名和密碼進行封裝,并供給 AuthenticationManager 進行驗證;驗證完成以后將返回一個認證成功的 Authentication 對象;

Authentication

Authentication對象中的主要方法

public interface Authentication extends Principal, Serializable {//#1.權限結合,可使用AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_ADMIN")返回字符串權限集合Collection<? extends GrantedAuthority> getAuthorities();//#2.用戶名密碼認證時可以理解為密碼Object getCredentials();//#3.認證時包含的一些信息。Object getDetails();//#4.用戶名密碼認證時可理解時用戶名Object getPrincipal();#5.是否被認證,認證為true boolean isAuthenticated();#6.設置是否能被認證void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;

ProviderManager

ProviderManager是AuthenticationManager的實現類,提供了基本認證實現邏輯和流程;

public Authentication authenticate(Authentication authentication)throws AuthenticationException {//#1.獲取當前的Authentication的認證類型Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;Authentication result = null;boolean debug = logger.isDebugEnabled();//#2.遍歷所有的providers使用supports方法判斷該provider是否支持當前的認證類型,不支持的話繼續遍歷for (AuthenticationProvider provider : getProviders()) {if (!provider.supports(toTest)) {continue;}if (debug) {logger.debug("Authentication attempt using "+ provider.getClass().getName());}try {#3.支持的話調用provider的authenticat方法認證result = provider.authenticate(authentication);if (result != null) {#4.認證通過的話重新生成Authentication對應的TokencopyDetails(authentication, result);break;}}catch (AccountStatusException e) {prepareException(e, authentication);// SEC-546: Avoid polling additional providers if auth failure is due to// invalid account statusthrow e;}catch (InternalAuthenticationServiceException e) {prepareException(e, authentication);throw e;}catch (AuthenticationException e) {lastException = e;}}if (result == null && parent != null) {// Allow the parent to try.try {#5.如果#1 沒有驗證通過,則使用父類型AuthenticationManager進行驗證result = parent.authenticate(authentication);}catch (ProviderNotFoundException e) {// ignore as we will throw below if no other exception occurred prior to// calling parent and the parent// may throw ProviderNotFound even though a provider in the child already// handled the request}catch (AuthenticationException e) {lastException = e;}}#6. 是否擦出敏感信息if (result != null) {if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {// Authentication is complete. Remove credentials and other secret data// from authentication((CredentialsContainer) result).eraseCredentials();}eventPublisher.publishAuthenticationSuccess(result);return result;}// Parent was null, or didn't authenticate (or throw an exception).if (lastException == null) {lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() },"No AuthenticationProvider found for {0}"));}prepareException(lastException, authentication);throw lastException;}
  • 遍歷所有的 Providers,然后依次執行該 Provider 的驗證方法
    • 如果某一個 Provider 驗證成功,則跳出循環不再執行后續的驗證;
    • 如果驗證成功,會將返回的 result 既 Authentication 對象進一步封裝為 Authentication Token; 比如 UsernamePasswordAuthenticationToken、RememberMeAuthenticationToken 等;這些 Authentication Token 也都繼承自 Authentication 對象;
  • 如果 #1 沒有任何一個 Provider 驗證成功,則試圖使用其 parent Authentication Manager 進行驗證;
  • 是否需要擦除密碼等敏感信息;
  • AuthenticationProvider

    ProviderManager 通過 AuthenticationProvider 擴展出更多的驗證提供的方式;而 AuthenticationProvider 本身也就是一個接口,從類圖中我們可以看出它的實現類AbstractUserDetailsAuthenticationProvider和AbstractUserDetailsAuthenticationProvider的子類DaoAuthenticationProvider。DaoAuthenticationProvider是Spring Security中一個核心的Provider,對所有的數據庫提供了基本方法和入口。

    DaoAuthenticationProvider

    DaoAuthenticationProvider主要做了以下事情

  • 對用戶身份盡心加密操作; #1.可直接返回BCryptPasswordEncoder,也可以自己實現該接口使用自己的加密算法核心方法String encode(CharSequence rawPassword);和boolean matches(CharSequence rawPassword, String encodedPassword);
  • private PasswordEncoder passwordEncoder;

    2. 實現了 `AbstractUserDetailsAuthenticationProvider` 兩個抽象方法,1. 獲取用戶信息的擴展點```java protected final UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {UserDetails loadedUser;try {loadedUser = this.getUserDetailsService().loadUserByUsername(username);} 主要是通過注入`UserDetailsService`接口對象,并調用其接口方法 `loadUserByUsername(String username)` 獲取得到相關的用戶信息。`UserDetailsService`接口非常重要。 2. 實現 additionalAuthenticationChecks 的驗證方法(主要驗證密碼);

    AbstractUserDetailsAuthenticationProvider

    AbstractUserDetailsAuthenticationProvider為DaoAuthenticationProvider提供了基本的認證方法;

    public Authentication authenticate(Authentication authentication)throws AuthenticationException {Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports","Only UsernamePasswordAuthenticationToken is supported"));// Determine usernameString username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED": authentication.getName();boolean cacheWasUsed = true;UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {#1.獲取用戶信息由子類實現即DaoAuthenticationProvideruser = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException notFound) {logger.debug("User '" + username + "' not found");if (hideUserNotFoundExceptions) {throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));}else {throw notFound;}}Assert.notNull(user,"retrieveUser returned null - a violation of the interface contract");}try {#2.前檢查由DefaultPreAuthenticationChecks類實現(主要判斷當前用戶是否鎖定,過期,凍結User接口)preAuthenticationChecks.check(user);#3.子類實現additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);}catch (AuthenticationException exception) {if (cacheWasUsed) {// There was a problem, so try again after checking// we're using latest data (i.e. not from the cache)cacheWasUsed = false;user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);preAuthenticationChecks.check(user);additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);}else {throw exception;}}#4.檢測用戶密碼是否過期對應#2 的User接口postAuthenticationChecks.check(user);if (!cacheWasUsed) {this.userCache.putUserInCache(user);}Object principalToReturn = user;if (forcePrincipalAsString) {principalToReturn = user.getUsername();}return createSuccessAuthentication(principalToReturn, authentication, user);}

    AbstractUserDetailsAuthenticationProvider主要實現了AuthenticationProvider的接口方法authenticate 并提供了相關的驗證邏輯;

  • 獲取用戶返回UserDetails AbstractUserDetailsAuthenticationProvider定義了一個抽象的方法
  • protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;

    2. 三步驗證工作1. preAuthenticationChecks2. additionalAuthenticationChecks(抽象方法,子類實現)3. postAuthenticationChecks 3. 將已通過驗證的用戶信息封裝成 UsernamePasswordAuthenticationToken 對象并返回;該對象封裝了用戶的身份信息,以及相應的權限信息,相關源碼如下,```java protected Authentication createSuccessAuthentication(Object principal,UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(),authoritiesMapper.mapAuthorities(user.getAuthorities()));result.setDetails(authentication.getDetails());return result;}

    UserDetailsService

    UserDetailsService是一個接口,提供了一個方法

    public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }

    通過用戶名 username 調用方法 loadUserByUsername 返回了一個UserDetails接口對象(對應AbstractUserDetailsAuthenticationProvider的三步驗證方法);

    public interface UserDetails extends Serializable {#1.權限集合Collection<? extends GrantedAuthority> getAuthorities();#2.密碼 String getPassword();#3.用戶民String getUsername();#4.用戶是否過期boolean isAccountNonExpired();#5.是否鎖定 boolean isAccountNonLocked();#6.用戶密碼是否過期 boolean isCredentialsNonExpired();#7.賬號是否可用(可理解為是否刪除)boolean isEnabled(); }

    Spring 為UserDetailsService默認提供了一個實現類 org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl

    JdbcUserDetailsManager

    該實現類主要是提供基于JDBC對 User 進行增、刪、查、改的方法

    public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsManager,GroupManager {// ~ Static fields/initializers// =====================================================================================// UserDetailsManager SQL#1.定義了一些列對數據庫操作的語句public static final String DEF_CREATE_USER_SQL = "insert into users (username, password, enabled) values (?,?,?)";public static final String DEF_DELETE_USER_SQL = "delete from users where username = ?";public static final String DEF_UPDATE_USER_SQL = "update users set password = ?, enabled = ? where username = ?";public static final String DEF_INSERT_AUTHORITY_SQL = "insert into authorities (username, authority) values (?,?)";public static final String DEF_DELETE_USER_AUTHORITIES_SQL = "delete from authorities where username = ?";public static final String DEF_USER_EXISTS_SQL = "select username from users where username = ?";public static final String DEF_CHANGE_PASSWORD_SQL = "update users set password = ? where username = ?";

    InMemoryUserDetailsManager

    該實現類主要是提供基于內存對 User 進行增、刪、查、改的方法 `public class InMemoryUserDetailsManager implements UserDetailsManager { protected final Log logger = LogFactory.getLog(getClass()); #1.用MAP 存儲 private final Map<String, MutableUserDetails> users = new HashMap<String, MutableUserDetails>();

    private AuthenticationManager authenticationManager;public InMemoryUserDetailsManager() { }public InMemoryUserDetailsManager(Collection<UserDetails> users) {for (UserDetails user : users) {createUser(user);} }`

    總結

    UserDetailsService接口作為橋梁,是DaoAuthenticationProvier與特定用戶信息來源進行解耦的地方,UserDetailsService由UserDetails和UserDetailsManager所構成;UserDetails和UserDetailsManager各司其責,一個是對基本用戶信息進行封裝,一個是對基本用戶信息進行管理;

    特別注意,UserDetailsService、UserDetails以及UserDetailsManager都是可被用戶自定義的擴展點,我們可以繼承這些接口提供自己的讀取用戶來源和管理用戶的方法,比如我們可以自己實現一個 與特定 ORM 框架,比如 Mybatis 或者 Hibernate,相關的UserDetailsService和UserDetailsManager;

    時序圖

    轉載于:https://my.oschina.net/u/3656540/blog/1805899

    總結

    以上是生活随笔為你收集整理的Spring Security认证过程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。