springSecurity系列:PasswordEncoder解读

springSecurity系列

最近在看 spring-security 的官方英文参考文档,过程中记录了笔记和自己的理解。持续更新中。。。

1 官方英文参考文档的优缺点

1.1 优点

对了,网上其实有很多资料,并且我也看过其他的针对spring security的相对全面的教程,那我为什么还要看官方文档呢?我解释下我的想法。

  1. 官方文档绝对是最权威、最正确、最可信的,并且是原汁原味的,完全表达了spring security作者的想法。

  2. 其他资料,或者是自己实际操作的总结、感悟、troubleshooting等,或者也是看了官方文档翻译为中文,可能会加入一些自己的理解,这些资料的缺点是:不一定全面,不一定正确。
    比如我之前看的教程,是一个连载的系列,作者随时遇到问题,随时记录下来写成文章发到网上,每篇文章都是一个单独的主题,缺少连贯性,也不全面。

  3. 我的英文还算可以,阅读英文基本上不吃力。
    其实我看了好几本英文原版的编程书籍了。当然,如果英文不好的小伙伴,我不建议看英文原版材料,因为可能会非常浪费时间,即使使用翻译软件翻译为中文,也不能完全翻译正确,可能还会造成误导,如果遇到译文语句不通顺,还要自己查字典,重新理解原文,也是很费时间的。总之一句话,效率太低。
    我的建议是,找一本全面介绍spring security的书(中文版)来看。因为一本书,一般来说知识点是非常全面的,而且能出版,说明书中的内容应该是非常准确的。当然,可以的话,尽量找评价比较高的,很多人推荐的。

1.2 缺点

尽管如此,官方文档还是有他的缺陷:

  1. 它只是一份参考文档,并非严格意义上教程,也就是说,该文档并没有根据学习的习惯来书写,一步步地引导、深入,另外有时候还需要查一些其他资料,所以学起来会有一点费劲。

  2. 对于英文不好的伙伴不友好。

2 功能(Features)

原文:Spring Security provides comprehensive support for authentication, authorization, and protection against common exploits. It also provides integration with other libraries to simplify its usage.
也就是说,spring-securtiy为 认证、授权和针对常见漏洞的保护 提供了全面的支持,也提供了和其他库的整合来简化其使用。

2.1 认证-密码存储(Authentication-Password Storage)

2.1.1 背景

原文:Prior to Spring Security 5.0 the default PasswordEncoder was NoOpPasswordEncoder which required plain text passwords. Based upon the Password History section you might expect that the default PasswordEncoder is now something like BCryptPasswordEncoder. However, this ignores three real world problems:

  1. There are many applications using old password encodings that cannot easily migrate
  2. The best practice for password storage will change again
  3. As a framework Spring Security cannot make breaking changes frequently

Instead Spring Security introduces DelegatingPasswordEncoder which solves all of the problems by:

  1. Ensuring that passwords are encoded using the current password storage recommendations
  2. Allowing for validating passwords in modern and legacy formats
  3. Allowing for upgrading the encoding in the future

其中,PasswordEncoder 接口是根接口,它规定了密码相关的3个功能(方法):编码、对比(验证密码)、判断是否需要用更安全的算法来加密密码。
我们一般只要看前面2个方法就行。

在5.0版本之前,默认使用的的 PasswordEncoder 的 实现类为 NoOpPasswordEncoder,该类中,密码直接是明文裸奔的。但这肯定不行啊,5.0之后,就默认使用 BCryptPasswordEncoder 了,它是对密码进行编码加密的。

但是直接使用 NoOpPasswordEncoder 或 BCryptPasswordEncoder 是有问题的:一是无法旧程序迁移到更好的编码算法比较麻烦;二是将来有更好的算法需要迁移,也挺麻烦;三是作为一个框架,spring security不能频繁地变化。
所以,就引入了 DelegatingPasswordEncoder,解决了上述所有问题。这是一个代理类,也是实现了 PasswordEncoder 。他的作用:一是保证密码被当前推荐的密码存储方案编码;二是允许以当前格式和遗留格式来校验密码;三是允许以后升级编码算法。

2.1.2 创建PasswordEncoder

  1. 创建一个默认的 PasswordEncoder
    其内部真正创建的实现类是 DelegatingPasswordEncoder,内部包含了BCryptPasswordEncoder作为真正的处理密码相关功能的类。

    • 创建方法
    PasswordEncoder passwordEncoder =  PasswordEncoderFactories.createDelegatingPasswordEncoder();
    
    • 设计模式分析:
      1. PasswordEncoderFactories 并不是一个设计模式,它最多只能算一个简单工厂,包含一个静态方法,简单的创建一个对象。
      2. DelegatingPasswordEncoder ,该类同样实现了 PasswordEncoder 接口,并且该类使用了装饰者模式。
        DelegatingPasswordEncoder 有一个 成员变量 private final PasswordEncoder passwordEncoderForEncode;,存放 真正干活的类,比如 BCryptPasswordEncoder,当调用encode()方法时,该方法的内部会附加一些功能,然后调用 BCryptPasswordEncoder对象的encode()方法。

2.1.3 密码存储格式

上面提到了 DelegatingPasswordEncoder 的 encode()方法。我们来分析下他的源码,就知道怎么密码是以什么格式存储的了。
源码如下:

    // 其他代码

    private static final String PREFIX = "{";
    private static final String SUFFIX = "}";
    // idForEncode的值的例子: bcrypt,ldap,MD4,MD5,noop,pbkdf2,scrypt,SHA-1,SHA-256,sha256,argon2
    private final String idForEncode;

    // 其他代码

    @Override
    public String encode(CharSequence rawPassword) {
        // 假设idForEncode值为 bcrypt,则 return的值为  {bcrypt}密码经过编码后的密文
        // 假设密码经过编码后的密文为 $2a$10$X5wFBtLrL/kHcmrOGGTrGufsBX8CJ0WpQpF3pgeuxBB/H73BK1DW6,
        //   则 return的值为  {bcrypt}$2a$10$X5wFBtLrL/kHcmrOGGTrGufsBX8CJ0WpQpF3pgeuxBB/H73BK1DW6
        return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword);
    }
posted @ 2022-04-26 16:33  mediocrep  阅读(378)  评论(0编辑  收藏  举报
既然选择了远方,便只顾风雨兼程!