【学习笔记】Spring Security 01 认识Spring Security的重要特征(Features)

Spring Security

零、概述

Spring Security(简称SS)是一个高可用的、可自定义的身份认证和鉴权控制的框架。

类似的框架还有Shiro。

需求场景:

现今流行的web开发中,安全的第一位

原本的鉴权开发流程:springweb自带的过滤器、拦截器等等。涉及到的方面

  • 功能权限
  • 访问权限
  • 菜单权限

使用过滤器需要大量的原生代码——冗余。

所以为了解决这些问题,就需要框架来帮助我们实现。

SS 官网地址以及官方文档

官方地址spring.io/projects/spring-security

官方文档阅读地址:https://docs.spring.io/spring-security/reference/servlet/authentication/index.html#servlet-authentication

image-20231007185421368

学习地址:kuangshenshuo 的B站视频

【【狂神说Java】SpringBoot整合SpringSecurity】https://www.bilibili.com/video/BV1KE411i7bC?vd_source=939c126663135132623f2393e41d7a8a

一、Spring Security快速开始

Spring Security使用的是面向切面编程的思想,也就是说不需要再刻意改动业务逻辑代码,只需要简单的配置,就可以快速接入使用。

1.1 Maven引入SS

直接上pom文件,推荐结合Springboot使用

<dependencies><!-- ... other dependency elements ... --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
</dependencies>

属性参数

<properties><!-- ... --><spring-security.version>6.1.4</spring-security.version><spring.version>6.0.11</spring.version>
</properties>

其他引入方式查看官网:https://docs.spring.io/spring-security/reference/getting-spring-security.html

官方提供的示例:

The completed application can be found in our samples repository. For your convenience, you can download a minimal Reactive Spring Boot + Spring Security application by [clicking here](https://start.spring.io/starter.zip?type=maven-project&language=java&packaging=jar&jvmVersion=1.8&groupId=example&artifactId=hello-security&name=hello-security&description=Hello Security&packageName=example.hello-security&dependencies=webflux,security).

完整的应用程序可以在我们的示例存储库中找到。 为了您的方便,您可以通过单击此处下载最小的反应式 Spring Boot + Spring 安全性应用程序。

1.2 Hello Web Security

二、认识Spring Security的重要特征(Features)

2.1 鉴权认证(Authentication)和 密码存储(Password Strorage)

SS提供了全面的身份验证,可以验证任何尝试访问特定资源的人员身份。一般指当用户输入账户密码后进行验证,验证身份后执行授权。

SS提供了单向转换的密码安全存储功能(无法提供双向密码验证),通常,使用PasswordEncoder存储需要在身份验证时与用户提供的密码比较的密码。

2.1.1 密码存储历史

了解即可。

小结密码的发展史如下:

明文——单向哈希——盐+哈希——自适应函数——长期凭证+短期凭据(比如bcrypt密码加密配合token令牌)

多年来,存储密码的标准机制已经发展。 最初,密码以明文形式存储。 密码被认为是安全的,因为数据存储密码保存在访问它所需的凭据中。 但是,恶意用户能够通过使用SQL注入等攻击找到获取用户名和密码的大型“数据转储”的方法。 随着越来越多的用户凭据公开,安全专家意识到我们需要做更多的事情来保护用户的密码。

然后鼓励开发人员在通过单向哈希(例如SHA-256)运行密码后存储密码。 当用户尝试进行身份验证时,哈希密码将与他们键入的密码的哈希进行比较。 这意味着系统只需要存储密码的单向哈希。 如果发生违规,则仅公开密码的单向哈希。 由于哈希是单向的,并且在计算上很难猜测给定哈希的密码,因此不值得努力找出系统中的每个密码。 为了击败这个新系统,恶意用户决定创建称为彩虹表的查找表( Rainbow Tables)。 他们不是每次都猜测每个密码,而是计算一次密码并将其存储在查找表中。

为了降低彩虹表的有效性,鼓励开发人员使用加盐密码。 将为每个用户的密码生成随机字节(称为盐),而不是仅使用密码作为哈希函数的输入。 盐和用户的密码将通过哈希函数运行以生成唯一的哈希。 盐将以明文形式与用户密码一起存储。 然后,当用户尝试进行身份验证时,哈希密码将与存储的盐的哈希值和他们键入的密码进行比较。 独特的盐意味着彩虹表不再有效,因为每个盐和密码组合的哈希值都不同。

在现代,我们意识到加密哈希(如SHA-256)不再安全。 原因是使用现代硬件,我们可以每秒执行数十亿次哈希计算。 这意味着我们可以轻松地单独破解每个密码

现在鼓励开发人员利用自适应单向函数来存储密码。 使用自适应单向函数验证密码是有意占用大量资源的(它们有意使用大量 CPU、内存或其他资源)。 自适应单向功能允许配置一个“工作因子”,该因子可以随着硬件的改进而增长。 我们建议将“工作因子”调整为大约需要一秒钟来验证系统上的密码。 这种权衡是使攻击者难以破解密码,但又不会太昂贵,以免给您自己的系统带来过多的负担或激怒用户。

Spring Security 试图为“工作因素”提供一个良好的起点,但我们鼓励用户为自己的系统自定义“工作因素”,因为性能因系统而异。 应该使用的自适应单向函数的示例包括 bcrypt, PBKDF2, scrypt 以及 argon2。

由于自适应单向函数有意占用大量资源,因此验证每个请求的用户名和密码可能会显著降低应用程序的性能。 Spring 安全性(或任何其他库)无法加快密码验证的速度,因为安全性是通过使验证资源密集来获得的。 建议用户将长期凭据(即用户名和密码)交换为短期凭据(例如会话和 OAuth 令牌等)。 可以快速验证短期凭据,而不会造成任何安全性损失。

2.1.2 DelegatingPasswordEncoder委派密码编码器

SS提供了自己的解决方案

在 Spring Security 5.0 之前,默认的 PasswordEncoderNoOpPasswordEncoder(需要纯文本密码)。

相当于“密码存储历史”部分的 “BCryptPasswordEncoder”。 但是,这忽略了三个现实世界的问题:

  • 许多应用程序使用无法轻松迁移(easily migrate)的旧密码。
  • 密码存储的最佳做法永远都在更新。
  • 作为一个框架,Spring Security不能经常进行重大更改。

为了解决这些问题,Spring Security 引入了 “DelegatingPasswordEncoder”,它通过以下方式解决了所有问题:

  • 确保使用当前密码存储建议对密码进行编码
  • 允许验证现代和传统格式的密码
  • 允许未来升级编码方式

以下是官方提供的案例https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html,只做了简单的搬运

您可以使用PasswordEncoderFactories方法轻松构建一个DelegatingPasswordEncoder 的实例:

  1. 创建一个默认的委派密码编码器

    PasswordEncoder passwordEncoder =PasswordEncoderFactories.createDelegatingPasswordEncoder();
    
  2. 或者,可以创建一个定义的委派密码编码器

    String idForEncode = "bcrypt";
    Map encoders = new HashMap<>();
    encoders.put(idForEncode, new BCryptPasswordEncoder());
    encoders.put("noop", NoOpPasswordEncoder.getInstance());
    encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
    encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
    encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
    encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
    encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
    encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
    encoders.put("sha256", new StandardPasswordEncoder());PasswordEncoder passwordEncoder =new DelegatingPasswordEncoder(idForEncode, encoders);
    

2.1.3 SS的密码存储格式

密码一般的存储格式如下(委派密码编码器的存储格式:

{id}encodedPassword
  • id是用于查找"PasswordEncoder"应使用的标识符

  • encodedPassword是所"PasswordEncoder"的原始编码密码。

  • id必须位于密码的开头,以 开头{,以 结尾}

  • 如果没有找到id,意味着id被设置为空

以下具体举例,所有的原始密码都是“password”:

{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0

显而易见,**密码前段的大括号{}作为标识符,存储的该密码的加密方式。**决定"PasswordEncoder"使用哪个来编码密码。

Note:一些用户可能担心存储格式是为潜在的黑客提供的。这不是一个问题,因为密码的存储不依赖于算法的秘密。此外,大多数格式在没有前缀的情况下很容易被攻击者识别出来。例如,BCrypt 密码通常以" 2 a 2a 2a"。

2.1.4 密码匹配问题

对应的,密码匹配问题也是根据大括号中的``id来决定使用哪种PasswordEncoder`进行匹配的。

默认情况下,执行方法matches(CharSequence, String)校验密码以及调用未映射的id(包括 null id)的结果为产生异常IllegalArgumentException

可以使用以下方法进行定制化:

DelegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(PasswordEncoder)

通过使用"id",我们可以匹配任何密码编码,甚至使用最现代的密码编码对密码进行编码。

这很重要,因为与加密不同,密码哈希的设计使得没有简单的方法可以恢复明文。由于无法恢复明文,因此很难迁移密码。为此我们选择了默认包含方便用户迁移的编码方式NoOpPasswordEncoder"(根据上面的案例可以发现,noop是就是明文),以简化入门体验

image-20231007202303764

2.1.5 快速体验SS的委派密码

如果您正在制作演示或样本,那么花时间对用户的密码进行哈希处理会有点麻烦。有一些便利的机制可以使这变得更容易,但这仍然不适合生产。

案例1:默认的密码编码案例(bcrypt)

UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("user").build();
System.out.println(user.getPassword());
// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG

如果需要创建多个用户,也是可以复用这个构建器(不需要创建新的实例)

// 只创建一个UserBuilder
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();

尽管这已经对存储的密码进行哈希处理,但密码仍然暴露在内存和编译的源代码中。

因此,对于生产环境来说,它仍然不被认为是安全的。对于生产,您应该在外部对密码进行哈希处理(hash your passwords externally),原文推荐使用SpringBoot CLI。

2.1.6 常用故障排查

2.1.6.1 IllegalArgumentException

当存储的密码之一没有 id时,会发生以下错误

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

解决方法

解决此问题的最简单方法是弄清楚您的密码当前是如何存储的,并明确提供正确的PasswordEncoder

  • 可以通过公开密码(noop)恢复到之前的加密方式。(个人理解就是跳过ss的用户加密,只套一个{noop}的壳

  • 给所有的密码上加上正确的前缀(已知加密方式的情况下)

    从
    $2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
    改成
    {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
    

详细的映射列表参考: PasswordEncoderFactories

2.1.7 常见的案例

简单总结一下:使用的方法大致是以下几个方法

  • 创建一个对应的编码器
  • 使用编码器的encode(String)方法得到SS的加密结果
  • 使用编码器的matches(原密码,加密后的密码)方法进行密码匹配
2.1.7.1 BCryptPasswordEncoder

广泛支持的 bcrypt 算法对密码进行哈希处理。 为了使其更能抵抗密码破解,bcrypt故意变慢。 与其他自适应单向函数一样,应将其调整为大约需要 1 秒来验证系统上的密码。

推荐:根据系统硬件水平测试调整,设置密码强度使得校验密码需要约1秒。

// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
2.1.7.2 Argon2PasswordEncoder

Argon2是密码哈希竞赛的获胜者。 为了防止自定义硬件上的密码破解,Argon2 是一种故意缓慢的算法,需要大量内存。 与其他自适应单向函数一样,应将其调整为大约需要 1 秒来验证系统上的密码。

// 创建一个全默认的编码器
Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
2.1.7.3 其他PasswrodEncoder

还有大量其他 “PasswordEncoder” 实现完全是为了向后兼容而存在的。

它们都已弃用,以指示它们不再被视为安全。 但是,没有计划删除它们,因为很难迁移现有的遗留系统。

2.1.7.4 密码存储配置

SS默认使用委派密码编码器。

如果要使用原来的密码编码方式,就使用无操作密码编译器即可:

@Bean
public static NoOpPasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();
}
// 声明一个"NoOpPasswordEncoder"的bean 名称为"passwordEncoder".

2.1.8 修改密码配置

大多数应用不仅用户提交密码,还必须支持修改密码。

正常我们需要提供一个端口给管理员进行密码管理,你可以通过配置SS来提供该端口,比如原来的应用中修改密码的端口是/change-password,你可以像这样配置SS:

http.passwordManagement(Customizer.withDefaults())

如果不是/change-password,也可以像这样配置

http.passwordManagement((management) -> management.changePasswordPage("/update-password"))

具体用法如下:

要使这段代码生效,您需要在Spring Security的配置文件中进行相应的配置。具体步骤如下:

  1. 打开Spring Security的配置文件(通常是SecurityConfig.javaSecurityConfig.kt)。

  2. 在配置文件中找到适当的位置,添加以下代码片段:

    http.passwordManagement(Customizer.withDefaults());
    

    或者,如果您使用XML配置:

    <http><password-management customizer-ref="org.springframework.security.config.Customizer#withDefaults" />
    </http>
    
  3. 如果您的应用程序中的密码更改端点是/change-password,则无需进行其他配置。否则,您可以使用.changePassword().changePasswordUrl("/your-change-password-url")方法来指定自定义的密码更改端点URL。

  4. 保存并关闭配置文件。

这样,当您的应用程序启动时,Spring Security将根据您的配置自动启用密码管理功能,并提供一个标准的密码更改URL供密码管理器使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/104496.shtml

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

相关文章

【ROS】使用vscode浏览navigation2源码时,提示:没有那个文件或目录

1、问题描述 使用vscode浏览navigation2源码时,头文件下面有波浪线,并提示:没有那个文件或目录。比如没有:geometry_msgs/msg/polygon.h 错误信息: 无法打开源文件 “geometry_msgs/msg/polygon.h” (dependency of “nav2_controller/controller_server.hpp”)C/C++(16…

【C++】异常处理之throw、catch、try、局部资源管理、标准异常库

一、抛出异常 异常处理机制两个主要成分&#xff1a; 异常的鉴定与发出&#xff1b;异常的处理方式。 C通过throw表达式产生异常&#xff1a; inline void Triangular_iterator:: check_integrity() {if(_index>Triangular::max_elems){throw iterator_overflow(_index,…

解决react使用css module无法重写bootstrap样式的问题

react使用css module虽然能够解决样式污染&#xff0c;但是同时也失去了写css样式的灵活性&#xff0c;特别是&#xff1a;在.module.css文件中当子元素是非变量的静态class类&#xff08;比如bootstrap&#xff09;, 此时使用css选择器对该子元素的样式不会起作用的 比如下面…

什么是API网关?——驱动数字化转型的“隐形冠军”

什么是API网关 API网关&#xff08;API Gateway&#xff09;是一个服务器&#xff0c;位于应用程序和后端服务之间&#xff0c;提供了一种集中式的方式来管理API的访问。它是系统的入口点&#xff0c;负责接收并处理来自客户端的请求&#xff0c;然后将请求路由到相应的后端服…

为什么Excel插入图片不显示,点击能够显示

今天在做Excel表格时&#xff0c;发现上传图片后不能显示&#xff0c;但是点击还是能够出现图片的 点击如下 点击能看到&#xff0c;但是不显示&#xff1f;那么只需鼠标右键点击浮动即可显示

CSS 滚动驱动动画 animation-range

animation-range 语法 normallength-percentagetimeline-range-name 具名时间线范围 named timeline rangecovercontainentry 和 entry-crossingexit 和 exit-crossing 兼容性 animation-range 这个属性可同时对 scroll progress timeline 和 view progress timeline 这两种不…

了解华为交换机路由器的基本命令

什么是CLI&#xff1a;使用户与设备交互的界面&#xff0c;用户输入对应的命令&#xff0c;设备会回复我们输入的内容&#xff0c;回车车后设备会执行对应命令&#xff0c;达到管理、配置、查看的目的。 CLI界面分为三种操作视图&#xff1a; 用户试图&#xff1a;设备登陆后…

【java|golang】多字段排序以及排序规则

奖励最顶尖的 K 名学生 给你两个字符串数组 positive_feedback 和 negative_feedback &#xff0c;分别包含表示正面的和负面的词汇。不会 有单词同时是正面的和负面的。 一开始&#xff0c;每位学生分数为 0 。每个正面的单词会给学生的分数 加 3 分&#xff0c;每个负面的词…

selenium打开火狐浏览器

项目上需求为&#xff1a;甲方OA 系统是IE系统&#xff0c;需要从IE系统点个按钮打开火狐浏览器单点登录跳转到我们的系统 前期解决方案为&#xff1a;打开浏览器就行了&#xff0c;然后就用的是打开本地浏览器&#xff0c;但是由于B/S架构&#xff0c;有别人远程访问我的ip来…

如何使用ChatPPT生成PPT文档

简介 ChatPPT是一个基于人工智能的PPT生成工具&#xff0c;可以帮助用户快速生成高质量的PPT文档。ChatPPT使用自然语言处理技术&#xff0c;可以根据用户的指令生成PPT内容、设计和排版。 使用方法 ChatPPT提供了两种使用方式&#xff1a;在线体验版和Office插件版。 在线…

【博客搭建】1、拾壹博客本地启动遇到的问题和需要注意的坑

一、后端&#xff08;blog&#xff09;启动 1、修改application.yml中的数据库链接与密码&#xff0c;Redis账号密码&#xff0c;即可启动成功&#xff1b; 2、运行之前先导入sql&#xff1b; 3、 如需上传文件保存至本地&#xff08;例如相册的照片&#xff09;&#xff0c;需…

【竞赛题目】木块(C语言详解)

" 木块 " 是【第二届全国高校计算机技能竞赛】里的一道竞赛题目&#xff0c;博主觉得很新颖想推荐给大家&#xff1b; 题目描述 为了提高词汇量&#xff0c;小理得到了一套四块木块&#xff0c;其中每块都是一个立方体&#xff0c;六面各写着一个字母。他正在通过将…

神经网络中卷积和池化的区别

1、什么叫卷积&#xff1f; 卷积层是用一个固定大小的矩形区去席卷原始数据&#xff0c;将原始数据分成一个个和卷积核大小相同的小块&#xff0c;然后将这些小块和卷积核相乘输出一个卷积值&#xff08;注意这里是一个单独的值&#xff0c;不再是矩阵了&#xff09;。 卷积的…

YOLOv5算法改进(11)— 主干网络介绍(MobileNetV3、ShuffleNetV2和GhostNet)

前言:Hello大家好,我是小哥谈。主干网络通常指的是深度学习中的主干模型,通常由多个卷积层和池化层组成,用于提取输入数据的特征。在训练过程中,主干网络的参数会被不断优化以提高模型的准确性。YOLOv5算法中的主干网络可以有多种替换方案,为了后面讲解的方便,本篇文章就…

CVE-2017-12615 Tomcat远程命令执行漏洞

漏洞简介 2017年9月19日&#xff0c;Apache Tomcat官方确认并修复了两个高危漏洞&#xff0c;漏洞CVE编号&#xff1a;CVE-2017-12615和CVE-2017-12616&#xff0c;其中 远程代码执行漏洞&#xff08;CVE-2017-12615&#xff09; 当 Tomcat 运行在 Windows 主机上&#xff0c;…

加持智慧医疗,美格智能5G数传+智能模组让就医触手可及

智慧医疗将云计算、物联网、大数据、AI等新兴技术融合赋能医疗健康领域&#xff0c;是提高医疗健康服务的资源利用效率&#xff0c;创造高质量健康医疗的新途径。《健康中国2030规划纲要》把医疗健康提升到了国家战略层面&#xff0c;之后《“十四五”全面医疗保障规划》等一系…

时间复杂度O(40n*n)的C++算法:修改图中的边权

本篇源码下载 点击下载 1.12.1. 题目 给你一个 n 个节点的 无向带权连通 图&#xff0c;节点编号为 0 到 n - 1 &#xff0c;再给你一个整数数组 edges &#xff0c;其中 edges[i] [ai, bi, wi] 表示节点 ai 和 bi 之间有一条边权为 wi 的边。 部分边的边权为 -1&#xff08…

CANdb++数据库操作

CANdb数据库操作 创建工程结构文件夹新建数据库&总线描述节点设置节点创建配置Message属性信号设置节点收发信号 环境变量配置一致性检验数据库工程XVehicle.dbc导入工程文件总结 创建工程结构文件夹 在文件夹X-Vehicle-1下&#xff0c;建立工程目录文件夹CANdb&#xff0…

CustomNavBar 自定义导航栏视图

1. 创建偏好设置键 CustomNavBarTitlePreferenceKey.swift import Foundation import SwiftUI//State private var showBackButton: Bool true //State private var title: String "Title" //"" //State private var subtitle: String? "SubTitl…

相似性搜索:第 1 部分- kNN 和倒置文件索引

图片来源&#xff1a;维亚切斯拉夫叶菲莫夫 一、说明 SImilarity 搜索是一个问题&#xff0c;给定一个查询的目标是在所有数据库文档中找到与其最相似的文档。 在数据科学中&#xff0c;相似性搜索经常出现在NLP领域&#xff0c;搜索引擎或推荐系统中&#xff0c;其中需要检索最…