文章目录

  • 一、加密和解密
    • 1. 为什么要加密
    • 2. 加密方案
    • 3. PasswordEncoder
  • 二、前期准备
  • 二、用户配置
    • 1. 配置文件
    • 2. 配置类

一、加密和解密

1. 为什么要加密

2011 年 12 月 21 日,有人在网络上公开了一个包含 600 万个 CSDN 用户资料的数据库,数据全部为明文储存,包含用户名、密码以及注册邮箱。事件发生后 CSDN 在微博、官方网站等渠道发出了声明,解释说此数据库系 2009 年备份所用,因不明原因泄露,已经向警方报案,后又在官网发出了公开道歉信。在接下来的十多天里,金山、网易、京东、当当、新浪等多家公司被卷入到这次事件中。整个事件中最触目惊心的莫过于 CSDN 把用户密码明文存储,由于很多用户是多个网站共用一个密码,因此一个网站密码泄露就会造成很大的安全隐患。由于有了这么多前车之鉴,我们现在做系统时,密码都要加密处理。

这次泄密,也留下了一些有趣的事情,特别是对于广大程序员设置密码这一项。人们从 CSDN 泄密的文件中,发现了一些好玩的密码,例如如下这些:

ppnn13%dkstFeb.1st 这段密码的中文解析是:娉娉袅袅十三余,豆蔻梢头二月初。
csbt34.ydhl12s 这段密码的中文解析是:池上碧苔三四点,叶底黄鹂一两声

2. 加密方案

密码加密我们一般会用到散列函数,又称散列算法、哈希函数,这是一种从任何数据中创建数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来,然后将数据打乱混合,重新创建一个散列值。散列值通常用一个短的随机字母和数字组成的字符串来代表。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到。我们常用的散列函数有 MD5 消息摘要算法、安全散列算法(Secure Hash Algorithm)。

但是仅仅使用散列函数还不够,为了增加密码的安全性,一般在密码加密过程中还需要加盐,所谓的盐可以是一个随机数也可以是用户名,加盐之后,即使密码明文相同的用户生成的密码密文也不相同,这可以极大的提高密码的安全性。但是传统的加盐方式需要在数据库中有专门的字段来记录盐值,这个字段可能是用户名字段(因为用户名唯一),也可能是一个专门记录盐值的字段,这样的配置比较繁琐。

Spring Security 提供了多种密码加密方案,官方推荐使用 BCryptPasswordEncoderBCryptPasswordEncoder 使用 BCrypt 强哈希函数,开发者在使用时可以选择提供 strength 和 SecureRandom 实例。strength 越大,密钥的迭代次数越多,密钥迭代次数为 2^strength。strength 取值在 4~31 之间,默认为 10。

不同于 Shiro 中需要自己处理密码加盐,在 Spring Security 中,BCryptPasswordEncoder 就自带了盐,处理起来非常方便。

BCryptPasswordEncoder 就是 PasswordEncoder 接口的实现类。

3. PasswordEncoder

二、前期准备

首先新建一个 Spring Boot 项目,创建时引入 Spring Security 依赖和 web 依赖,如下图:


项目创建成功后,Spring Security 的依赖就添加进来了,在 Spring Boot 中我们加入的是 spring-boot-starter-security ,其实主要是这两个:


项目创建成功后,我们添加一个测试的 HelloController,内容如下:

@RestController
publicclass HelloController {@GetMapping("/hello")public String hello() {return"hello";}
}

接下来什么事情都不用做,我们直接来启动项目。

在项目启动过程中,我们会看到如下一行日志:

Using generated security password: 30abfb1f-36e1-446a-a79b-f70024f589ab

这就是 Spring Security 为默认用户 user 生成的临时密码,是一个 UUID 字符串。

接下来我们去访问 http://localhost:8080/hello 接口,就可以看到自动重定向到登录页面了:


在登录页面,默认的用户名就是 user,默认的登录密码则是项目启动时控制台打印出来的密码,输入用户名密码之后,就登录成功了,登录成功后,我们就可以访问到 /hello 接口了。

在 Spring Security 中,默认的登录页面和登录接口,都是 /login ,只不过一个是 get 请求(登录页面),另一个是 post 请求(登录接口)。

大家可以看到,非常方便,一个依赖就保护了所有接口

有人说,你怎么知道知道生成的默认密码是一个 UUID 呢?

这个其实很好判断。
和用户相关的自动化配置类在 UserDetailsServiceAutoConfiguration 里边,在该类的 getOrDeducePassword 方法中,我们看到如下一行日志:

if (user.isPasswordGenerated()) {logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
}

毫无疑问,我们在控制台看到的日志就是从这里打印出来的。打印的条件是 isPasswordGenerated 方法返回 true,即密码是默认生成的。

进而我们发现,user.getPassword 出现在 SecurityProperties 中,在 SecurityProperties 中我们看到如下定义:

/*** Default user name.*/
private String name = "user";
/*** Password for the default user name.*/
private String password = UUID.randomUUID().toString();
private boolean passwordGenerated = true;

可以看到,默认的用户名就是 user,默认的密码则是 UUID,而默认情况下,passwordGenerated 也为 true

二、用户配置

默认的密码有一个问题就是每次重启项目都会变,这很不方便。

在正式介绍数据库连接之前,先和大家介绍两种非主流的用户名/密码配置方案。

1. 配置文件

我们可以在 application.properties 中配置默认的用户名密码。

怎么配置呢?上面说的 SecurityProperties,默认的用户就定义在它里边,是一个静态内部类,我们如果要定义自己的用户名密码,必然是要去覆盖默认配置,我们先来看下 SecurityProperties 的定义:

@ConfigurationProperties(prefix = "spring.security")
publicclass SecurityProperties {}

这就很清晰了,我们只需要以 spring.security.user 为前缀,去定义用户名密码即可:

spring.security.user.name=yolo
spring.security.user.password=123

这就是我们新定义的用户名密码。

properties 中定义的用户名密码最终是通过 set 方法注入到属性中去的,这里我们顺便来看下 SecurityProperties.User#setPassword 方法:

public void setPassword(String password) {if (!StringUtils.hasLength(password)) {return;}this.passwordGenerated = false;this.password = password;
}

从这里我们可以看到,application.properties 中定义的密码在注入进来之后,还顺便设置了 passwordGenerated 属性为 false,这个属性设置为 false 之后,控制台就不会打印默认的密码了。

此时重启项目,就可以使用自己定义的用户名/密码登录了。

2. 配置类

除了上面的配置文件这种方式之外,我们也可以在配置类中配置用户名/密码。

在配置类中配置,我们就要指定 PasswordEncoder 了,这是一个非常关键的东西。

PasswordEncoder 这个接口中就定义了三个方法:

public interface PasswordEncoder {String encode(CharSequence rawPassword);boolean matches(CharSequence rawPassword, String encodedPassword);default boolean upgradeEncoding(String encodedPassword) {returnfalse;}
}

(1)encode 方法用来对明文密码进行加密,返回加密之后的密文。
(2)matches 方法是一个密码校对方法,在用户登录的时候,将用户传来的明文密码和数据库中保存的密文密码作为参数,传入到这个方法中去,根据返回的 Boolean 值判断用户密码是否输入正确。
(3)upgradeEncoding 是否还要进行再次加密,这个一般来说就不用了。

通过下图我们可以看到 PasswordEncoder 的实现类:


具体的配置:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@BeanPasswordEncoder passwordEncoder() {return NoOpPasswordEncoder.getInstance();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("yolo").password("123").roles("admin");}
}

(1) 首先我们自定义 SecurityConfig 继承自 WebSecurityConfigurerAdapter,重写里边的 configure 方法。
(2)首先我们提供了一个 PasswordEncoder 的实例,因为目前的案例还比较简单,因此我暂时先不给密码进行加密,所以返回 NoOpPasswordEncoder 的实例即可。
(3)configure 方法中,我们通过 inMemoryAuthentication 来开启在内存中定义用户,withUser 中是用户名,password 中则是用户密码,roles 中是用户角色。

如果需要配置多个用户,用 and 相连。

在没有 Spring Boot 的时候,我们都是 SSM 中使用 Spring Security,这种时候都是在 XML 文件中配置 Spring Security,既然是 XML 文件,标签就有开始有结束,现在的 and 符号相当于就是 XML 标签的结束符,表示结束当前标签,这是个时候上下文会回到 inMemoryAuthentication 方法中,然后开启新用户的配置。

配置完成后,再次启动项目,Java 代码中的配置会覆盖掉 XML 文件中的配置,此时再去访问 /hello 接口,就会发现只有 Java 代码中的用户名/密码才能访问成功。

一篇文章带你入门 SpringSecurity实现密码加密和解码相关推荐

  1. 一篇文章带你入门adb自动化测试

    一篇文章带你入门adb自动化测试 前言 一.什么是adb 1.adb的原理的应用场景 2.(adb)Android debug bridge用于调试使用安卓系统的设备 3.adb基本原理 二.adb环 ...

  2. 转发 微博 Qzone 微信 一篇文章带你入门ZooKeeper实现原理!(超详细)

    转发 微博 Qzone 微信 一篇文章带你入门ZooKeeper实现原理!(超详细)

  3. 有没有python的班_【万字长文】别再报班了,一篇文章带你入门Python

    最近有许多小伙伴后台联系我,说目前想要学习Python,但是没有一份很好的资料入门.一方面的确现在市面上Python的资料过多,导致新手会不知如何选择,另一个问题很多资料内容也很杂,从1+1到深度学习 ...

  4. python基础论文_北大博士Python学习笔记,Python基础语法总结,一篇文章带你入门...

    image.png 网上现在Python学习资料有很多,但是很杂.很多初学Python的朋友就不知道该怎么去抉择,那些是自己当下所需要的. 刚好朋友是北大的博士,在IT行业也工作八年了.就把他学习Py ...

  5. 万字长文,一篇文章带你入门Python

    注释 Python中用#表示单行注释,#之后的同行的内容都会被注释掉. 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例 ...

  6. 0基础学python培训班_[长文] 学Python不用培训班,一篇文章带你入门

    最近有许多小伙伴后台联系我,说目前想要学习Python,但是没有一份很好的资料入门.一方面的确现在市面上Python的资料过多,导致新手会不知如何选择,另一个问题很多资料内容也很杂,从1+1到深度学习 ...

  7. 万字干货,Python语法大合集,一篇文章带你入门

    最近有许多小伙伴后台联系我,说目前想要学习Python,但是没有一份很好的资料入门.一方面的确现在市面上Python的资料过多,导致新手会不知如何选择,另一个问题很多资料内容也很杂,从1+1到深度学习 ...

  8. 一篇文章带你入门zabbix监控系统

    目录 一.监控介绍 二.监控软件区别 三.zabbix监控架构 四.zabbix监控介绍 1.zabbix优点 2.zabbix缺点 3.zabbix监控系统监控对象 4.zabbix监控方式 五.z ...

  9. 一篇文章带你入门音视频

    一.概述 1)流媒体协议是服务器与客户端之间通信遵循的规定.当前网络上主要的流媒体协议如表所示. 2)封装格式的主要作用是把视频码流和音频码流按照一定的格式存储在一个文件中. 3)视频编码的主要作用是 ...

最新文章

  1. Redis 为什么默认 16 个数据库?
  2. 一些常见的名词解释(持续更新中)
  3. Python基础知识(第十天)
  4. c语言两个正整数的最小公倍数,C语言求两个正整数的最小公倍数
  5. netty为什么性能高
  6. 祝融号火星车亮相,每小时仅移动40米,为何比乌龟还慢?
  7. Wo Cloud CentOS 挂载磁盘小计
  8. 【测试用例级别定义】
  9. tooltip 提示多行显示
  10. 7-2 温度转换 (5 分)
  11. lvs负载均衡的调度算法和工作模式
  12. JSP与Servlet传值及对比
  13. 计算机保研夏令营英语面试,保研经验 | 夏令营面试那些事儿(内含视频)
  14. 【机器学习+NER】手把手教你用机器学习CRF模型构建NER系统(CCL2021)
  15. 系统仿真实践中的精益思维(随感)
  16. 数据分类算法-朴素贝叶斯
  17. python提速网站资源_三分钟学会Django缓存,让你的网站提速十倍以上
  18. python获取json数据,快速生成excel
  19. 文明游戏5的计算机配置,文明6配置要求高吗 文明6最低电脑配置与推荐电脑配置...
  20. SQL数据计数(count)

热门文章

  1. superuser允许所有授权
  2. 简述seo的站外优化
  3. 中软国际成功转港交所主板上市
  4. SQL零基础投喂(复杂⼀点的查询)
  5. 南邮计算机学院2019研究生调剂,南京邮电大学2019年考研复试时间及调剂信息的公告...
  6. 低欲望催生出来的“性冷淡”产业营销策略分析
  7. K-fold划分数据进行训练有k个训练模型,那最终选取哪个模型?
  8. mysql 月份_MySQL显示所有月份
  9. 3万计算机配置清单,电脑组装知识网预算2万至3万元电脑主机推荐九代酷睿i9-9900K搭RTX2080Ti全能型高配电脑主机配置清单...
  10. Win7系统下如何使用蓝牙耳机收听音乐