springsecurity从4.2升级到5.0之后,做简单的登录,出现如下所示的错误:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”
at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder
.matches(DelegatingPasswordEncoder.java:238)
at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:198)
根据官方文档的资料和网上解决办法,需要做一些修改。
默认情况下与4.2版本不同的是,springsecurity5.0密码加密方式采用了bcrypt的方式,而且密码直接配置在xml文件中,不光是需要使用BCryptPasswordEncoder来加密,还需要指定一个encodingId,如果不指定,就会报出如题所示的错误。
我们可以看看PasswordEncoderFactories.createDelegatingPasswordEncoder()方法:
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);
}
这就是为什么说默认情况下使用的bcrypt方式加密。
知道了用什么方式,我们就可以来改进了。
1、密码不加密,和springsecurity4.2一样,使用明文密码,那就需要配置密码验证方式为noop,配置如下。
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"></beans:bean>
在spring-security.xml配置文件中配置以上bean即可。
密码配置如下:
<user name="admin" password="123456" authorities="ROLE_USER"/>
这样,默认密码就不需要进行加密,原样传入,原样匹配。这种配置方式密码前面,不需要加上{noop}
2、使用默认的密码加密方式,需要改动密码,并且密码格式如下:
{bcrypt}$2a$10$rY/0dflGbwW6L1yt4RVA4OH8aocD7tvMHoChyKY/XtS4DXKr.JbTC
在通过bcrypt方式加密之后的密文,前面加上{bcrypt}。
这里另外介绍两种方式来通过bcrypt方式加密,直接上源码:
package com.xxx.security.test;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
public class PasswordEncoder {
public static void main(String[] args) {
org.springframework.security.crypto.password.PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
String password = "123456";
String password2 = encoder.encode(password);
System.out.println(password2);
System.out.println(encoder.matches(password, password2));
BCryptPasswordEncoder encoder2 = new BCryptPasswordEncoder();
String password3 = encoder2.encode(password);
System.out.println(password3);
System.out.println(encoder2.matches(password, password3));
}
}
运行以上代码,打印结果如下:
眼尖的你,可能一下子就发现了一个小问题,通过bcrypt方式加密的密文,除了{bcrypt}外两次结果不一样,怎么会这样?
和以往加密方式不同的是,springsecurity5.0以上版本对bcrypt加密方式进行了随机数处理,所以每次产生的结果都不一样。不管是哪种方式,我们如果使用默认的加密方式,就需要在xml中配置密码为如下的样子。记得前面有{bcrypt}。
{bcrypt}$2a 10 10 10rY/0dflGbwW6L1yt4RVA4OH8aocD7tvMHoChyKY/XtS4DXKr.JbTC
报错的意思是说我们没有指定一个encodingId,最终在encoders map中没有匹配到encodingId。
如果我们需要使用MD5方式加密,我们就需要指定如下所示的密码:
{MD5}{pwegM3ORx1OzOc4eFzVztSPdeZit0BZPWTA5JB1Juc0=}dda279a014895996e55957976ec4cd36
同理,如果我们需要像springsecurity4.2那样,密码明文,我们可以这样配置:
也无需像第一种方式那样在配置文件中增加NoOpPasswordEncoder的配置了。