Spring Security 之用户名/密码 认证
发布日期:2021-06-29 15:52:28 浏览次数:2 分类:技术文章

本文共 7889 字,大约阅读时间需要 26 分钟。

验证用户身份的最常见方法之一是验证用户名和密码

Spring Security提供了以下内置机制来从HttpServletRequest读取用户名和密码:

每种受支持的读取用户名和密码的机制都可以利用任何受支持的存储机制:

  • 内存存储
  • 数据库存储
  • 自定义数据存储
  • LDAP存储

表单登录 Form Login

Spring Security支持通过html表单提供用户名和密码

重定向到的登录表单

在这里插入图片描述

提交用户名和密码后,UsernamePasswordAuthenticationFilter将对用户名和密码进行验证。UsernamePasswordAuthenticationFilter扩展了AbstractAuthenticationProcessingFilter

在这里插入图片描述

Spring Security表单登录在默认情况下是启用的。但是,只要提供了任何基于servlet的配置,就必须显式地提供基于表单的登录。

protected void configure(HttpSecurity http) {
http // ... .formLogin(withDefaults());}

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

下面的配置演示了如何在表单中提供自定义登录表单

protected void configure(HttpSecurity http) throws Exception {
http // ... .formLogin(form -> form .loginPage("/login") .permitAll() );}

当在Spring Security配置中指定登录页面时,需要自己实现该页面。

下面是一个Thymeleaf模板:

            Please Log In                

Please Log In

Invalid username and password.
You have been logged out.

关于默认HTML表单有几个关键点:

表单应该用post方法登录

该表单将需要包括一个CSRF令牌,(没有关闭CSRF防护的情况下)

默认用户名和密码的表单参数为usernamepassword,可以自定义

使用 SpringMVC的情况下,需要一个Controller处理GET /login请求

@Controllerclass LoginController {
@GetMapping("/login") String login() {
return "login"; }}

Basic Authentication

本节详细介绍Spring Security如何为基于servlet的应用程序提供对 的支持。

在这里插入图片描述

当客户端接收到WWW-Authenticate报头时,它知道应该用用户名和密码重试。下面是处理用户名和密码的流程。

在这里插入图片描述

默认情况下,Spring Security的HTTP Basic Authentication是启用的。但是,只要提供了任何基于servlet的配置,就必须显式地提供HTTP Basic。

protected void configure(HttpSecurity http) {
http // ... .httpBasic(withDefaults());}

Digest Authentication 摘要认证

⚠️ 不推荐使用摘要身份验证,因为它被认为不安全。最明显的问题是必须以明文、加密或MD5格式存储密码。所有这些存储格式都是不安全的。相反,您应该使用单向自适应密码散列(即bCrypt, PBKDF2, SCrypt等)存储凭证,这是摘要认证不支持的。

摘要身份验证试图解决基本身份验证的许多弱点,特别是通过确保凭证永远不会通过网络以明文发送。许多浏览器支持摘要身份验证。

摘要中心身份验证是一个"nonce"。这是服务器生成的值。Spring Security的nonce采用以下格式:

base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))expirationTime:   The date and time when the nonce expires, expressed in millisecondskey:              A private key to prevent modification of the nonce token

需要使用NoOpPasswordEncoder配置不安全的明文密码存储。

@AutowiredUserDetailsService userDetailsService;DigestAuthenticationEntryPoint entryPoint() {
DigestAuthenticationEntryPoint result = new DigestAuthenticationEntryPoint(); result.setRealmName("My App Relam"); result.setKey("3028472b-da34-4501-bfd8-a355c42bdf92");}DigestAuthenticationFilter digestAuthenticationFilter() {
DigestAuthenticationFilter result = new DigestAuthenticationFilter(); result.setUserDetailsService(userDetailsService); result.setAuthenticationEntryPoint(entryPoint());}protected void configure(HttpSecurity http) throws Exception {
http // ... .exceptionHandling(e -> e.authenticationEntryPoint(authenticationEntryPoint())) .addFilterBefore(digestFilter());}

In-Memory Authentication

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

示例

创建UserDetailService

@Beanpublic 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创建userDetails对象@Beanpublic 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);}

JDBC Authentication

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

Default Schema

Spring Security为基于JDBC的身份验证提供默认查询。本节提供与默认查询相对应的默认模式。您将需要调整模式,以匹配与您正在使用的查询和数据库方言相匹配的定制。

User Schema

JdbcDaoImpl需要表来加载用户的密码、帐户状态(启用或禁用)和权限(角色)列表。

ℹ️ 默认表定义文件:org/springframework/security/core/userdetails/jdbc/users.ddl

create table users(    username varchar_ignorecase(50) not null primary key,    password varchar_ignorecase(500) not null,    enabled boolean not null);create table authorities (    username varchar_ignorecase(50) not null,    authority varchar_ignorecase(50) not null,    constraint fk_authorities_users foreign key(username) references users(username));create unique index ix_auth_username on authorities (username,authority);
Group Schema
create table groups (    id bigint generated by default as identity(start with 0) primary key,    group_name varchar_ignorecase(50) not null);create table group_authorities (    group_id bigint not null,    authority varchar(50) not null,    constraint fk_group_authorities_group foreign key(group_id) references groups(id));create table group_members (    id bigint generated by default as identity(start with 0) primary key,    username varchar(50) not null,    group_id bigint not null,    constraint fk_group_members_group foreign key(group_id) references groups(id));

设置数据源

在配置JdbcUserDetailsManager之前,必须创建一个数据源。在我们的示例中,我们将设置一个使用默认用户模式初始化的嵌入式数据源。

@BeanDataSource dataSource() {
return new EmbeddedDatabaseBuilder() .setType(H2) .addScript("classpath:org/springframework/security/core/userdetails/jdbc/users.ddl") .build();}

在生产环境中,必须设置一个外部的数据源

SpringBoot项目中,配置了MyBatis后一般可以直接使用注入好的DataSource

JdbcUserDetailsManager Bean

向容器中注入一个UserDetailsManager管理用户

@Beanpublic UserDetailsService jdbcUserDetailsService(DataSource dataSource){
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(); jdbcUserDetailsManager.setDataSource(dataSource); final User.UserBuilder userBuilder = User.builder().passwordEncoder(s -> passwordEncoder().encode(s)); final UserDetails userAdmin = userBuilder.username("admin").password("123456").roles("admin", "user").build(); final UserDetails userUser = userBuilder.username("user").password("123456").roles( "user").build(); jdbcUserDetailsManager.updateUser(userAdmin); jdbcUserDetailsManager.updateUser(userUser); return jdbcUserDetailsManager;}

配置使用 JdbcUserDetailsManager

@Overrideprotected void configure(HttpSecurity http) throws Exception {
//.... http.userDetailsService(jdbcUserDetailsService);}

注意:只要配置了UserDetailsService Bean,可以不用显式指定http.userDetailsService

UserDetails

UserDetailsUserDetailsService返回。DaoAuthenticationProvider验证UserDetails,然后返回一个Authentication,该Authentication有一个主体,该主体是由已配置的UserDetailsService返回的UserDetails

UserDetailsService

DaoAuthenticationProvider使用UserDetailsService检索用户名、密码和其他属性,以验证用户名和密码。Spring Security提供了UserDetailsService的内存和JDBC实现。

可以向容器中注入UserDetailsService, Spring Security会自动调用

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

PasswordEncoder

Spring Security的servlet通过与PasswordEncoder集成来支持安全存储密码,通过注入PasswordEncoder Bean来实现自定义PasswordEncoder

DaoAuthenticationProvider

DaoAuthenticationProvider是一个AuthenticationProvider实现,它利用UserDetailsServicePasswordEncoder来验证用户名和密码。

处理流程

在这里插入图片描述

LDAP Authentication

LDAP经常被组织用作用户信息的中心存储库和身份验证服务。它还可以用于存储应用程序用户的角色信息。

转载地址:https://console.blog.csdn.net/article/details/116464236 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:SpringSecurity如何处理logout注销操作
下一篇:Spring Security框架认证整体梳理

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月13日 22时05分33秒