当前位置: 首页 > article >正文

一文上手SpringSecurity【二】

书接上回,我们直接引入了spring security的依赖,之后啥也没有干,在访问接口的时候, 就需要认证之后才能访问了 ,咱们没有主动干啥,那肯定有人帮助我们干啥了,这一切都利益出spring boot自动装配机制,下面咱们就看看spring security的自动装配,帮助我们干啥了.

一、Spring Security自动装配

1.1 回顾一下spring boot的自动装配

spring boot会根据类路径中的jar包、类,为jar包里的类自动配置,这样可以极大的减少配置的数量。也就是说spring boot会根据定义在classpath下的类,自动的给你生成一些Bean,并加载到Spring的Context中。

spring boot通过条件注解的配置决定哪些bean可以被自动装配,这些条件定义成具体的xxxAutoConfiguration, 将xxxAutoConfiguration配置到spring.factories文件当中,最好基于spi机制进行加载.但是需要注意的是在spring boot 2.7.0版本及之后的版本,此种方式已经不被推荐使用了,而是加载:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, 如下图所示:
加载
文件内容:

// 部分内容 .... 
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

其核心注解@Import(AutoConfigurationImportSelector.class)读取类路径下的文件需要自动装配的类, 一般都是以xxxAutoConfiguration结尾, 再加载的时候,结合条件注解,过滤一下哪些个bean需要加载.以WebMvcAutoConfiguration为例进行说明.

当读取到了WebMvcAutoConfiguration这个类,开始加载所对应的bean.
webmvc
条件注解
同样在WebMvcAutoConfiguration类当中,观察一下WebMvcAutoConfigurationAdapter类,如下图所示:
属性配置
属性配置

  • 重要的小总结
    • 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,加载各种各样的Bean
    • 结合条件注解@Condition,决定加载哪些、不加载哪些Bean
    • 一般自动装配的类都是以xxxAutoConfigufation结尾
    • 一般默认属性配置类都会在xxxAutoConfiguration类当中引入,以xxxProperties结尾

以Redis自动装配为例,再体会一下.目前我们的工程当中没有引入spring-boot-starer-data-redis依赖,但是在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件当中,有它的自动装配类,如下图所示:
redis
打开瞅瞅
redis
有了以上的知识作为铺垫,我们直接来看spring security自动装配.

1.2 spring security自动装配

1.2.1 @Conditional(DefaultWebSecurityCondition.class)注解

按照我们之前说的,一般都会有xxxAutoConfiguration,我们直接搜索一下瞅瞅. 费劲扒拉的搜了半天,发现直接搜索SpringSecurityAutoConfiguration没有,得搜SecurityAutoConfiguration才能找到.

@AutoConfiguration(before = UserDetailsServiceAutoConfiguration.class)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
	@Bean
	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
	public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
		return new DefaultAuthenticationEventPublisher(publisher);
	}
}

@EnableConfigurationProperties(SecurityProperties.class),表示启用默认装配的各种属性.
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class }), 表示引入其它的配置.我们看SpringBootWebSecurityConfiguration.class这个类. 核心代码如下所示:


// Web 安全性的默认配置。它依赖于 Spring Security 的内容协商策略来确定要使用的身份验证类型。
// 如果用户指定了自己的 SecurityFilterChain bean,这将完全回退,
// 用户应该指定他们想要作为自定义安全配置的一部分配置的所有位。
@Configuration(proxyBeanMethods = false)
	@ConditionalOnDefaultWebSecurity
	static class SecurityFilterChainConfiguration {
		@Bean
		@Order(SecurityProperties.BASIC_AUTH_ORDER)
		SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
			http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
			http.formLogin(withDefaults());
			http.httpBasic(withDefaults());
			return http.build();
		}
	}

@ConditionalOnDefaultWebSecurity注解用于条件化地配置 Web 安全相关的组件,只有在满足特定条件时才会生效. 其核心代码如下所示:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(DefaultWebSecurityCondition.class)
public @interface ConditionalOnDefaultWebSecurity {
}

@Conditional(DefaultWebSecurityCondition.class),这个注解告诉 Spring,只有当DefaultWebSecurityCondition中定义的条件满足时,才会应用被注解的配置类、方法或 Bean. 终于到关键代码了:

class DefaultWebSecurityCondition extends AllNestedConditions {
	DefaultWebSecurityCondition() {
		super(ConfigurationPhase.REGISTER_BEAN);
	}

	@ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })
	static class Classes {
	}

	@ConditionalOnMissingBean({ SecurityFilterChain.class })
	static class Beans {
	}
}

下边重点解释一下这 DefaultWebSecurityCondition类. 重点就看这两个静态内部类:

	@ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })
	static class Classes {
	}
  • @ConditionalOnClass 是一个 Spring Boot 的条件注解。它的意思是:只有在类路径上存在指定的类(SecurityFilterChain 和 HttpSecurity)时,条件才成立。
  • 这里指定了两个类 SecurityFilterChain 和 HttpSecurity,它们属于 Spring Security 的核心类。只有当这些类可用时,Classes 条件才会被满足。
	@ConditionalOnMissingBean({ SecurityFilterChain.class })
	static class Beans {
	}
  • @ConditionalOnMissingBean 也是一个条件注解。它的意思是:如果 Spring 容器中不存在指定的 bean(SecurityFilterChain),条件才会成立。
  • 这个注解确保了当 SecurityFilterChain bean 不存在时,Spring 可以根据此条件加载其他配置。换句话说,它防止了重复创建 SecurityFilterChain 的 bean。

综上所述, 得出结论, 使得DefaultWebSecurityCondition的生效条件有两个:

  1. 类路径中必须存在 SecurityFilterChain 和 HttpSecurity(由 Classes 内部类定义)
  2. pring 容器中不能已经存在 SecurityFilterChain bean(由 Beans 内部类定义)

也就是说,如果我们需要自己定义spring security相关配置,只需要破坏这两个条件当中的一个即可

1.2.2 默认SecurityFilterChain配置

由于现在我们并没有自定义配置,所以现在生效的都是默认配置,回到SpringBootWebSecurityConfiguration类当中,查看生效的默认配置.

@Configuration(proxyBeanMethods = false)
	@ConditionalOnDefaultWebSecurity // 这个注解生效了,则直接开始加载spring security默认配置
	static class SecurityFilterChainConfiguration {

		@Bean
		@Order(SecurityProperties.BASIC_AUTH_ORDER)
		SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
			// 第1行
			http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
			// 第2行
			http.formLogin(withDefaults());
			// 第3行
			http.httpBasic(withDefaults());
			return http.build();
		}
	}
  • 第1行代码: 配置了请求授权规则, requests.anyRequest().authenticated()表示应用程序的所有请求都需要经过身份认证,也就是说,任何请求都不能匿名访问,必须是已登录的用户。
  • 第2行代码: 启用了默认的表单登录(form-based login), 就是咱们在一文上手SpringSecurity一当中看到的那个表单.
  • 第3行代码:启用了默认的HTTP Basic 认证, 这是一种简单的基于用户名和密码的认证机制,通常用于 API 或没有复杂前端的应用中。
  • 【方法入参】: HttpSecurity是配置 HTTP 安全的重要构建器类, 由spring 提供.
  • 【方法出参】: SecurityFilterChain对象, 该对象用于构建和配置安全过滤器链, 整个spring security由n个过滤器组成的

经过以上的分析,可以得出出下的结论:

  • spring security执行了默认配置
  • 默认配置当中,配置了默认的授权规则、默认的登录表单
  • SecurityFilterChain用于构建过滤器链,整个spring security由多个过滤器链组成
  • HttpSecurity核心的构建器类,spring security各种功能都是由它来配置

那么我进一步探讨一个,默认表单是如何生成的,默认的用户名称和密码又是如何创建的呢?

二、默认流程处理

2.1 默认登录页面流程

我们之前看到的SecurityFilterChain接口,它表示过滤器链, 当请求到达服务器的时候,会经过过滤器链,看一眼接口定义:

public interface SecurityFilterChain {
	// 匹配请求
	boolean matches(HttpServletRequest request);
	// 用户存储Filter,即javaee当中的过滤器对象
	List<Filter> getFilters();
}

其实现类:

public final class DefaultSecurityFilterChain implements SecurityFilterChain {
	private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
	private final RequestMatcher requestMatcher;
	private final List<Filter> filters;
	// ...

根据上边说的自动装配,应用程序启动的时候,会自动创建SecurityFilterChain对象,将其放到容器当中,所以我们可以在应用程序的入口,从容器当中取出List,查看一下到底有哪些过滤器.

public class RjSpringSecurityDemo01Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(RjSpringSecurityDemo01Application.class, args);

        DefaultSecurityFilterChain filterChain = context.getBean(DefaultSecurityFilterChain.class);
        List<Filter> filters = filterChain.getFilters();
        for (Filter filter : filters) {
            System.out.println(filter.getClass().getName());
        }
    }
}

日志内容如下所示

org.springframework.security.web.session.DisableEncodeUrlFilter
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextHolderFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.authentication.www.BasicAuthenticationFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.access.ExceptionTranslationFilter
org.springframework.security.web.access.intercept.AuthorizationFilter

稍微看一眼,上边输出的过滤器,这些过滤器共同组成了一个过滤器链,请求打到服务器的时候,依次经过这些过滤器的处理. 从多的过滤器当中,咱们光从字面意思来看,就找咱们认识的,应该能找到:

  • LogoutFilter, 登出过滤器
  • UsernamePasswordAuthenticationFilter, 用户名称和密码认证过滤器
  • DefaultLoginPageGeneratingFilter, 默认的登录页面生成过滤器,【哎哟可以哦,好像就是它啊】
  • DefaultLogoutPageGeneratingFilter, 默认的登出页面生成过滤器
  • ExceptionTranslationFilter, 异常处理过滤器
  • AuthorizationFilter, 授权过滤器

处理流程:

  • 当我们请求/hello接口的时候,由于我们引入了spring security,所以当前的这个请求会经过一个又一个的过滤器
  • 当请求到达AuthorizationFilter过滤器的时候,发现该请求并未认证.请求会被拦截下来,并且抛出AccessDeniedExceptin异常.
  • 当抛出异常之后会被过滤器ExceptionTranslationFilter捕获,这个Filter中会调用LoginUrlAuthenticationEntryPoint#commence方法给客户端返回302,要求客户端重写向到/login页面
  • 客户端发送请求到/login
  • /login会再次被拦截器DefaultLoginPageGeneratingFilter拦截到,并且在该拦截器当中生成登录在页面.

核心源码:
DefaultLoginPageGeneratingFilter过滤器当中

public class DefaultLoginPageGeneratingFilter extends GenericFilterBean {
	public static final String DEFAULT_LOGIN_PAGE_URL = "/login";
	// ...
}

doFilter方法

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		boolean loginError = isErrorPage(request);
		boolean logoutSuccess = isLogoutSuccess(request);
		if (isLoginUrlRequest(request) || loginError || logoutSuccess) {
			// 生成默认的登录页面
			String loginPageHtml = generateLoginPageHtml(request, loginError, logoutSuccess);
			response.setContentType("text/html;charset=UTF-8");
			response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);
			response.getWriter().write(loginPageHtml);
			return;
		}
		chain.doFilter(request, response);
	}

generateLoginPageHtml, 生成页面方法

private String generateLoginPageHtml(HttpServletRequest request, boolean loginError, boolean logoutSuccess) {
		String errorMsg = loginError ? getLoginErrorMessage(request) : "Invalid credentials";
		String contextPath = request.getContextPath();
		StringBuilder sb = new StringBuilder();
		sb.append("<!DOCTYPE html>\n");
		sb.append("<html lang=\"en\">\n");
		sb.append("  <head>\n");
		sb.append("    <meta charset=\"utf-8\">\n");
		sb.append("    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n");
		sb.append("    <meta name=\"description\" content=\"\">\n");
		sb.append("    <meta name=\"author\" content=\"\">\n");
		sb.append("    <title>Please sign in</title>\n");
		sb.append("    <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" "
				+ "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n");
		sb.append("    <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" "
				+ "rel=\"stylesheet\" integrity=\"sha384-oOE/3m0LUMPub4kaC09mrdEhIc+e3exm4xOGxAmuFXhBNF4hcg/6MiAXAf5p0P56\" crossorigin=\"anonymous\"/>\n");
		sb.append("  <
	// ... 其它的略,自己去源码查看即可
}

2.2 默认认证流程分析

从自动装配的类,找到登录表单的默认配置

@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
	http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
	http.formLogin(withDefaults()); // 默认表单配置
	http.httpBasic(withDefaults());
	return http.build();
}

点进方法

public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {
	formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));
	return HttpSecurity.this;
}

FormLoginConfigurer<>()方法

public FormLoginConfigurer() {
	// 调用父类过滤器
	super(new UsernamePasswordAuthenticationFilter(), null);
	usernameParameter("username");
	passwordParameter("password");
}

进入UsernamePasswordAuthenticationFilter(),找到UsernamePasswordAuthenticationFilter#构造方法

public UsernamePasswordAuthenticationFilter() {
	super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
}

得到常量值:

// 表示登录的请求地址和请求姿势
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login","POST");

构造方法内部调用了父类的构造方法,进入到UsernamePasswordAuthenticationFilter父类AbstractAuthenticationProcessingFilter当中,由于是过滤器,实现了接口Filter,所以必然会执行doFilter方法

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
		throws IOException, ServletException {
    // 调用本类的成员方法
	doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
}

UsernamePasswordAuthenticationFilte#当前类的doFiler()

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
		throws IOException, ServletException {
	if (!requiresAuthentication(request, response)) {
		chain.doFilter(request, response);
		return;
	}
	try {
		// 在AbstractAuthenticationProcessingFilter类定义的【模板方法】attemptAuthentication(), 尝试认证, 交给它的子类UsernamePasswordAuthenticationFilte去实现.
		Authentication authenticationResult = attemptAuthentication(request, response);
		// 略....
}

AbstractAuthenticationProcessingFilter类定义的【模板方法】attemptAuthentication()

public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException, IOException, ServletException;

UsernamePasswordAuthenticationFilte实现父类AbstractAuthenticationProcessingFilter定义的模板方法attemptAuthentication(),【这就到了认证的核心逻辑】

	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		if (this.postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
		// 取出用户名称和密码
		String username = obtainUsername(request);
		username = (username != null) ? username.trim() : "";
		String password = obtainPassword(request);
		password = (password != null) ? password : "";
		
		// 将用户名称和密码封装为UsernamePasswordAuthenticationToken对象
		UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
				password);
		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
		// 认证管理器调用认证方法,即去验证我们输入的用户名称和密码是否正确这件事
		return this.getAuthenticationManager().authenticate(authRequest);
	}

this.getAuthenticationManager().authenticate(authRequest)这一块比较复杂,包括两个内容:

  • this.getAuthenticationManager(),返回认证管理器对象AuthenticationManager
  • authenticate(), 接口AuthenticationManager定义认证方法
// 认证管理器接口
public interface AuthenticationManager {
	// 【核心认证方法】, 可以发现,此处的入参跟出参都是Authentication接口,如果认证失败,抛出认证异常,如果认证成功,则
	//  返回一个完整的Authentication对象.
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
}

AuthenticationManager接口的实现类有
4
那么问题来了,接口不能实例化,那么此时this.getAuthenticationManager()指向了它的哪个实现类呢?这里如果有兴趣的伙伴,可以去自己debug一下,我直接说了哈: 使用的是: ProviderManager这个实现类,通过ProviderManager实现类的对象去调用认证方法.

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
	// ...
}

ProviderManager当中重写接口AuthenticationManager的authenticate()方法【核心方法实现】

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
	 // 略去了其它代码....
     for (AuthenticationProvider provider : getProviders()) {
		try {
			// 核心方法
			result = provider.authenticate(authentication);
			if (result != null) {
				copyDetails(authentication, result);
				break;
			}
		}
	}
}

AuthenticationProvider接口表示认证提供的接口,它的定义和AuthenticationManager一样

public interface AuthenticationProvider {
	// 认证方法和认证管理器AuthenticationManager一毛一样
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
	boolean supports(Class<?> authentication);
}

11

AuthenticationProvider是一个接口,不能直接使用,必须在运行时找它一个它的实现类,然后调用认证方法,默认指向的实现类是DaoAuthenticationProvider,由它实现在AuthenticationProvider接口定义的认证方法, 但是你查看它的类定义如下所示:

// 实现类并没有直接实现接口,而在父类当中实现的接口,在此类查找authenticate()的实现发现向了父类,在父当中实现的抽象方法
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
	// ...
}

66
AbstractUserDetailsAuthenticationProvider

// 实现了接口AuthenticationProvider
public abstract class AbstractUserDetailsAuthenticationProvider
		implements AuthenticationProvider, InitializingBean, MessageSourceAware {
	// ...
}

AbstractUserDetailsAuthenticationProvider#authenticate()核心逻辑

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
	String username = determineUsername(authentication);
	boolean cacheWasUsed = true;
	UserDetails user = this.userCache.getUserFromCache(username);
	if (user == null) {
		cacheWasUsed = false;
		try {
			// 这里也是使用了【模板】方法,在当前类AbstractUserDetailsAuthenticationProvider定义的模板方法,具体功能
			// 由子类DaoAuthenticationProvider去实现
			user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
		}
}

AbstractUserDetailsAuthenticationProvider#retrieveUser()模板方法定义

// 检索用户
protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
		throws AuthenticationException;

AbstractUserDetailsAuthenticationProvider子类DaoAuthenticationProvider实现父类定义的模板方法retrieveUser
1

@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
		throws AuthenticationException {
	prepareTimingAttackProtection();
	try {
		// 核心代码
		// 根据用户名称去数据源当中查找出用户信息,并将用户对象封装为UserDetails 
		UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
		if (loadedUser == null) {
			throw new InternalAuthenticationServiceException(
					"UserDetailsService returned null, which is an interface contract violation");
		}
		return loadedUser;
	}

this.getUserDetailsService().loadUserByUsername(username);

  • this.getUserDetailsService(), 返回接口UserDetailsService, 即用户详情服务.

接口定义

public interface UserDetailsService {
	// 根据用户名,去数据源当中查询出用户信息,并将认证的用户信息封装成UserDetails对象
	// 返回值: UserDetails是一个接口,主要包含三个东西: 用户名称、密码、权限列表
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

3

this.getUserDetailsService()在运行时,返回一个实现了UserDetailsService接口的实现类的对象.这里返回的是: InMemoryUserDetailsManager

public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {
   // ...
}

InMemoryUserDetailsManager#loadUserByUsername()

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
	// 根据用户名称,取出用户信息,并且封装成了UserDetails对象
	UserDetails user = this.users.get(username.toLowerCase());
	if (user == null) {
		throw new UsernameNotFoundException(username);
	}
	// User是个实现了接口UserDetails的实现类
	return new User(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(),
			user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
}

最返回信息UserDetails .

至此认证流程基本分析完毕.剩下的是验证用户名称和密码的过程、存储缓存等步骤了.下篇再进行分析.
大家初看一大堆接口和实现类,还有各种模板方法,不必慌张,整个流程还是比较清晰的,建议先跟着文章,自己动手实践一下.多看几次也就记住了.这玩意没有捷径,我亦无他,惟手熟尔矣.

三、总结

3.1 重点内容总结

  • spring boot 3.3.4 自动装配核心内容
  • spring security 过滤器链
  • spring security自动装配
  • spring security默认流程处理之默认的登录页面处理流程
  • spring security 认证流程分析

3.2 下篇内容

  • spring security认证流程图解
  • 默认密码生成策略
  • 自定义认证页面和密码

http://www.kler.cn/news/324922.html

相关文章:

  • Flink 结合kafka 实现端到端的一致性原理
  • 一文说完c++全部基础知识,IO流(二)
  • 2、Java 基础 - 面向对象基础
  • Qt 信号重载问题--使用lambda表达式--解决方法
  • 国庆节快乐|中国何以成为中国
  • 在Spring项目中使用MD5对数据库加密
  • QT中基于QMatrix4x4与QVector3D的三维坐标变换类实现
  • 理想汽车使用无仪表盘设计的原因和弊端
  • 传统行业选择企业大文件传输系统需要注意哪些?
  • 【C语言刷力扣】2079.给植物浇水
  • 关于MATLAB计算3维图的向量夹角总是不正确的问题记录
  • 金融加密机的定义与功能
  • 【RabbitMQ——SpringBoot整合】
  • 少帅进行曲
  • 模拟实现(优先级队列)priority_queue:优先级队列、仿函数、 反向迭代器等的介绍
  • 再见 ESNI,你好 ECH!—— ECH的前世今生
  • 负载均衡(Load Balancing)是一种计算机技术,用于在网络应用中分配工作负载,以优化资源使用、最大化吞吐量、减少响应时间以及避免过载。
  • Elasticsearch实战应用:构建高效搜索引擎
  • vue 同一个页面第二次跳转路由内容不更新
  • SQL常用数据过滤 - EXISTS运算符
  • 基于SpringBoot校园失物招领系统设计与实现
  • 职业技能大赛-单元测试笔记分享
  • Git GUI操作流程
  • 使用Spring Cloud Config和JCE加密配置文件的实战教程
  • 新版Android Studio Koala 导入github第三方依赖 maven仓库的处理方法 (java版)
  • 云端融合,远程监控:EasyCVR工地无线安防监控系统的云解决方案
  • 故障诊断 | 基于双路神经网络的滚动轴承故障诊断
  • dig和nmap的区别
  • Python 数据分析与可视化:从入门到实践
  • hbase之布隆过滤器