CAS中,默认支持的PasswordEncoder只能通过一种密码算法进行编码或加密,但是日常场景经常存在组合编码的需求,这就需要我们去定制一种支持密码组合编码或加密的编码器。
文章目录
- 场景
- 定制目标
- 定制方案
- 定制代码
场景
一般项目中有很多需要进行组合编码或加密的场景:如
用户在前端输入密码后,前端先进行一次md5编码,后端进行密码校验的时候,再进行一次sha1编码,最后再与数据库的值进行比对。
对于这些场景,CAS系统中默认的密码编码器DefaultPasswordEncoder
已经无法实现需求,需要进行定制。
定制目标
- 能支持密码组合编码或加密。
- 改动尽可能少,不影响其它功能。
定制方案
- 不改动
PasswordEncoderProperties
配置映射,只对其中encodingAlgorithm
字段进行特殊解析。 - 如果
encodingAlgorithm
字段包含指定的前缀,则认为需要组合加密,进行组合加密的逻辑。如果不包含,则使用默认逻辑。 - 具体组合加密逻辑为,通过分隔符分割出所有需要组合加密的算法,依次进行编码或加密。
定制代码
注意事项:
-
重写默认的
DefaultPasswordEncoder
。 -
请将日志工具替换为cas的日志工具或自己的日志工具。
import com.atfwus.*****.util.log.LoggingUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* @author atfwus
* @ClassName DefaultPasswordEncoder
* @description: 默认密码加密器
* @date 2023.08.04
* @version: 1.0
*/
@Slf4j
@RequiredArgsConstructor
public class DefaultPasswordEncoder implements PasswordEncoder {
private final String encodingAlgorithm;
private final String characterEncoding;
private final String COMBINE_PREFIX = "COMBINE::";
private final String SPLIT_CHARACTER = ",";
private String oneEncode(final CharSequence password, String oneEncodingAlgorithm) {
val encodingCharToUse = StringUtils.isNotBlank(this.characterEncoding)
? this.characterEncoding : Charset.defaultCharset().name();
log.debug("Using [{}] as the character encoding algorithm to update the digest", encodingCharToUse);
try {
val pswBytes = password.toString().getBytes(encodingCharToUse);
val encoded = Hex.encodeHexString(DigestUtils.getDigest(oneEncodingAlgorithm).digest(pswBytes));
log.debug("Encoded password via algorithm [{}] and character-encoding [{}] is [{}]", oneEncodingAlgorithm,
encodingCharToUse, encoded);
return encoded;
} catch (final Exception e) {
LoggingUtils.error(log, e);
}
return null;
}
@Override
public String encode(final CharSequence password) {
if (password == null) {
return null;
}
if (StringUtils.isBlank(this.encodingAlgorithm)) {
log.warn("No encoding algorithm is defined. Password cannot be encoded; Returning null");
return null;
}
if(this.encodingAlgorithm.startsWith(COMBINE_PREFIX)) {
CharSequence encoded = password;
String[] algorithms = this.encodingAlgorithm.substring(COMBINE_PREFIX.length()).split(SPLIT_CHARACTER);
return Arrays.stream(algorithms)
.reduce(encoded, this::oneEncode, (result1, result2) -> result2)
.toString();
} else {
return oneEncode(password, this.encodingAlgorithm);
}
}
@Override
public boolean matches(final CharSequence rawPassword, final String encodedPassword) {
val encodedRawPassword = StringUtils.isNotBlank(rawPassword) ? encode(rawPassword.toString()) : null;
val matched = StringUtils.equals(encodedRawPassword, encodedPassword);
val msg = String.format("Provided password does%smatch the encoded password",
BooleanUtils.toString(matched, StringUtils.EMPTY, " not "));
log.debug(msg);
return matched;
}
}
在配置文件中,对需要用到PasswordEncoder
的地方进行类似如下的配置,即可实现组合编码或加密。
ATFWUS 2023-08-09