Spring Security 实战干货:UsernamePasswordAuthenticationFilter 源码分析
时间:2022-07-22
本文章向大家介绍Spring Security 实战干货:UsernamePasswordAuthenticationFilter 源码分析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1. 前言
欢迎阅读 Spring Security 实战干货系列文章,在集成Spring Security安全框架的时候我们最先处理的可能就是根据我们项目的实际需要来定制注册登录了,尤其是Http登录认证。根据以前的相关文章介绍,Http登录认证由过滤器UsernamePasswordAuthenticationFilter
进行处理。我们只有把这个过滤器搞清楚才能做一些定制化。今天我们就简单分析它的源码和工作流程。
2. UsernamePasswordAuthenticationFilter 源码分析
UsernamePasswordAuthenticationFilter
继承于AbstractAuthenticationProcessingFilter
(另文分析)。它的作用是拦截登录请求并获取账号和密码,然后把账号密码封装到认证凭据UsernamePasswordAuthenticationToken
中,然后把凭据交给特定配置的AuthenticationManager
去作认证。源码分析如下:
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
// 默认取账户名、密码的key
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
// 可以通过对应的set方法修改
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
// 默认只支持 POST 请求
private boolean postOnly = true;
// 初始化一个用户密码 认证过滤器 默认的登录uri 是 /login 请求方式是POST
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
// 实现其父类 AbstractAuthenticationProcessingFilter 提供的钩子方法 用去尝试认证
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// 判断请求方式是否是POST
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
// 先去 HttpServletRequest 对象中获取账号名、密码
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 然后把账号名、密码封装到 一个认证Token对象中,这是就是一个通行证,但是这时的状态时不可信的,一旦通过认证就变为可信的
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// 会将 HttpServletRequest 中的一些细节 request.getRemoteAddr() request.getSession 存入的到Token中
setDetails(request, authRequest);
// 然后 使用 父类中的 AuthenticationManager 对Token 进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
// 获取密码 很重要 如果你想改变获取密码的方式要么在此处重写,要么通过自定义一个前置的过滤器保证能此处能get到
@Nullable
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
// 获取账户很重要 如果你想改变获取密码的方式要么在此处重写,要么通过自定义一个前置的过滤器保证能此处能get到
@Nullable
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
// 参见上面对应的说明为凭据设置一些请求细节
protected void setDetails(HttpServletRequest request,
UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
// 设置账户参数的key
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
// 设置密码参数的key
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
// 认证的请求方式是只支持POST请求
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
}
为了加强对流程的理解,我特意画了一张图来对这个流程进行清晰的说明:
UsernamePasswordAuthenticationFilter工作流程
3. 我们可以定制什么
根据上面的流程,我们理解了UsernamePasswordAuthenticationFilter
工作流程后可以做这些事情:
- 定制我们的登录请求 URI 和请求方式。
- 登录请求参数的格式定制化,比如可以使用JSON格式提交甚至几种并存。
- 如何将用户名和密码封装入凭据
UsernamePasswordAuthenticationToken
,定制业务场景需要的特殊凭据。
4. 我们会有什么疑问
AuthenticationManager
从哪儿来,它又是什么,它是如何对凭据进行认证的,认证成功的后续细节是什么,认证失败的后续细节是什么。
- WPF/XML 资源及相关开源项目
- Android应用底部导航栏(选项卡)实例
- 有关 ASMX 2.0、WSE 3.0 和 WCF 的内容
- 微信小程序游戏其实一般,我也就站在寒风里玩了一个小时
- beagle MONO 应用的desktop search
- Python3与OpenCV3.3 图像处理(一)-环境搭建与简单DEMO
- winform中利用正则表达式得到有效的电话/手机号
- 浅述RDF,畅想一下FOAF应用
- 数据源控件参数类Parameter
- 我们来继续研究 mybatis 框架sql映射文件的属性
- 开源.NET邮件服务器
- 次次获得《头脑王者》满分的秘诀
- 如何在ASP.NET 2.0中定制Expression Builders
- codeproject 几篇asp.net文章
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- [javascript] 支付宝小程序网络GET请求
- [PHP] 生成器模式-创建型设计模式
- [PHP] 工厂方法设计模式-创建型设计模式
- [PHP] 对象池模式-创建型设计模式
- [PHP] 原型模式-创建型设计模式
- [PHP] 简单工厂模式-创建型设计模式
- [PHP] 单例模式-创建型设计模式
- [PHP] 静态工厂模式-创建型设计模式
- [PHP] 适配器模式-结构型设计模式
- [PHP] 使用curl扩展POST或者PUT时数据不全和连接中断的排查
- [PHP] 桥接模式-结构型设计模式
- [PHP] 组合模式-结构型设计模式
- [GO-FLY] GO-FLY客服实现浏览器消息提示音
- [PHP] 数据映射器模式-结构型设计模式
- [GO] 变参函数-GO中函数传递变长参数