爆破专栏丨Spring Security系列教程之SpringSecurity中的密码加密_spring

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
img

正文

前言

截止到现在,一一哥已经带各位学习了很多关于SpringSecurity的知识点,但是Spring Security作为一个安全框架,其中必然就应该带有安全加密方面的内容,所以本篇文章,一一哥 就带各位来学习Spring Security中的密码加密机制。

需要更多教程,微信扫码即可

👆👆👆

别忘了扫码领资料哦【高清Java学习路线图

全套学习视频及配套资料****

一. 密码加密简介

1. 散列加密概述

我们开发时进行密码加密,可用的加密手段有很多,比如对称加密、非对称加密、信息摘要等。在一般的项目里,常用的就是信息摘要算法,也可以被称为散列加密函数,或者称为散列算法、哈希函数

这是一种可以从任何数据中创建数字“指纹”的方法,常用的散列函数有 MD5 消息摘要算法、安全散列算法(Secure Hash Algorithm)等

2. 散列加密原理

散列函数通过把消息或数据压缩成摘要信息,使得数据量变小,将数据的格式固定下来,然后将数据打乱混合,再重新创建成一个散列值,从而达到加密的目的。

散列值通常用一个短的随机字母和数字组成的字符串来代表,一个好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理时,如果我们不抑制冲突来区别数据,会使得数据库中的记录很难找到。

但是仅仅使用散列函数还不够,如果我们只是单纯的使用散列函数而不做特殊处理,其实是有风险的!比如在两个用户密码明文相同时,生成的密文也会相同,这样就增加了密码泄漏的风险。

所以为了增加密码的安全性,一般在密码加密过程中还需要“加盐”,而所谓的“盐”可以是一个随机数,也可以是用户名。”加盐“之后,即使密码的明文相同,用户生成的密码密文也不相同,这就可以极大的提高密码的安全性。

**传统的加盐方式需要在数据库中利用专门的字段来记录盐值,**这个字段可以是用户名字段(因为用户名唯一),也可以是一个专门记录盐值的字段,但这样的配置比较繁琐。

3. Spring Security中的密码处理方案

那么在Spring Security中,对密码是怎么进行处理的呢?其实Spring Security对密码的处理方案,有如下3种方式:

  • 对密码进行明文处理,即不采用任何加密方式;
  • 采用MD5加密方式;
  • 采用哈希算法加密方式。

4. BCryptPasswordEncoder简介

以上说的是3种密码处理方案,并不代表只有3种加密算法,这个请大家注意哦!

实际上,Spring Security提供了多种密码加密算法,但官方推荐使用的是BCrypt Password Encoder方案,如下图所示:

图片

我们开发时,用户表中的密码通常是使用MD5等不可逆算法加密后存储,但为了防止彩虹表破解,可以先使用一个特定的字符串(如域名)进行加密,然后再使用一个随机的salt(盐值)加密。其中特定的字符串是程序代码中固定的,salt是每个密码单独随机的,我们一般会给用户表加一个字段单独存储,但这样比较麻烦。

**而BCrypt算法却可以随机生成salt并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt。**不同于 Shiro 中需要自己处理密码加盐,在 Spring Security 中,BCrypt Password Encoder 本身就自带了盐,所以处理起来非常方便。

另外BCryptPasswordEncoder使用BCrypt强哈希函数,我们在使用时可以选择提供strength和SecureRandom参数。strength值(取值在4~31之间,默认为10)越大,则密钥的迭代次数就越多,密钥迭代次数为2^strength。

二. 利用BCryptPasswordEncoder进行加密

了解了这些基本的理论知识之后,壹哥 就带各位进行代码实现啦。

我们继续在之前的案例基础之上进行本案例的代码实现,所以项目创建过程略过,请参考之前的章节内容。

1. 编写register接口

为了方便测试,我们首先在UserController中编写一个register接口,用于注册一个新用户,在添加用户时对密码进行加密。

    @Autowired
    **private** PasswordEncoder passwordEncoder;

 /**
     * 添加用户.这里我们采用表单形式传参,传参形式如下:
     * http://localhost:8080/user/register?username=test&password=123
     */
    @GetMapping("/register")
    **public** User **registerUser**(@RequestParam(required = **false**) User user){
        user.setEnable(**true**);
        user.setRoles("ROLE_ADMIN");

        //对密码进行加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));

        userMapper.addUser(user);

        **return** user;
    }

别忘了注入PasswordEncoder对象!

2. 配置密码加密算法

接下来我们在Security Config配置类中,配置到底该采用哪种密码加密算法。我们在Spring Boot环境中是非常容易实现加密算法配置的,只需要创建一个Password Encoder对象即可。

@EnableWebSecurity(debug = **true**)
**public** **class** **SecurityConfig** **extends** **WebSecurityConfigurerAdapter** {

    @Override
    **protected** **void** **configure**(HttpSecurity http) **throws** Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("ADMIN")
                //放行register接口
                .antMatchers("/user/register")
                .permitAll()
                .antMatchers("/user/**")
                .hasRole("USER")
                .antMatchers("/app/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and()
                //对跨域请求伪造进行防护---->csrf:利用用户带有登录状态的cookie进行攻击的手段
                .csrf()
                .disable();
    }

    //配置采用哪种密码加密算法
    @Bean
    **public** PasswordEncoder **passwordEncoder**() {
        //不使用密码加密
        //return NoOpPasswordEncoder.getInstance();

        //使用默认的BCryptPasswordEncoder加密方案
        **return** **new** BCryptPasswordEncoder();

        //strength=10,即密钥的迭代次数(strength取值在4~31之间,默认为10)
        //return new BCryptPasswordEncoder(10);

        //利用工厂类PasswordEncoderFactories实现,工厂类内部采用的是委派密码编码方案.
        //return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}

**注意:**这里我们可以有多种创建PasswordEncoder对象的写法!并且别忘了把“/user/register”注册接口直接放行,注册接口不应该拦截。

3. 测试运行

最后把项目启动起来,测试一下/user/register接口,注册添加一个新的用户,可以看到添加成功后的用户信息返回如下。

图片

与此同时,我们的数据库中,也有了相关信息:

图片

可以看到,我们的密码已经被BCrypt Password Encoder 方案进行了加密,此时我们进行登录时,也需要采用加密的密码才能进行访问了。

4. BCryptPasswordEncoder加解密原理

我前面说过,BCrypt Password Encoder加密时,每次都会随机生成一个盐值混入到密码中,以此保证即使密码明文一样,最终得到的密文也不一样。但是这时候问题就来了,这个盐值是BCryptPasswordEncoder自动生成的,我们程序员也不知道,那到时候怎么进行密码的比对呢?因为比对密码时,肯定也需要把明文添加盐值后再加密才能比对啊!别急,往下看!

BCrypt Password Encoder调用 encode(…) 方法对密码明文加密时,每次都会随机的生成一个盐值,把这个盐值和明文再一起混淆最终得到密码的密文,所以这个最终的密文分为两部分:盐值和最终加密的结果。

BCryptPasswordEncoder调用matches(…)方法对比的时候,会利用自己特定的方法,先从密文里面拿出盐值,然后利用该盐值对密码的明文进行加密得到一个新的密文,最后利用这个新生成的密文和之前的密文进行对比,这样就能知道传递过来的密码是否和存储的密码是否一样了。

三. 利用其他Encoder进行加密实现

1. MessageDigestPasswordEncoder的用法

除了可以使用上面提到的默认的BCrypt Password Encoder加密方案之外,我们还可以使用Message Digest Password Encoder方案,该方案内部是采用"MD5"、“SHA-1”、"SHA-256"等信息摘要算法实现的加密,所以我们需要在构造的时候传入MD5等算法名称字符串。这个配置在SecurityConfig类中实现即可!

@Bean
**public** MessageDigestPasswordEncoder **messageDigestPasswordEncoder**(){
    
    **return** **new** MessageDigestPasswordEncoder("MD5");
}

配置好了Message Digest Password Encoder对象,我们就可以利用该encoder对象对密码明文,比如“123”进行加密,就会得到如下密文:

{EUjIxnT/OVlk5J54s3LaJRuQgwTchm1gduFHTqI0qjo=}4b40375c57c285cc56c7048bb114db23

利用Message Digest Password Encoder 的encode(…) 加密方法,每次都会随机生成盐值,所以对相同的明文进行多次加密,每次得到的结果是不一样的。

Message Digest Password Encoder这个加密的最终结果也是分为两部分:盐值 + MD5 (password+盐值)。那么当我们调用 matches(…) 方法对比密码的时候,也是先从密文中得到盐值,然后利用该盐值再加密明文,最后利用这个新生成的密文和之前的密文进行对比。

2. DelegatingPasswordEncoder的用法

我们还有另一种加密实现写法,就是利用Delegating Password Encoder来进行实现。

Delegating Password Encoder是Spring Security 推出的一套兼容方案,该方案会根据加密类型的id字符串(idFor Encode),去自身缓存的所有加密方式中(idTo Password Encoder)取出对应的加密方案对象,然后对明文进行加密和密文的对比。Delegating Password Encoder对象的初始化,一般是使用 Spring Security 提供的一个工厂构造方法:

 **public** **static** PasswordEncoder **createDelegatingPasswordEncoder**() {
  String encodingId = "bcrypt";
  Map<String, PasswordEncoder> encoders = **new** HashMap<>();
  encoders.put(encodingId, **new** BCryptPasswordEncoder());
  encoders.put("ldap", **new** org.springframework.security.crypto.password.LdapShaPasswordEncoder());
  encoders.put("MD4", **new** org.springframework.security.crypto.password.Md4PasswordEncoder());
  encoders.put("MD5", **new** org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
  encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
  encoders.put("pbkdf2", **new** Pbkdf2PasswordEncoder());
  encoders.put("scrypt", **new** SCryptPasswordEncoder());
  encoders.put("SHA-1", **new** org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
  encoders.put("SHA-256", **new** org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
  encoders.put("sha256", **new** org.springframework.security.crypto.password.StandardPasswordEncoder());
  encoders.put("argon2", **new** Argon2PasswordEncoder());

  **return** **new** DelegatingPasswordEncoder(encodingId, encoders);
 }

这个工厂的静态构造方法把常用的几种密码方案都注入到了缓存Map中,默认注入的 encodingId 对应的是 BCrypt Password Encoder加密方案,这样系统就可以达到在新存储密码可以使用 BCrypt Password Encoder 加密方案进行加密,但是对于数据库里面以前用其他方式加密的密码也支持比对。我们可以复写该方法,然后修改这个“encodingId”的值,就可以在几种加密算法中进行切换了。

四. 源码解析

利用上面的代码,我们就实现了密码加密,还是很简单的,那么加密的底层原理是怎么样的呢?我们看看源码是怎么定义的吧。

1. PasswordEncoder接口解读

根据上文可知,Spring Security 为我们提供了一套简单易用的密码加密和比对规则,主要是利用org.springframework.security.crypto.password.PasswordEncoder 接口来进行实现,在该接口中定义了如下三个方法:

**public** **interface** **PasswordEncoder** {

 /**
  * Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or
  * greater hash combined with an 8-byte or greater randomly generated salt.
  */
 String **encode**(CharSequence rawPassword);

 /**
  * Verify the encoded password obtained from storage matches the submitted raw
  * password after it too is encoded. Returns true if the passwords match, false if
  * they do not. The stored password itself is never decoded.
  *
  * **@param** rawPassword the raw password to encode and match
  * **@param** encodedPassword the encoded password from storage to compare with
  * **@return** true if the raw password, after encoding, matches the encoded password from
  * storage
  */
 **boolean** **matches**(CharSequence rawPassword, String encodedPassword);

 /**
  * Returns true if the encoded password should be encoded again for better security,
  * else false. The default implementation always returns false.
  * **@param** encodedPassword the encoded password to check
  * **@return** true if the encoded password should be encoded again for better security,
  * else false.
  */
 **default** **boolean** **upgradeEncoding**(String encodedPassword) {
  **return** **false**;
 }
}

在PasswordEncoder接口中,有3个方法如下:

  • **encode()方法,**用于对密码进行加密,参数 rawPassword 表示我们传入的密码明文,返回值是加密之后的密文;
  • **matches()方法,**表示对密码进行比对,参数 rawPassword 代表用户登录时传入的密码,encodedPassword 则代表加密后的密码(一般从数据库中查询而来);
  • **upgradeEncoding()方法,**则用于判断是否需要对密码进行再次加密,以使得密码更加安全, 默认不需要。

一、网安学习成长路线图

网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

二、网安视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
在这里插入图片描述

三、精品网安学习书籍

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
在这里插入图片描述

四、网络安全源码合集+工具包

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
在这里插入图片描述

五、网络安全面试题

最后就是大家最关心的网络安全面试题板块
在这里插入图片描述在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

.csdnimg.cn/15c1192cad414044b4dd41f3df44433d.png)在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
[外链图片转存中…(img-3HCXQRwI-1713621976984)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值