SpringSecurity系列——用户名密码登录day3-1(源于官网5.7.2版本)

181 篇文章 3 订阅
24 篇文章 31 订阅

前言

源于官方最新5.7.2文档,若你觉得官方文档阅读起来很枯燥,内容复杂,我提供了解析概括在每个部分的结尾,我对官方文档的内容做了一些改变,实例代码我会在后续进行更新,请查看如:SpringSecurity系列——认证架构实例代码的文章

但是如果你有能力,还是推荐直接阅读官方文档

本文的实例代码

简单自定义登录

关于本文

前面是对官文的翻译以及修改
最后一个解释概括则是对整个自定义密码策略的解释,若你感觉前面的内容难以理解,可以直接看最后一个解释概括

表单登录

重定向登录页面

在这里插入图片描述

  1. 首先,用户向未授权的资源 /private 发出未经身份验证的请求。
  2. Spring Security 的 FilterSecurityInterceptor 指示通过抛出 AccessDeniedException 拒绝未经身份验证的请求。
  3. 由于用户未通过身份验证,ExceptionTranslationFilter 会启动 Start Authentication,并使用配置的 AuthenticationEntryPoint 将重定向发送到登录页面。 在大多数情况下,AuthenticationEntryPoint 是 LoginUrlAuthenticationEntryPoint 的一个实例。
  4. 然后浏览器将请求它被重定向到的登录页面。
  5. 应用程序中的某些东西,必须呈现登录页面。

用户名密码提交

提交用户名和密码后,UsernamePasswordAuthenticationFilter 会验证用户名和密码。 UsernamePasswordAuthenticationFilter 扩展了 AbstractAuthenticationProcessingFilter
在这里插入图片描述

  1. 当用户提交他们的用户名和密码时,UsernamePasswordAuthenticationFilter 通过从 HttpServletRequest 中提取用户名和密码来创建一个 UsernamePasswordAuthenticationToken,这是一种身份验证。

  2. 接下来,将 UsernamePasswordAuthenticationToken 传递给 AuthenticationManager 进行身份验证。 AuthenticationManager 的详细信息取决于用户信息的存储方式。

  3. 如果认证失败,则失败

    1. SecurityContextHolder 被清除。
    2. 调用 RememberMeServices.loginFail。 如果记住我没有配置,这是一个空操作。
    3. AuthenticationFailureHandler 被调用。
  4. 如果认证成功,则为 Success。

    1. SessionAuthenticationStrategy 收到新登录通知。
    2. Authentication 在 SecurityContextHolder 上设置。
    3. 调用 RememberMeServices.loginSuccess。 如果记住我没有配置,这是一个空操作。
    4. ApplicationEventPublisher 发布一个 InteractiveAuthenticationSuccessEvent。
    5. AuthenticationSuccessHandler 被调用。 通常这是一个 SimpleUrlAuthenticationSuccessHandler,当我们重定向到登录页面时,它将重定向到 ExceptionTranslationFilter 保存的请求。

默认登录表单

默认情况下启用 Spring Security 表单登录。 但是,一旦提供了任何基于 servlet 的配置,就必须明确提供基于表单的登录。 可以在下面找到最小的显式 Java 配置:

public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		.formLogin(withDefaults());
	// ...
}

在此配置中,Spring Security 将呈现默认登录页面。 大多数生产应用程序都需要自定义登录表单。

自定义登录表单

public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		.formLogin(form -> form
			.loginPage("/login")
			.permitAll()
		);
	// ...
}

内存认证

Spring Security 的 InMemoryUserDetailsManager 实现 UserDetailsS​​ervice 以支持存储在内存中的基于用户名/密码的身份验证。 InMemoryUserDetailsManager 通过实现 UserDetailsManager 接口提供对 UserDetails 的管理。 当 Spring Security 配置为接受用户名/密码进行身份验证时,使用基于 UserDetails 的身份验证。

内存认证配置

@Bean
public UserDetailsService users() {
	UserDetails user = User.builder()
		.username("user")
		.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
		.roles("USER")
		.build();
	UserDetails admin = User.builder()
		.username("admin")
		.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
		.roles("USER", "ADMIN")
		.build();
	return new InMemoryUserDetailsManager(user, admin);
}

使用User.withDefaultPasswordEncoder进行内存认证配置(非生产环境)

利用 User.withDefaultPasswordEncoder 来确保存储在内存中的密码受到保护。 但是,它不能防止通过反编译源代码来获取密码。 出于这个原因, User.withDefaultPasswordEncoder 应该只用于“入门”,而不是用于生产

UserBuilder users = User.withDefaultPasswordEncoder();
@Bean
public UserDetailsService users() {
	// The builder will ensure the passwords are encoded before saving in memory
	UserBuilder users = User.withDefaultPasswordEncoder();
	UserDetails user = users
		.username("user")
		.password("password")
		.roles("USER")
		.build();
	UserDetails admin = users
		.username("admin")
		.password("password")
		.roles("USER", "ADMIN")
		.build();
	return new InMemoryUserDetailsManager(user, admin);
}

解释概括

  1. InMemoryUserDetailsManager 实现 UserDetailsS​​ervice 以支持存储在内存中的基于用户名/密码的身份验证
  2. 我们可以使用UserDetails对一个用户配置多个身份使用.role()方法
  3. 使用@Bean注册为实例
  4. 返回InMemoryUserDetailsManager实例

JDBC身份验证

Spring Security 的 JdbcDaoImpl 实现 UserDetailsS​​ervice 以支持使用 JDBC 检索的基于用户名/密码的身份验证。 JdbcUserDetailsManager 扩展了 JdbcDaoImpl 以通过 UserDetailsManager 接口提供对 UserDetails 的管理。当 Spring Security 配置为接受用户名/密码进行身份验证时,使用基于 UserDetails 的身份验证。

在以下部分中,我们将讨论:

  1. Spring Security JDBC 身份验证使用的默认模式
  2. 设置数据源
  3. JdbcUserDetailsManager Bean

用户架构

默认的用户架构数据库

看到这里,大家只需要在数据库中使用如下sql生产默认的两张表即可,这里我对官网给出的sql做了一点修改,官网的sql语句是会报错的

create table users(
	username varchar(50) not null primary key,
	password varchar(500) not null,
	enabled boolean not null
);

create table authorities (
	username varchar(50) not null,
	authority varchar(50) not null,
	constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
新建数据库

在这里插入图片描述

复制运行

在这里插入图片描述

结果

生成的表如下
在这里插入图片描述

Setting up a DataSource(配置一个数据源)

在我们配置 JdbcUserDetailsManager 之前,我们必须创建一个 DataSource。 在我们的示例中,我们将设置一个使用默认用户模式初始化的嵌入式 DataSource。实际生产环境中不会这样干,而是会使用外部数据源

@Bean
DataSource dataSource() {
	return new EmbeddedDatabaseBuilder()
		.setType(H2)
		.addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
		.build();
}

JdbcUserDetailsManager Bean(JDBC用户信息管理Bean实例)

如下为JdbcUserDetailsManager

@Bean
UserDetailsManager users(DataSource dataSource) {
	UserDetails user = User.builder()
		.username("user")
		.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
		.roles("USER")
		.build();
	UserDetails admin = User.builder()
		.username("admin")
		.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
		.roles("USER", "ADMIN")
		.build();
	JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
	users.createUser(user);
	users.createUser(admin);
	return users;
}

UserDetails

UserDetails 由 UserDetailsS​​ervice 返回。 DaoAuthenticationProvider 验证 UserDetails,然后返回一个 Authentication,该 Authentication 的主体是配置的 UserDetailsS​​ervice 返回的 UserDetails。

UserDetailsService

DaoAuthenticationProvider 使用 UserDetailsS​​ervice 来检索用户名、密码和其他属性,以使用用户名和密码进行身份验证。 Spring Security 提供了 UserDetailsS​​ervice 的内存和 JDBC 实现。

您可以通过将自定义 UserDetailsS​​ervice 公开为 bean 来定义自定义身份验证。 例如,假设 CustomUserDetailsS​​ervice 实现 UserDetailsS​​ervice,以下将自定义身份验证:

这仅在尚未填充 AuthenticationManagerBuilder 且未定义 AuthenticationProviderBean 时使用。

@Bean
CustomUserDetailsService customUserDetailsService() {
	return new CustomUserDetailsService();
}

PasswordEncoder(密码编译器)

Spring Security 的 servlet 支持通过与 PasswordEncoder 集成来安全地存储密码。 可以通过公开 PasswordEncoder Bean 来自定义 Spring Security 使用的 PasswordEncoder 实现。

DaoAuthenticationProvider

DaoAuthenticationProvider是一个 AuthenticationProvider 实现,它利用 UserDetailsS​​ervice 和 PasswordEncoder 来验证用户名和密码
在这里插入图片描述

  1. 来自读取用户名和密码的身份验证过滤器将 UsernamePasswordAuthenticationToken 传递给由 ProviderManager 实现的 AuthenticationManager。
  2. ProviderManager 被配置为使用 DaoAuthenticationProvider 类型的 AuthenticationProvider。
  3. DaoAuthenticationProvider 从 UserDetailsS​​ervice 中查找 UserDetails。
  4. DaoAuthenticationProvider 然后使用 PasswordEncoder 验证上一步返回的 UserDetails 上的密码。
  5. 身份验证成功时,返回的身份验证是 UsernamePasswordAuthenticationToken 类型,并且具有一个主体,即配置的 UserDetailsS​​ervice 返回的 UserDetails。 最终,返回的 UsernamePasswordAuthenticationToken 将由身份验证过滤器在 SecurityContextHolder 上设置。

解释概括

  1. 在未进行自定义登录操作时,我们发送的任何请求都会被拒绝并重定向到默认的/login地址的默认登录界面
  2. 提交认证信息后,UsernamePasswordAuthenticationFilter 会验证用户名和密码, 从HttpServletRequest 中提取用户名和密码来创建一个 UsernamePasswordAuthenticationToken
  3. 但最后实际是AuthenticationManager 进行身份验证(流程请见上篇文章
  4. 当 Spring Security 配置为接受用户名/密码进行身份验证时,使用基于 UserDetails 的身份验证
  5. InMemoryUserDetailsManager 实现 UserDetailsS​​ervice 以支持存储在内存中的基于用户名/密码的身份验证,对UserDetails进行管理
  6. 当使用外部数据源(数据库认证)我们需要自定义 UserDetailsS​​ervice 公开为 bean 来定义自定义身份验证,此时使用到JdbcDaoImpl 实现 UserDetailsS​​ervice 以支持使用 JDBC 检索的基于用户名/密码的身份验证
  7. JdbcUserDetailsManager 扩展了 JdbcDaoImpl 以通过 UserDetailsManager 接口提供对 UserDetails 的管理
  8. DaoAuthenticationProvider是一个 AuthenticationProvider 实现,它利用 UserDetailsS​​ervice 和 PasswordEncoder 来验证用户名和密码
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值