Spring Security 基础介绍

以下内容学习并翻译自Spring Security 5.3.2官方文档,为了方便日后回顾以此博客摘要记录

特点

Spring Security 为身份验证、授权和针对常见的漏洞防护提供了全面的支持。 它还提供与其他第三方库的集成,以简化其使用。

身份认证

身份验证是我们验证特定资源访问者的身份的方法。 验证用户身份的常用方法是要求用户输入用户名和密码。 一旦执行了身份验证,我们就会是谁在访问特定资源并可以执行授权。

密码的安全存储

Spring Security 的PasswordEncoder接口用于对密码进行不可逆的转换。一般情况下都把转换后的密文存储到数据库中,而不是在数据库中存储明文密码,这样大大提高了密码的安全性。

密码存储的发展历史

最初,密码以纯文本格式存储。假定密码是安全的,因为数据存储密码已保存在访问它所需的凭据中。但是,恶意用户能够使用 SQL 注入攻击的方法来获取用户名和密码。随着越来越多的用户的安全意识的加强,我们意识到我们需要做更多的事情来保护用户的密码。

后来鼓励开发人员将密码通过诸如 SHA-256 之类的单向散列后得出的密文进行存储。当用户尝试进行身份验证时,会将散列后的密文与他们键入的密码的散列进行比较。这意味着系统仅需要存储密码的单向哈希。如果发生违规,则仅暴露密码的一种哈希方式。由于散列是一种方式,计算出给定哈希值的密码很难计算,因此找出系统中的每个密码都不值得。为了击败这个新系统,恶意用户决定创建称为Rainbow Tables 的查找表。他们不必每次都猜测每个密码,而是只计算一次密码并将其存储在查找表中。

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

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

现在鼓励开发人员利用自适应单向函数来存储密码。具有自适应单向函数的密码验证有意消耗大量资源(即CPU,内存等)。自适应单向函数允许配置“工作因数”,该因数会随着硬件的改进而增加。建议将“工作因数”调整为大约1秒钟,以验证系统上的密码。这种做法使攻击者难以破解密码,但又不会给您自己的系统带来过重的负担。 Spring Security 试图为“工作因素”提供一个良好的起点,但是鼓励用户为自己的系统自定义“工作因素”,因为不同系统之间的性能会有很大差异。应使用的自适应单向函数示例包括 bcrypt,PBKDF2,scrypt 和 argon2。

由于自适应单向函数有意占用大量资源,因此为每个请求验证用户名和密码都会大大降低应用程序的性能。 Spring Security(或任何其他库)无法采取任何措施来加快密码的验证速度,因为通过增加验证资源的强度来获得安全性。鼓励用户将长期凭证(即用户名和密码)转换为短期凭证(即会话,OAuth 令牌等)。可以快速验证短期凭证,而不会损失任何安全性。

DelegatingPasswordEncoder

在 Spring Security 5.0之前,默认的密码编码器是NoOpPasswordEncoder,它需要纯文本密码。为安全起见,您现在可能希望默认的密码编码器类似于BCryptPasswordEncoder。为了方便升级又不影响Spring Security框架的结构,5.0版本之后,Spring Security 引入了DelegatingPasswordEncoder 的概念。虽然DelegatingPasswordEncoder 实现了PasswordEncoder接口,但事实上它并不是传统意义上的编码器,它并不使用某一特定算法进行编码,顾名思义,它是一个委派密码编码器,它将具体编码的实现根据要求委派给不同的算法。

您可以使用PasswordEncoderFactories轻松地构造一个passwordencoder实例。

// 创建默认的DelegatingPasswordEncoder
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
// 创建自定义的DelegatingPasswordEncoder
String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());
PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
密码存储的格式

DelegatingPasswordEncoder 存储的密码格式是:{id}encodedPassword
id:是所选择使用编码器的标识
encodedPassword:是使用该编码器将原始密码进行编码后的字符串

id一定在最前面且被{}包裹
有些人可能认为把密码的编码类型标识出来会有潜在的风险,但其实密码的存储并不依赖于算法的机密性。此外,攻击者很容易在没有前缀的情况下发现大多数格式。例如,BCrypt 密码通常以$2a$开头。

下面是字符串"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 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值