本文共 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防护的情况下)
默认用户名和密码的表单参数为username
和password
,可以自定义
使用 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检索的基于用户名/密码的身份验证的支持。JdbcUserDetailsManage
r扩展了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
UserDetails
由UserDetailsService
返回。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
实现,它利用UserDetailsService
和PasswordEncoder
来验证用户名和密码。
处理流程
LDAP Authentication
LDAP经常被组织用作用户信息的中心存储库和身份验证服务。它还可以用于存储应用程序用户的角色信息。
转载地址:https://console.blog.csdn.net/article/details/116464236 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!