整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          SpringSecurity(一)源碼分析及認證流程

          SpringSecurity(一)源碼分析及認證流程

          、Spring Security 概述

          Spring Security 是能夠為Spring 企業(yè)應(yīng)用提供聲明式的安全訪問控制解決方案的安全框架,為應(yīng)用系統(tǒng)提供聲明式的安全訪問控制功能,減少為企業(yè)系統(tǒng)安全訪問控制編寫大量重復(fù)的代碼。

          1.1 Spring Security項目核心模塊

          spring-security
          ├── 核心 - spring-security-core.jar
          ├── Remoting - spring-security-remoting.jar
          ├── Web - spring-security-web.jar
          ├── 配置 - spring-security-config.jar
          ├── LDAP - spring-security-ldap.jar
          ├── OAuth 2.0核心 - spring-security-oauth2-core.jar
          ├── OAuth 2.0客戶端 - spring-security-oauth2-client.jar
          ├── OAuth 2.0 JOSE - spring-security-oauth2-jose.jar
          ├── ACL - spring-security-acl.jar
          ├── CAS - spring-security-cas.jar
          ├── OpenID - spring-security-openid.jar
          └── 測試 - spring-security-test.jar
          

          核心詳細描述參考中文官網(wǎng)

          1.2 Spring Security 簡單集成

          Spring Security 支持Maven和Gradle集成,本文主要使用Spring Boot與Maven: pom.xml

          <dependencies>
              <!-- ... other dependency elements ... -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-security</artifactId>
              </dependency>
          </dependencies>
          

          1.2.1 使用Security安全功能

          • 增加配置類,使用注解@EnableWebSecurity開啟Web安全功能
          • 實現(xiàn)了接口WebSecurityConfigurer或者繼承自WebSecurityConfigurerAdapter),增加Security安全配置。

          代碼清單:

          @EnableWebSecurity
          public class SecurityConfig extends WebSecurityConfigurerAdapter {
          
            @Override
            protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests((authorize) -> authorize
                      .antMatchers("/css/**", "/index").permitAll()
                      .antMatchers("/user/**").hasRole("USER")
                  )
                  .formLogin((formLogin) -> formLogin
                      .loginPage("/login")
                      .failureUrl("/login-error")
                  );
            }
            @Bean
            public UserDetailsService userDetailsService() {
              UserDetails userDetails=User.withDefaultPasswordEncoder()
                  .username("user")
                  .password("password")
                  .roles("USER")
                  .build();
              return new InMemoryUserDetailsManager(userDetails);
            }
          }
          

          當(dāng)添加了SecurityConfig 之后我們的應(yīng)用就具備如下功能:

          • 要求對應(yīng)用程序中的除了/css/**", "/index每個URL進行身份驗證
          • 要求對應(yīng)用程序中的/user/**的URL訪問都需要USER角色才能訪問
          • 指定“/login”該路徑為登錄頁面,當(dāng)未認證的用戶嘗試訪問任何受保護的資源時,都會跳轉(zhuǎn)到“/login”。
          • 默認指定“/logout”為注銷頁面
          • 配置一個內(nèi)存中的用戶認證器,使用admin/admin作為用戶名和密碼,具有USER角色。
          • CSRF攻擊預(yù)防
          • 會話固定保護
          • 安全標頭集成
          • 用于安全請求的 HTTP嚴格傳輸安全性
          • X-Content-Type-Options集成
          • 緩存控制(稍后可由應(yīng)用程序覆蓋以允許緩存靜態(tài)資源)
          • X-XSS-Protection集成
          • X-Frame-Options集成有助于防止Clickjacking
          • 與以下Servlet API方法集成
          • HttpServletRequest的#getRemoteUser()
          • HttpServletRequest.html#getUserPrincipal()
          • HttpServletRequest.html#的isUserInRole(java.lang.String中)
          • HttpServletRequest.html#login(java.lang.String,java.lang.String)
          • HttpServletRequest.html#注銷()

          2、源碼分析-Security安全認證實現(xiàn)

          2.1 Security安全認證過程類圖

          2.2 Security安全認證自動化配置

          2.2.1 @EnableWebSecurity

          從類關(guān)系圖可以清楚**@EnableWebSecurity**注解是開啟Security安全功能的核心注解,EnableWebSecurity源碼清單:

          @Retention(RetentionPolicy.RUNTIME)
          @Target(ElementType.TYPE)
          @Documented
          @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
          		HttpSecurityConfiguration.class })
          @EnableGlobalAuthentication
          @Configuration
          public @interface EnableWebSecurity {
          
          	/**
          	 * Controls debugging support for Spring Security. Default is false.
          	 * @return if true, enables debug support with Spring Security
          	 */
          	boolean debug() default false;
          
          }
          

          @EnableWebSecurity是組合注解,引入了Impost注解包含的外部配置以及激活了@EnableGlobalAuthentication注解,而@EnableGlobalAuthentication注解激活了AuthenticationConfiguration認證配置類。

          2.2.2 WebSecurityConfiguration

          WebSecurityConfiguration 是web安全配置核心類,WebSecurityConfiguration最主要的功能就是創(chuàng)建了springSecurityFilterChain Bean,springSecurityFilterChain 是spring security的核心過濾器,是整個認證的入口。WebSecurityConfiguration中完成了聲明springSecurityFilterChain的作用,并且最終交給DelegatingFilterProxy這個代理類,負責(zé)攔截請求(注意DelegatingFilterProxy這個類不是spring security包中的,而是存在于web包中,spring使用了代理模式來實現(xiàn)安全過濾的解耦)。WebSecurityConfiguration源碼清單:

          @Configuration(proxyBeanMethods=false)
          public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
          //省略==========================================@Bean(name=AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
          	public Filter springSecurityFilterChain() throws Exception {
          		boolean hasConfigurers=this.webSecurityConfigurers !=null && !this.webSecurityConfigurers.isEmpty();
          		boolean hasFilterChain=!this.securityFilterChains.isEmpty();
          		Assert.state(!(hasConfigurers && hasFilterChain),
          				"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
          		if (!hasConfigurers && !hasFilterChain) {
          			WebSecurityConfigurerAdapter adapter=this.objectObjectPostProcessor
          					.postProcess(new WebSecurityConfigurerAdapter() {
          					});
          			this.webSecurity.apply(adapter);
          		}
          		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
          			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
          			for (Filter filter : securityFilterChain.getFilters()) {
          				if (filter instanceof FilterSecurityInterceptor) {
          					this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
          					break;
          				}
          			}
          		}
          		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
          			customizer.customize(this.webSecurity);
          		}
          		return this.webSecurity.build();
          	}
          
          	//省略=====================}
          

          2.2.3 SpringWebMvcImportSelector

          SpringWebMvcImportSelector主要作用是判斷當(dāng)前的環(huán)境是否包含springmvc,因為spring security可以在非spring環(huán)境下使用,為了避免DispatcherServlet的重復(fù)配置,所以使用了這個注解來區(qū)分。

          2.2.4 OAuth2ImportSelector

          OAuth2ImportSelector類是為了對 OAuth2.0 開放授權(quán)協(xié)議進行支持。ClientRegistration 如果被引用,具體點也就是 spring-security-oauth2 模塊被啟用(引入依賴jar)時。會啟用 OAuth2 客戶端配置 OAuth2ClientConfiguration

          2.2.5 HttpSecurityConfiguration

          HttpSecurityConfiguration配置類首先通過@Autowired去獲取容器中的一個AuthenticationManager實例,如果沒能獲取到則使用依賴注入的AuthenticationConfiguration實例創(chuàng)建一個AuthenticationManager實例,這個實例其實就是ProviderManager。然后初始化httpSecurity。

          2.2.5.1 HttpSecurity

          通過HttpSecurity配置指明了Web Security 攔截什么URL、登錄認證方式、設(shè)置什么權(quán)限等。

          2.2.6 AuthenticationConfiguration

          AuthenticationConfiguration 主要作用就是創(chuàng)建全局的身份認證管理者AuthenticationManager,AuthenticationManager便是最核心的身份認證管理器。 AuthenticationConfiguration源碼清單:

          @Configuration(proxyBeanMethods=false)
          @Import(ObjectPostProcessorConfiguration.class)
          public class AuthenticationConfiguration {
          
          	//省略================@Bean
          	public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
          			ApplicationContext context) {
          		LazyPasswordEncoder defaultPasswordEncoder=new LazyPasswordEncoder(context);
          		AuthenticationEventPublisher authenticationEventPublisher=getBeanOrNull(context,
          				AuthenticationEventPublisher.class);
          		DefaultPasswordEncoderAuthenticationManagerBuilder result=new DefaultPasswordEncoderAuthenticationManagerBuilder(
          				objectPostProcessor, defaultPasswordEncoder);
          		if (authenticationEventPublisher !=null) {
          			result.authenticationEventPublisher(authenticationEventPublisher);
          		}
          		return result;
          	}
          
          	@Bean
          	public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
          			ApplicationContext context) {
          		return new EnableGlobalAuthenticationAutowiredConfigurer(context);
          	}
          
          	@Bean
          	public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(
          			ApplicationContext context) {
          		return new InitializeUserDetailsBeanManagerConfigurer(context);
          	}
          
          	@Bean
          	public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(
          			ApplicationContext context) {
          		return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
          	}
          
          	public AuthenticationManager getAuthenticationManager() throws Exception {
          		if (this.authenticationManagerInitialized) {
          			return this.authenticationManager;
          		}
          		AuthenticationManagerBuilder authBuilder=this.applicationContext.getBean(AuthenticationManagerBuilder.class);
          		if (this.buildingAuthenticationManager.getAndSet(true)) {
          			return new AuthenticationManagerDelegator(authBuilder);
          		}
          		for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
          			authBuilder.apply(config);
          		}
          		this.authenticationManager=authBuilder.build();
          		if (this.authenticationManager==null) {
          			this.authenticationManager=getAuthenticationManagerBean();
          		}
          		this.authenticationManagerInitialized=true;
          		return this.authenticationManager;
          	}
          
          //省略================private AuthenticationManager getAuthenticationManagerBean() {
          		return lazyBean(AuthenticationManager.class);
          	}
          

          2.3 驗證邏輯

          2.3.1 AuthenticationManager

          AuthenticationManager 提供了認證的入口,源碼清單:

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

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

          2.3.2 Authentication

          Authentication 源碼:

          public interface Authentication extends Principal, Serializable {
          
          	//#1.權(quán)限集合
          	Collection<? extends GrantedAuthority> getAuthorities();
          	//#2. 用戶密碼認證時,可以理解為密碼
              Object getCredentials();
               //#3. 認證時包含的信息
          	Object getDetails();
              //# 4. 用戶密碼認證時,可以理解為用戶名
          	Object getPrincipal();
              //# 5. 是否被認證,認證為true
          	boolean isAuthenticated();
              //# 6. 設(shè)置是否能夠被認證
          	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
          }
          

          2.3.3 ProviderManager

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

          @Override
          	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                          //# 獲取當(dāng)前認證類型
          		Class<? extends Authentication> toTest=authentication.getClass();
          		AuthenticationException lastException=null;
          		AuthenticationException parentException=null;
          		Authentication result=null;
          		Authentication parentResult=null;
          		int currentPosition=0;
          		int size=this.providers.size();
                          //遍歷所有providers,調(diào)用supports驗證是否支持當(dāng)前認證
          		for (AuthenticationProvider provider : getProviders()) {
          			if (!provider.supports(toTest)) {
          				continue;
          			}
          			if (logger.isTraceEnabled()) {
          				logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
          						provider.getClass().getSimpleName(), ++currentPosition, size));
          			}
          			try {
                         //調(diào)用provider的認證方法進行認證
          				result=provider.authenticate(authentication);
          				if (result !=null) {
          					copyDetails(authentication, result);
          					break;
          				}
          			}
          			catch (AccountStatusException | InternalAuthenticationServiceException ex) {
          				prepareException(ex, authentication);
          				// SEC-546: Avoid polling additional providers if auth failure is due to
          				// invalid account status
          				throw ex;
          			}
          			catch (AuthenticationException ex) {
          				lastException=ex;
          			}
          		}
          		if (result==null && this.parent !=null) {
          			// Allow the parent to try.
          			try {
                           //認證失敗,調(diào)用父類的認證方法進行認證
          				parentResult=this.parent.authenticate(authentication);
          				result=parentResult;
          
          			}
          			catch (ProviderNotFoundException ex) {
          				// 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 ex) {
          				parentException=ex;
          				lastException=ex;
          			}
          		}
          		if (result !=null) {
          			if (this.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 then it
          			// will publish an AuthenticationSuccessEvent
          			// This check prevents a duplicate AuthenticationSuccessEvent if the parent
          			// AuthenticationManager already published it
          			if (parentResult==null) {
          				this.eventPublisher.publishAuthenticationSuccess(result);
          			}
          
          			return result;
          		}
          
          		// Parent was null, or didn't authenticate (or throw an exception).
          		if (lastException==null) {
          			lastException=new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",
          					new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));
          		}
          		// If the parent AuthenticationManager was attempted and failed then it will
          		// publish an AbstractAuthenticationFailureEvent
          		// This check prevents a duplicate AbstractAuthenticationFailureEvent if the
          		// parent AuthenticationManager already published it
          		if (parentException==null) {
          			prepareException(lastException, authentication);
          		}
          		throw lastException;
          	}
          

          2.3.3 AuthenticationProvider

          AuthenticationProvider, ProviderManager通過AuthenticationProvider擴展多種認證方法,AuthenticationProvider 本身也就是一個接口,從類圖中我們可以看出它的實現(xiàn)類AbstractUserDetailsAuthenticationProvider 和AbstractUserDetailsAuthenticationProvider的子類DaoAuthenticationProvider 。

          2.3.4 DaoAuthenticationProvider

          DaoAuthenticationProvider 是Spring Security中一個核心的Provider,對所有的數(shù)據(jù)庫提供了基本方法和入口。DaoAuthenticationProvider源碼清單:

          public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
          
          	/**
          	 * 實現(xiàn)用戶密碼加密
          	 */
          	private PasswordEncoder passwordEncoder;
          
          	//省略
          
          	/**
          	 *
          	 * 實現(xiàn) additionalAuthenticationChecks 的驗證方法(主要驗證密碼);
          	 */
          	@Override
          	@SuppressWarnings("deprecation")
          	protected void additionalAuthenticationChecks(UserDetails userDetails,
          			UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
          		if (authentication.getCredentials()==null) {
          			this.logger.debug("Failed to authenticate since no credentials provided");
          			throw new BadCredentialsException(this.messages
          					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
          		}
          		String presentedPassword=authentication.getCredentials().toString();
          		if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
          			this.logger.debug("Failed to authenticate since password does not match stored value");
          			throw new BadCredentialsException(this.messages
          					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
          		}
          	}
          
          //省略
          
          	/**
          	 * 實現(xiàn)了 AbstractUserDetailsAuthenticationProvider retrieveUser 抽象方法
          	 * 主要是通過注入UserDetailsService接口對象,并調(diào)用其接口方法 loadUserByUsername(String username)獲取得到相關(guān)的用戶信息。
          	 * UserDetailsService接口非常重要。
          	 */
          	@Override
          	protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
          			throws AuthenticationException {
          		prepareTimingAttackProtection();
          		try {
          			//數(shù)據(jù)庫獲取用戶信息的擴展點
          			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);
          		}
          	}
          
          	//省略
          }
          

          2.3.4 AbstractUserDetailsAuthenticationProvider

          AbstractUserDetailsAuthenticationProvider主要實現(xiàn)了AuthenticationProvider的接口方法 authenticate 并提供了相關(guān)的驗證邏輯;

          抽象獲取用戶信息方法

          protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
          			throws AuthenticationException;
          

          三步驗證工作: i. preAuthenticationChecks ii. additionalAuthenticationChecks(抽象方法,子類實現(xiàn)) iii. postAuthenticationChecks將驗證結(jié)果封裝成UsernamePasswordAuthenticationToken返回。

          源碼清單:

          public abstract class AbstractUserDetailsAuthenticationProvider
          		implements AuthenticationProvider, InitializingBean, MessageSourceAware {
          //省略
          	//抽象驗證方法(主要驗證密碼)
          	protected abstract void additionalAuthenticationChecks(UserDetails userDetails,
          			UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;
          
          //省略
          
          	/**
          	 *
          	 * 實現(xiàn)AuthenticationProvider.authenticate驗證方法
          	 */
          	@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 {
          				//1. 獲取用戶信息
          				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 {
          			//2. 預(yù)檢查由DefaultPreAuthenticationChecks類實現(xiàn)(主要判斷當(dāng)前用戶是否鎖定,過期,凍結(jié)User接口)
          			this.preAuthenticationChecks.check(user);
          			//3. 子類實現(xiàn)
          			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;
          			user=retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
          			this.preAuthenticationChecks.check(user);
          			additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
          		}
          		//#4.檢測用戶密碼是否過期對應(yīng)#2 的User接口
          		this.postAuthenticationChecks.check(user);
          		if (!cacheWasUsed) {
          			this.userCache.putUserInCache(user);
          		}
          		Object principalToReturn=user;
          		if (this.forcePrincipalAsString) {
          			principalToReturn=user.getUsername();
          		}
          		//4. 將驗證結(jié)果封裝成UsernamePasswordAuthenticationToken
          		return createSuccessAuthentication(principalToReturn, authentication, user);
          	}
          
          	private String determineUsername(Authentication authentication) {
          		return (authentication.getPrincipal()==null) ? "NONE_PROVIDED" : authentication.getName();
          	}
          
          	//封裝驗證結(jié)果
          	protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
          			UserDetails user) {
          		// Ensure we return the original credentials the user supplied,
          		// so subsequent attempts are successful even with encoded passwords.
          		// Also ensure we return the original getDetails(), so that future
          		// authentication events after cache expiry contain the details
          		UsernamePasswordAuthenticationToken result=new UsernamePasswordAuthenticationToken(principal,
          				authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
          		result.setDetails(authentication.getDetails());
          		this.logger.debug("Authenticated user");
          		return result;
          	}
          
          	//抽象獲取用戶信息接口
          	protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
          			throws AuthenticationException;
          
          	//省略
          
          }
          

          2.3.4 UserDetailsService

          UserDetailsService接口作為橋梁,是DaoAuthenticationProvier與特定用戶信息來源進行解耦的地方,UserDetailsService由UserDetails和UserDetailsManager所構(gòu)成;UserDetails和UserDetailsManager各司其責(zé),一個是對基本用戶信息進行封裝,一個是對基本用戶信息進行管理;特別注意,UserDetailsService、UserDetails以及UserDetailsManager都是可被用戶自定義的擴展點,我們可以繼承這些接口提供自己的讀取用戶來源和管理用戶的方法,比如我們可以自己實現(xiàn)一個 與特定 ORM 框架,比如 Mybatis 或者 Hibernate,相關(guān)的UserDetailsService和UserDetailsManager;

          2.3.4.1 UserDetailsService

          UserDetails 驗證用戶實體

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

          2.4 認證流程圖

          文由掘金@天行天忌授權(quán)發(fā)布,前端晚間課對其內(nèi)容進行微改。


          HTML,超文本標記語言,是一種用于創(chuàng)建網(wǎng)頁的標準標記語言。自從引入 HTML 以來,它就一直用于構(gòu)建互聯(lián)網(wǎng)。與 JavaScript 和 CSS 一起,HTML 構(gòu)成前端開發(fā)的三劍客。

          盡管許多新技術(shù)使網(wǎng)站創(chuàng)建過程變得更簡單、更高效,但 HTML 始終是核心。隨著 HTML5 的普及,在 2014 年,這種標記語言發(fā)生了很多變化,變得更加友好,瀏覽器對新標準的支持熱度也越來越高。而HTML并不止于此,還在不斷發(fā)生變化,并且可能會獲得一些特性來證明對 HTML6 的命名更改是合理的。

          支持原生模式

          該元素<dialog> 將隨 HTML6 一起提供。它被認為等同于用 JavaScript 開發(fā)的模態(tài),并且已經(jīng)標準化,但只有少數(shù)瀏覽器完全支持。但這種現(xiàn)象會改變,很快它將在所有瀏覽器中得到支持。

          這個元素在其默認格式下,只會將光標顯示在它所在的位置上,但可以使用 JavaScript 打開模式。

          <dialog>
            <form method="dialog">
              <input type="submit" value="確定" />
              <input type="submit" value="取消" />
            </form>
          </dialog>

          在默認形式下,該元素創(chuàng)建一個灰色背景,其下方是非交互式內(nèi)容。

          可以在 <dialog> 其中的表單上使用一種方法,該方法將發(fā)送值并將其傳遞回自身 <dialog>

          總的來說,這個標簽在用戶交互和改進的界面中變得有益。

          可以通過更改 <dialog> 標簽的 open 屬性以控制打開和關(guān)閉。

          <dialog open>
            <p>組件內(nèi)容</p>
          </dialog>

          沒有 JavaScript 的單頁應(yīng)用程序

          FutureClaw 雜志主編 Bobby Mozumder 建議:

          將錨元素鏈接到 JSON/XML、API 端點,讓瀏覽器在內(nèi)部將數(shù)據(jù)加載到新的數(shù)據(jù)結(jié)構(gòu)中,然后瀏覽器將 DOM 元素替換為根據(jù)需要加載的任何數(shù)據(jù)。初始數(shù)據(jù)(以及標準錯誤響應(yīng))可以放在標題裝置中,如果需要,可以稍后替換。

          據(jù)他介紹,這是單頁應(yīng)用程序網(wǎng)頁設(shè)計模式,可以提高響應(yīng)速度和加載時間,因為不需要加載 JavaScript。

          這個是一個比較有意思的提案,就有點類似我們以前沒有做前后端分離之前的混合編程的模式,HTML變成模板語言,通過JSON API請求數(shù)據(jù),不一樣的是變成瀏覽器來默認解析,瀏覽器內(nèi)部加載數(shù)據(jù)到新的數(shù)據(jù)結(jié)構(gòu)中,然后瀏覽器將按需加載到的數(shù)據(jù)替換成 DOM 元素

          大家可以看一下InfoQ上的這篇文章《針對非正式 HTML6 提案“無需 JavaScript 的單頁應(yīng)用”引發(fā)的論戰(zhàn)》,了解更多!

          https://www.infoq.cn/article/2015/03/html6-without-javascript


          自由調(diào)整圖像大小

          HTML6 愛好者相信即將到來的更新將允許瀏覽器調(diào)整圖像大小以獲得更好的觀看體驗。

          每個瀏覽器都難以呈現(xiàn)相對于設(shè)備和屏幕尺寸的最佳圖像尺寸,不幸的是,src 標簽 img 在處理這個問題時不是很有效。

          這個問題可以通過一個新標簽 <srcset> 來解決,它使瀏覽器在多個圖像之間進行選擇的工作變得更加容易。

          專用庫

          將可用庫引入 HTML6 絕對是提高開發(fā)效率的重要一步。

          微格式

          很多時候,需要在互聯(lián)網(wǎng)上定義一般信息,而這些一般信息可以是任何公開的信息,例如電話號碼、姓名、地址等。微格式是能夠定義一般數(shù)據(jù)的標準。微格式可以增強設(shè)計者的能力,并可以減少搜索引擎推斷公共信息所需的努力。

          自定義菜單

          盡管標簽<ul>、<ol>非常有用,但在某些情況下仍有一些不足之處。可以處理交互元素的標簽將是一個不錯的選擇。

          這就是創(chuàng)建標簽 <menu> 的驅(qū)動力,它可以處理按鈕驅(qū)動的列表元素。

          <menu type="toolbar">
            <li><button>個人信息</button></li>
            <li><button>系統(tǒng)設(shè)置</button></li>
            <li><button>賬號注銷</button></li>
          </menu>

          因此 <menu>,除了能夠像普通列表一樣運行之外,還可以增強 HTML 列表的功能。

          增強身份驗證

          雖然HTML5在安全性方面還不錯,瀏覽器和網(wǎng)絡(luò)技術(shù)也提供了合理的保護。毫無疑問,在身份驗證和安全領(lǐng)域還有很多事情可以做。如密鑰可以異地存儲;這將防止不受歡迎的人訪問并支持身份驗證。使用嵌入式密鑰而不是 cookie,使數(shù)字簽名更好等。

          集成攝像頭

          HTML6 允許以更好的方式使用設(shè)備上的相機和媒體。將能夠控制相機、它的效果、模式、全景圖像、HDR 和其他屬性。

          總結(jié)

          沒有什么是完美的,HTML 也不是完美的,所以 HTML 規(guī)范可以做很多事情來使它更好。應(yīng)該對一些有用的規(guī)范進行標準化,以增強 HTML 的能力。小的變化已經(jīng)開始推出。如增強藍牙支持、p2p 文件傳輸、惡意軟件保護、云存儲集成,下一個 HTML 版本可以考慮一下。

          解決企業(yè)“注銷難”的問題,市市場監(jiān)管局說:今天,上海市注銷企業(yè)“一窗通”網(wǎng)上平臺正式上線→企業(yè)填報注銷申請信息后,平臺同步推送給市場監(jiān)管、稅務(wù)、人社、商務(wù)、海關(guān)、人民銀行、外管等行政部門以及人民法院等司法部門,各部門并聯(lián)預(yù)審辦理,提高效率。平臺還提供銀行賬戶撤銷的預(yù)約服務(wù)功能和在國家企業(yè)信用信息公示系統(tǒng)免費發(fā)布注銷公告的功能。怎么使用?看過來↓

          企業(yè)注銷流程再造

          企業(yè)填報注銷申請信息后,平臺同步推送給各相關(guān)部門,各部門并聯(lián)預(yù)審辦理。平臺引導(dǎo)企業(yè)按照最優(yōu)方案辦理注銷,幫助申請人了解各項業(yè)務(wù)的前后辦理關(guān)系。平臺還提供銀行賬戶撤銷的預(yù)約服務(wù)功能,幫助企業(yè)縮短注銷銀行賬戶的辦理時間。

          注銷企業(yè)“一窗通”網(wǎng)上平臺現(xiàn)有2個入口:

          第一個入口:上海市人民政府官網(wǎng)“中國上海”網(wǎng)站

          (http://www.shanghai.gov.cn/)首頁上點擊“一網(wǎng)通辦”

          然后可以在第二個頁面的“優(yōu)化營商環(huán)境”欄目下找到“企業(yè)注銷”按鈕

          第二個入口:上海市市場監(jiān)管局官網(wǎng)上的專欄通道

          (http://www.sgs.gov.cn/shaic/index_new.html)

          企業(yè)注銷規(guī)范透明

          平臺為注銷申請人集中提供各部門辦理指南、結(jié)果反饋、全流程進度跟蹤等電子政務(wù)服務(wù),及時告知企業(yè)辦理流程、辦理要求和進度情況,督促各業(yè)務(wù)部門按照規(guī)定時限和要求完成注銷業(yè)務(wù)程序,進一步實現(xiàn)注銷流程的規(guī)范化、法治化。

          企業(yè)注銷便捷高效

          通過平臺的信息互通共享,各部門可從平臺獲取企業(yè)信息和部門辦理結(jié)果,從而簡化數(shù)據(jù)填報采集和材料提交要求,優(yōu)化辦理流程。

          海關(guān)、人力資源社會保障部門可以利用注銷平臺,通過數(shù)據(jù)校驗和信息共享實現(xiàn)網(wǎng)上辦理;市場監(jiān)管部門可以實現(xiàn)各類申請文書材料的自動生成,進一步壓縮注銷登記辦理時間;稅務(wù)等部門可以實現(xiàn)預(yù)審并出具個性化“診斷書”,告知企業(yè)是否符合稅務(wù)免辦或即辦條件,減少和簡化材料提交的數(shù)量和要求。

          此外,平臺還為企業(yè)提供了在國家企業(yè)信用信息公示系統(tǒng)上免費發(fā)布注銷公告的功能。

          企業(yè)注銷智能增效

          平臺通過和多個企業(yè)數(shù)據(jù)庫的對接,實現(xiàn)智能比對篩選,對企業(yè)注銷申請進行分類處置,進一步提升辦理效率:

          符合簡易注銷登記條件的企業(yè),可進入國家企業(yè)信用信息公示系統(tǒng)辦理簡易注銷,并可在平臺上查看簡易注銷辦理進度。

          對其他企業(yè)提出的注銷申請,平臺將相關(guān)申請信息同步發(fā)送給各相關(guān)業(yè)務(wù)部門,各部門進行預(yù)審,按照各自要求對注銷企業(yè)實行分類處理。對于可通過數(shù)據(jù)共享即時辦結(jié)的,業(yè)務(wù)系統(tǒng)實時反饋平臺;對于不適用線上直接辦理的,根據(jù)企業(yè)情況出具“診斷書”,將辦理本部門注銷所需提交的材料、辦理要求、聯(lián)系方式等信息通過平臺和短信反饋給企業(yè)。通過清單式告知方式,實現(xiàn)窗口辦事“一口清”。

          資料:市市場監(jiān)管局

          編輯:景雯


          主站蜘蛛池模板: 99久久精品国产一区二区成人 | 国产激情无码一区二区| 亚洲国产精品一区二区三区久久 | 五十路熟女人妻一区二区| 在线|一区二区三区| 中文人妻无码一区二区三区| 亚洲色精品VR一区区三区 | 亚洲AV无码一区二区二三区入口| 无码国产精品一区二区免费式芒果| 亚洲一区二区三区久久| 精品亚洲一区二区| 亚洲成人一区二区| 天天视频一区二区三区| 国产日韩精品一区二区三区在线| 亚洲国产专区一区| 亚洲一区二区三区四区视频| 日韩精品一区二区三区在线观看| 国产午夜精品一区二区三区| 亚洲成a人一区二区三区| 中文无码一区二区不卡αv| 一区二区免费视频| 久久精品一区二区东京热| 国产精品日本一区二区不卡视频 | 极品尤物一区二区三区| 夜夜精品无码一区二区三区| 日本中文字幕一区二区有码在线| 日本无码一区二区三区白峰美| 亚洲国产专区一区| 亚洲日韩一区精品射精| 日韩一本之道一区中文字幕| 亚洲国产欧美国产综合一区 | 亚洲乱码一区av春药高潮| 美女视频免费看一区二区| 亚洲一区精品视频在线| 无码AV动漫精品一区二区免费| 91视频一区二区三区| 国产熟女一区二区三区四区五区| 爱爱帝国亚洲一区二区三区| 无码人妻精品一区二区在线视频| 波多野结衣一区二区免费视频| 国产乱码精品一区二区三区中|