-
数据安全是很重要的事情,关键数据传输时,要加密;数据库里面的重要信息也要加密。
-
在开始本章之前,我想先分享一个故事,上个星期我的一台服务器被勒索病毒攻击了,里面很多信息都没有了,数据库也没有了,只留下一个地址,让我给对方付0.03比特币(3000元左右),非常难受(上半年侥幸活下来了,下半年的事都有点难顶呀),平台说cpu没事,让我重装系统(甩锅,看来免费的东西都不怎么靠谱);平台给了我几条建议:
1.禁用ROOT 2.用户名和密码尽量复杂 3.修改ssh的默认22端口 4.安装DenyHosts防暴力破解软件 5.禁用密码登录,使用RSA公钥登录
依赖
|
依赖注意事项:我们构建Web类型的安全项目时,spring-security-config、spring-security-core、spring-security-web三个依赖都是必须添加的。
PasswordEncoder
|
PasswordEncoder是Spring Security提供的密码加密方式的接口定义。
1)源码类如下所示:
package org.springframework.security.crypto.password;
public interface PasswordEncoder {
//加密
String encode(CharSequence var1);
//匹配,第一个是客户端的明文密码,第二个是已经加密的密码
boolean matches(CharSequence var1, String var2);
//对于已经加密的密码再次加密
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
#encode
该方法提供了明文密码的加密处理,加密后密文的格式主要取决于PasswordEncoder接口实现类实例。
#matches
匹配存储的密码以及登录时传递的密码(登录密码是经过加密处理后的字符串)是否匹配,如果匹配该方法则会返回true.
2)它的一个主要默认实现类:BCryptPasswordEncoder
public class BCryptPasswordEncoder implements PasswordEncoder {
private Pattern BCRYPT_PATTERN;
private final Log logger;
private final int strength;
private final BCryptPasswordEncoder.BCryptVersion version;
private final SecureRandom random;
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
} else {
String salt;
if (this.random != null) {
salt = BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);
} else {
salt = BCrypt.gensalt(this.version.getVersion(), this.strength);
}
return BCrypt.hashpw(rawPassword.toString(), salt);
}
}
}
3)简单的一个demo
@SpringBootTest
class SpringSecuritySsoApplicationTests {
@Test
void contextLoads() {
PasswordEncoder pw= new BCryptPasswordEncoder();
//加密
String encode=pw.encode("123");
System.out.println(encode);
//比较密码
boolean matches=pw.matches("123",encode);
System.out.println("==============================");
System.out.println(matches);
}
}
DelegatingPasswordEncoder
|
在之前版本集成Spring Security时,我们需要通过@Bean的方式去配置全局统一使用的密码加密方式(PasswordEncoder),这种方法现在用的也很多。
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
但5.X版本开始为了支持多种加密方式,诞生了DelegatingPasswordEncoder委托加密方式类。
它内部其实是一个Map集合,根据传递的Key(Key为加密方式)获取Map集合的Value,而Value则是具体的PasswordEncoder实现类。
@Bean
PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
负责生产 DelegatingPasswordEncoder 的工厂方法:
public class PasswordEncoderFactories {
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new LdapShaPasswordEncoder());
encoders.put("MD4", new Md4PasswordEncoder());
encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new StandardPasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
private PasswordEncoderFactories(){}
}
如此注入 PasswordEncoder 之后,我们在数据库中需要这么存储数据:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
DelegatingPasswordEncoder默认使用的还是bcrypt加密方式
注意:
Spring Security 5中密码策略的变更:
(1)必须显式指定PasswordEncoder,
(2)存储的密码格式变为:{id}加密后的密文
(3)id是一个标识符,用于查找是哪个PasswordEncoder,也就是密码加密时使用的PasswordEncoder。密码必须以id开始,id前后必须加{}。如果id找不到,id则会为空。