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)

如果找到就进行识别

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