(3)spring security - 认识PasswordEncoder

server/2024/12/15 5:38:35/

目录

  • 1.简介
    • 1.1.简单了解认证流程
  • 2.密码验证
  • 3.PasswordEncoder的内置实现
  • 4.小结

目标:

  1. 简单了解认证的流程
  2. 简单认识spring security中的Password Encoder

1.简介

在这里插入图片描述
还是以这幅图为基础,认识Password Encoder到底是什么?

1.1.简单了解认证流程

在这里插入图片描述
当我们在登录表单上输入用户名和密码后,将会被UsernamePasswordAuthenticationFilter过滤器拦截,在public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)函数中,尝试从请求中获取用户名和密码。

java">//从请求中获取用户名
String username = obtainUsername(request);
username = (username != null) ? username.trim() : "";
//从请求中获取密码
String password = obtainPassword(request);
password = (password != null) ? password : "";

在这里插入图片描述
接着封装UsernamePasswordAuthenticationToken对象:

java">//将用户名和密码封装为未认证的UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,password);

在这里插入图片描述
设置details:

java">//设置authRequest对象中的details
setDetails(request, authRequest);

在这里插入图片描述
认证authRequest封装的信息:

java">this.getAuthenticationManager().authenticate(authRequest);

this.getAuthenticationManager()实际是获取一个AuthenticationManager类型的对象,返回的是ProviderManager类型的对象,并调用authenticate函数。当认证成功后会返回一个Authentication对象。

ProviderManager在Spring Security中扮演着认证提供者管理器的角色,其主要作用是管理和协调多个AuthenticationProvider的认证过程。ProviderManager是AuthenticationManager的一个实现,它通过使用一组AuthenticationProvider来处理认证请求。
其主要功能和作用包括:

  1. 认证过程管理‌:ProviderManager会遍历所有支持的AuthenticationProvider,找到第一个能够成功认证的AuthenticationProvider并返回填充更多信息的Authentication对象。如果某个AuthenticationProvider认证失败,ProviderManager会尝试下一个AuthenticationProvider,直到找到一个成功的认证结果或者所有Provider都尝试完毕‌。
  2. 异常处理‌:在认证过程中,如果某个AuthenticationProvider在认证过程中抛出AuthenticationException,ProviderManager会继续尝试下一个Provider。但如果抛出AccountStatusException或InternalAuthenticationServiceException,则会停止认证过程并抛出异常‌。
  3. 扩展性和灵活性‌:ProviderManager支持多种类型的AuthenticationProvider,包括数据库认证、LDAP认证等,这使得Spring Security能够处理不同类型的认证请求,增强了系统的灵活性和扩展性‌。
    在这里插入图片描述

由于我们现在使用的是数据库认证,所以实际使用的认证程序是DaoAuthenticationProvider:
在这里插入图片描述
实际调用的authenticate函数是AbstractUserDetailsAuthenticationProvider抽象类中的authenticate函数:

java">	@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {//......//获取你在登录表单中输入的用户名String username = determineUsername(authentication);//......//查询缓存中是否有匹配的用户信息,实际user为nullUserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {//使用retrieveUser函数,在数据库中获取用户信息//实际是使用我们自己在EmployeeDetailsService.java中定义的loadUserByUsername函数,//来获取数据库中的用户信息user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException ex) {//异常处理}//......}try {//检查账户是否锁定、账户是否可用、账户是否过期this.preAuthenticationChecks.check(user);//检查密码是否为null,如果密码不为null,获取密码并验证密码additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);}catch (AuthenticationException ex) {//异常处理}//密码验证通过后,检查密码是否过期this.postAuthenticationChecks.check(user);//缓存用户信息if (!cacheWasUsed) {this.userCache.putUserInCache(user);}Object principalToReturn = user;//......//重新生成一个已经通过认证的UsernamePasswordAuthenticationToken(实际上它也是Authentication类型的),并返回它//最终的Authentication对象被返回给UsernamePasswordAuthenticationFilter过滤器的attemptAuthentication函数return createSuccessAuthentication(principalToReturn, authentication, user);}

接着执行AbstractAuthenticationProcessingFilterdoFilter函数。
在这里插入图片描述
doFilter函数中将会:

  1. 组合多个SessionAuthenticationStrategy实例,以提供更灵活和强大的会话管理功能。
  2. 在securitycontexholder上设置认证成功的身份验证对象
  3. 通知配置的RememberMeServices登录成功(当前没做任何事情)
  4. 通过配置的ApplicationEventPublisher触发一个InteractiveAuthenticationSuccessEvent
  5. 将其他行为委托给AuthenticationSuccessHandler。

最后回到FilterChainProxy,执行其它过滤器。

2.密码验证

上一节说到,密码的验证是由一下代码实现的:

java">//检查密码是否为null,如果密码不为null,获取密码并验证密码
//其中user保存的数据库中的用户信息数据
//authentication保存的是登录表单中输入的用户名和密码
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);

additionalAuthenticationChecks的源代码是:

java">	@Override@SuppressWarnings("deprecation")protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {//判断密码是否为nullif (authentication.getCredentials() == null) {//打印日志//抛出异常}//获取明文密码String presentedPassword = authentication.getCredentials().toString();//验证密码if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {//密码验证失败//打印日志//抛出异常}}

其中的matches函数源码如下:

java">	@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {if (rawPassword == null) {throw new IllegalArgumentException("rawPassword cannot be null");}if (encodedPassword == null || encodedPassword.length() == 0) {this.logger.warn("Empty encoded password");return false;}//判断是否是BCryptif (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {this.logger.warn("Encoded password does not look like BCrypt");return false;}//检查明文密码(登陆表单中输入的密码)是否与先前散列的密码(数据库中的密码)匹配return BCrypt.checkpw(rawPassword.toString(), encodedPassword);}

checkpw实际就是比较两个字符串:

  1. 首先将明文密码编码成与数据库中的密码一样的格式
  2. 然后使用MessageDigest.isEqual方法,比较连个已编码的密码是否具有相同的长度并且对应位置的所有字节都相等

存储在数据库中的已编码的密码本身永远不会被解码!!!

3.PasswordEncoder的内置实现

我们可以在PasswordEncoderFactories类中找到spring security中支持的编码器的完整列表。如果其中一个符合我们的要求,我们就不需要自定义自己的编码器。

  • bcrypt - BCryptPasswordEncoder
  • ldap - org. springframework. security. crypto. password. LdapShaPasswordEncoder
  • MD4 - org. springframework. security. crypto. password. Md4PasswordEncoder
  • MD5 - new MessageDigestPasswordEncoder(“MD5”)
  • noop - org. springframework. security. crypto. password. NoOpPasswordEncoder:不编码密码,保持密码为明文状态。它只能用于单元测试。
  • pbkdf2 - Pbkdf2PasswordEncoder. defaultsForSpringSecurity_v5_5()
  • pbkdf2@SpringSecurity_v5_8 - Pbkdf2PasswordEncoder. defaultsForSpringSecurity_v5_8()
  • scrypt - SCryptPasswordEncoder. defaultsForSpringSecurity_v4_1()
  • scrypt@SpringSecurity_v5_8 - SCryptPasswordEncoder. defaultsForSpringSecurity_v5_8()
  • SHA-1 - new MessageDigestPasswordEncoder(“SHA-1”)
  • SHA-256 - new MessageDigestPasswordEncoder(“SHA-256”)
  • sha256 - org. springframework. security. crypto. password. StandardPasswordEncoder
  • argon2 - Argon2PasswordEncoder. defaultsForSpringSecurity_v5_2()
  • argon2@SpringSecurity_v5_8 - Argon2PasswordEncoder. defaultsForSpringSecurity_v5_8()

在上述的编码器中,建议使用bcrypt , pbkdf2 或scrypt 。

4.小结

本章以(2)Spring Security - 了解UserDetailsService的代码为基础,通过调试代码,简单学习了spring security的认证流程和密码的验证流程。

以后如果需要用到自定义的密码编码器,将另外写一篇学习笔记。


http://www.ppmy.cn/server/150270.html

相关文章

VUE版本大模型智能语音交互

纯前端页面实现的智能语音实时听写、大模型答复、语音实时合成功能。 <template><div class"Model-container" style"padding: 10px;margin-bottom:50px; "><!--聊天窗口开始 --><el-row><el-col :span"24"><d…

Docker构建centos容器Dockerfile中使用yum命令报错问题

报错&#xff1a; ERROR: failed to solve: process "/bin/sh -c sudo yum -y install httpd" did not complete successfully: exit code: 127错误原因排查 网络原因 所操作的服务器无法访问互联网&#xff0c;可以尝试ping下公网&#xff0c;比如 ping www.baidu…

supervisor使用详解

参考文章&#xff1a; Supervisor使用详解 Supervisor 是一个用 Python 编写的客户端/服务器系统&#xff0c;它允许用户在类 UNIX 操作系统&#xff08;如 Linux&#xff09;上监控和控制进程。Supervisor 并不是一个分布式调度框架&#xff0c;而是一个进程管理工具&#x…

微服务网关SpringCloudGateway、Kong比较

网关产品 1. Spring Cloud Gateway 基本信息 Spring Cloud Gateway是Spring Cloud生态系统中的一个组件&#xff0c;基于Spring 5、Project Reactor和Spring Boot 2构建。它旨在为微服务架构提供一种简单而有效的API网关解决方案。 功能特点 路由功能强大&#xff1a;使用Rou…

EIP1967可升级合约详解

可升级代理合约方案&#xff1a;用户访问proxy合约&#xff0c;实际方法由logic合约实现。数据存储在proxy合约中 部署Proxy示例地址&#xff1a;https://testnet.bscscan.com/address/0xcb301306aa03115d40052eec804cc7458d03f1c2 // SPDX-License-Identifier: MIT pragma so…

QT笔记- QSystemTrayIcon系统托盘功能完整示例

1. 创建托盘对象 // 创建托盘图标QSystemTrayIcon * trayIcon new QSystemTrayIcon(this);QIcon icon("://icon/test.png");trayIcon->setIcon(icon);trayIcon->show();trayIcon->connect(trayIcon, &QSystemTrayIcon::activated,this, &MainWindo…

快速上手Neo4j图关系数据库

参考视频&#xff1a; 【IT老齐589】快速上手Neo4j网状关系图库 1 Neo4j简介 Neo4j是一个图数据库&#xff0c;是知识图谱的基础 在Neo4j中&#xff0c;数据的基本构建块包括&#xff1a; 节点(Nodes)关系(Relationships)属性(Properties)标签(Labels) 1.1 节点(Nodes) 节点…

抖音后端实习一面总结

置之死地而后生 抖音后端开发实习一面 自我介绍 你参加了PAT比赛&#xff1f;介绍一下&#xff1f; 平时有刷题吗&#xff1f;有的&#xff0c;那来做一下算法题目吧&#xff0c;单词拆分&#xff08;动态规划1h过去了...&#xff09; TCP有哪些状态&#xff1f;每种状态代表…