(3)spring security - 认识PasswordEncoder

news2024/12/15 15:38:09

目录

  • 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)函数中,尝试从请求中获取用户名和密码。

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

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

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

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

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

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

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函数:

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		//......
		//获取你在登录表单中输入的用户名
		String username = determineUsername(authentication);
		//......
		//查询缓存中是否有匹配的用户信息,实际user为null
		UserDetails 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.密码验证

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

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

additionalAuthenticationChecks的源代码是:

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

其中的matches函数源码如下:

	@Override
	public 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;
		}
		//判断是否是BCrypt
		if (!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.coloradmin.cn/o/2260001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

29.在Vue 3中使用OpenLayers读取WKB数据并显示图形

在Web开发中,地理信息系统(GIS)应用越来越重要,尤其是在地图展示和空间数据分析的场景中。OpenLayers作为一个强大的开源JavaScript库,为开发者提供了丰富的地图展示和空间数据处理能力。在本篇文章中,我将…

LLM大语言模型私有化部署-OpenEuler22.03SP3上容器化部署Dify与Qwen2.5

背景 Dify 是一款开源的大语言模型(LLM) 应用开发平台。其直观的界面结合了 AI 工作流、 RAG 管道、 Agent 、模型管理、可观测性功能等,让您可以快速从原型到生产。相比 LangChain 这类有着锤子、钉子的工具箱开发库, Dify 提供了更接近生产需要的完整…

革新3D高保真数字人生成:无需深度摄像头,普通手机视频即可创建逼真面部动画

在数字化内容创作领域,特别是虚拟人物和增强现实(AR)应用中,高质量的3D数字人生成正变得越来越重要。然而,传统方法依赖于昂贵的深度摄像头和复杂的设备设置,这不仅增加了成本,也限制了其灵活性和易用性。为了解决这些问题,并降低进入门槛,一款基于MetaHuman的插件应运…

【前端 Uniapp】使用Vant打造Uniapp项目(避坑版)

一、基本介绍 Uniapp 是基于 Vue.js 的开发框架,通过一套代码可以同时发布到多个平台的应用框架。而 Vant 是针对移动端 Vue.js 的组件库。通过这样的组合,我们可以快速构建出一个跨平台的移动应用。Vant 已经支持多种小程序和 H5 平台,也对…

【记录】Django解决与VUE跨域问题

1 梗概 这里记录Django与VUE的跨域问题解决方法,主要修改内容是在 Django 中。当然其他的前端项目 Django 也可以这样处理。 2 安装辅助包 pip install django-cors-headers3 配置 settings.py INSTALLED_APPS [ # ... corsheaders, # ... ] 为了响应…

【AI知识】激活函数介绍(sigmoid Tanh Relu)+ 梯度爆炸 / 消失及解决办法

激活函数: 使用激活函数的原因: 神经网络中每一层的输入输出都是一个线性求和的过程,下一层的输出只是承接了上一层输入函数的线性变换,如果没有激活函数,无论构造的神经网络多么复杂,有多少层,…

驱动开发-入门【1】

1.内核下载地址 Linux内核源码的官方网站为https://www.kernel.org/,可以在该网站下载最新的Linux内核源码。进入该网站之后如下图所示: 从上图可以看到多个版本的内核分支,分别为主线版本(mainline)、稳定版本&#…

3D 生成重建038-DiffGS训练一个3DGS编码器来简化训练

3D 生成重建038-DiffGS训练一个3DGS编码器来简化训练 文章目录 0 论文工作1 论文方法2 实验结果 0 论文工作 三维高斯溅射(3DGS)在渲染速度和保真度方面表现出了令人信服的性能,但由于其离散性和非结构性,高斯溅射的生成仍然是一…

【渗透测试一】信息收集

信息收集简介 定义 信息收集是渗透测试和网络安全评估等活动的初始关键阶段。它是指通过各种合法手段(如网络扫描、查询公开数据库、社会工程学等),收集与目标系统(包括网络、主机、应用程序、组织等)相关的信息&…

小迪笔记第五十一天-前后台功能点文件下载文件读取文件删除目录遍历目录穿越

前言 前后台功能点 就是因为权限的不同而造成的 功能的不同 一般这个文件的 下载 文件的读取 一般出现在前台 而人家的删除和遍历 目录的穿越出现在 后台 以这个海洋cms 为例进行 演示 (源码在后台) 后台管理地址:http://192.168…

「Mac玩转仓颉内测版50」小学奥数篇13 - 动态规划入门

本篇将通过 Python 和 Cangjie 双语介绍动态规划的基本概念,并解决一个经典问题:斐波那契数列。学生将学习如何使用动态规划优化递归计算,并掌握编程中的重要算法思想。 关键词 小学奥数Python Cangjie动态规划斐波那契数列 一、题目描述 …

phidata - 具有记忆、知识、工具和推理能力的多模态代理

Phidata 是一个用于构建多模态代理的框架,使用 phidata 可以:使用内存、知识、工具和推理构建多模式代理。建立可以协同工作解决问题的代理团队。使用漂亮的 Agent UI 与您的代理聊天。 16200 Stars 2200 Forks 28 Issues 82 贡献者 MPL-2.0 License Pyt…

第六届全球校园人工智能算法精英大赛-算法巅峰专项赛(系列文章)-- 开篇

前言 “全球校园人工智能算法精英大赛”是江苏省人工智能学会举办的面向全球具有正式学籍的全日制高等院校及以上在校学生举办的算法竞赛。其中的算法巅峰专项赛是新赛道,2024年是其第一届比赛。 翻阅过所有赛道的题目,题目出的真心可以,很具…

柚坛工具箱Uotan Toolbox适配鸿蒙,刷机体验再升级

想要探索智能设备的无限可能?Uotan Toolbox(柚坛工具箱)将是您的得力助手。这款采用C#语言打造的创新型开源工具箱,以其独特的设计理念和全面的功能支持,正在改变着用户与移动设备互动的方式。 作为一款面向专业用户的…

‘Close Project‘ is not available while IDEA is updating indexes的解决

XXX is not available while IDEA is updating indexes IDEA 1.Remove from Recent Projects 2.重新 Open工程即可

[笔记] 编译LetMeowIn(C++汇编联编程序)过程

文章目录 前言过程下载源码vs2017 创建空项目 引入编译文件改项目依赖属性改汇编编译属性该项目还需注意编译运行 总结 前言 编译LetMeowin 项目发现是个混编项目,c调用汇编的程序,需要配置一下,特此记录一下 过程 下载源码 首先下载源码…

Linux系统操作03|chmod、vim

上文: Linux系统操作02|基本命令-CSDN博客 目录 六、chmod:给文件设置权限 1、字母法 2、数字法(用的最多) 七、vim:代码编写和文本编辑 1、启动和退出 1️⃣启动 2️⃣退出 2、vim基本操作 六、chmod&#x…

SpringCloud微服务实战系列:01让SpringCloud项目在你机器上运行起来

目录 项目选型 项目安装-本地运行起来 软件安装: 项目启动: 总结&答疑 项目选型 软件开发,基本上都不会从0开始,一般都是在其他项目或者组件的基础上进行整合优化迭代,站在巨人肩膀上才能看得更远&#xff0c…

Python鼠标轨迹算法(游戏防检测)

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序,它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言,原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势: 模拟…

npm error Error: Command failed: F:\360Downloads\Software\nodejs\node.exe

前言: 电脑环境:win7 node版本:18.20.0 npm版本:10.9.2 情景再现:电脑上是存在的vuevite的项目且可以正常运行。想着摸鱼的时间复习一下ts语法,所以想创建一个demo。按照 开始 | Vite 官方中文文档 官网创建…