代码实现
从Spring Security官网文档可以看到,我们想要实现一个基于内存的用户名密码认证,需要编写配置,如下:
在我们的项目中,建立如下项目结构:
我们将Spring Security有关配置全部放置于Security包中。
/**
* @author 无涯子
* @date 2024/4/20
*/
@Configuration
public class WebSecurityConfig {
@Bean
public UserDetailsService userDetailsService(){
// 创建内存用户管理器
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// 添加一个用户
manager.createUser(User.withDefaultPasswordEncoder()
.username("user").password("password").roles("USER").build());
return manager;
}
}
在templates目录下面新建一个index.html
,内容如下:
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title>Hello Security!</title>
</head>
<body>
<h1>Hello Security</h1>
<a th:href="@{/logout}">Log Out</a>
</body>
</html>
启动项目运行:
底层代码分析
UsernamePasswordAuthenticationFilter
从这里的名字可以大概看出会读取表单中username
和password
两个表单项的值,请求方法是POST
在这个类里面有一个方法attemptAuthentication
,主要进行尝试去验证。
- 会查看请求是不是
POST
,不是会抛出异常 - 读取
username
、password
,并去掉收尾空格 - 使用
username
、password
生成一个UsernamePasswordAuthenticationToken
对象,主要是用于后面从内存中读取的对象对比 authenticate()
调用认证方法进行认证
AbstractUserDetailsAuthenticationProvider
我们先看一个继承关系:
这个接口只有一个方法authenticate
就是进行认证,我们直接跳转到我们的authenticate
这个方法主要根据我们输入的用户名去查询内存中存储的所有用户信息,查看有没有与用户名匹配的,有就返回,没有则抛出异常。返回的是一个UserDetails
类型
我们在我们的配置中创建基于内存管理时候就添加过一个这个类型的用户信息:
loadUserByUsername()
方法非常重要,后面我们实现基于数据库的用户认证也会重写这个方法,这个方法就是根据你输入的username查询用户信息,返回一个UserDetails
。
从内存中找到用户信息后,我们将我们输入的密码与查询出来对象中的密码进行对比:
如果比对成功,认证流程重要的过程就结束了。