SpringBoot Security 入門 以及登錄流程

首先參照官方文檔,我們構建一個 SpringBoot Security . 本文以Maven構建.粘貼即可

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>securitytest</groupId>
    <artifactId>securitytest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
    </dependencies>
</project>

Java Config Mvc Security Application

package com.config;


import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
    }
}


package com.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
  protected void configure(HttpSecurity http) throws Exception {
    http
       .authorizeRequests()
         .antMatchers("/", "/home").permitAll()
         .anyRequest().authenticated()
         .and()
       .formLogin()
         .loginPage("/login")
         .permitAll()
         .and()
       .logout()
         .permitAll();
   }

    
    @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//        auth.userDetailsService(new UserDetailsService() {
//            @Override
//            public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//                return null;
//            }
//        })
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("user").
                password(new BCryptPasswordEncoder().encode("123456")).roles("USER");
   }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }
}


package com;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {


    public static void main(String[] args) throws Throwable {
        SpringApplication.run(Application.class, args);
    }
}

其中注意  WebSecurityConfig 有三種config方法

// 此方法控制主要是作用爲用戶默認,識別你是否爲本系統用戶
protected void configure(AuthenticationManagerBuilder auth) throws Exception;
// 此方法主要保護訪問內部資源
protected void configure(HttpSecurity http) throws Exception ;
// 此方法爲全局方法,構建過濾鏈條
public void configure(WebSecurity web) throws Exception ;

Security核心有2種作用。第一 識別,你是誰,第二 你能做什麼。

Security登錄流程

首先外部系統訪問會經過一系列 WebSecurity構建的過濾鏈,最終達到其中一個 

AbstractAuthenticationProcessingFilter.doFilter(req,res,chain)
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;
		}

	       
		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);
        }
}

我們研究的主題方法,

`attemptAuthentication(req,res) `,登錄方法

`onAuthentication(authResult, request, response)` 會話管理,二次登錄方法

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); // request.getParamter("username")
		String password = obtainPassword(request); //同上

		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);
           // getAuthenticationManager 此處是一個認證管理器。是上面auth創出 ProviderManager
		return this.getAuthenticationManager().authenticate(authRequest);
}

// 此處是上面auth配置的默認認證器
public class ProviderManager{

   ...
    // 獲取所有的認證器服務。此服務可以創建時指定。默認匿名服務
    public List<AuthenticationProvider> getProviders() {
		return providers;
	}

}

通過這些鑑權器可以完成所有協議的支持.

我們重點研究  authenticate(authentication) 方法

public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
				() -> messages.getMessage(
						"AbstractUserDetailsAuthenticationProvider.onlySupports",
						"Only UsernamePasswordAuthenticationToken is supported"));

		// Determine username
		String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
				: authentication.getName();

		boolean cacheWasUsed = true;
		UserDetails user = this.userCache.getUserFromCache(username);

		if (user == null) {
			cacheWasUsed = false;

			try {
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);
			}
		
		}
}

protected final UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
	
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
		
}

     // 密碼校驗  passwordEncoder 可自定義
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
	
		String presentedPassword = authentication.getCredentials().toString();

		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"));
		}
	}



最終會調用我們設置的UserDetailService。中loadUserByUsername。獲取用戶。我們自定義和實現基於jdbc的設置.然後通過密碼校驗。完成登錄.

登錄成功後 放入 

SecurityContextHolder.getContext中

在第二次訪問中。調用 SecurityContextPersistenceFilter.doFilter(res,req,chain)

SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain){
    SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
    SecurityContextHolder.setContext(contextBeforeChainExecution);
}
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
		HttpServletRequest request = requestResponseHolder.getRequest();
		HttpServletResponse response = requestResponseHolder.getResponse();
		HttpSession httpSession = request.getSession(false);
}
Tomcat,中
Session doGetSession(boolean create)

如果找到就進行識別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章