在项目开发过程中,常常使用SpringSecurity来接管用户登录认证的功能,基本流程是前端将账号密码post到后端配置的SpringSecurity的登录请求url中,SpringSecutity接收到数据后自行对账号密码作对比认证,得到登录成功与否的结果。在实际的项目中,虽然post请求的参数不会直接暴露在请求的url中,但是仍然可以通过抓包获取到传输的账号密码等信息,这显然是不安全的。此时,我们可以使用非对称加密的方式来对密码进行加密后再传输,后端接收到请求交给SpringSecurity处理之前先对密码字符串进行解密再交给SpringSecurity处理。这里我们使用常用的非对称加密方式RSA进行数据的加解密。
”RSA加密算法是一种非对称加密算法。对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就肯定会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA钥匙才可能被强力方式解破。到目前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。”----来自度娘
前端加密密码时可以使用jsencrypt进行,
// 实例化JSEncrypt对象
let encryptor = new JSEncrypt()
// 设置公钥
let publicKey = `公钥`
encryptor.setPublicKey(publicKey)
// 加密123456
let password = encryptor.encrypt('123456')
后端过滤器:(RSASecurityUtils看上篇)
@Slf4j
@Component
public class DecryptUsernamePasswordFilter extends GenericFilterBean {
// 私钥文件地址
private String privateKeyPath = "privateKey.txt";
// 读取文件
String privateKey = RSASecurityUtils.getKey(privateKeyPath);
// Spring Security Web 的一个概念模型接口,可用来匹配请求
private final RequestMatcher requiresRequestMatcher;
public DecryptUsernamePasswordFilter() {
// 匹配路径为/auth/token,请求方式为POST的请求
requiresRequestMatcher = new AntPathRequestMatcher("/auth/token", "POST");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresRequestMatcher.matches(request)) {
chain.doFilter(request, response);
return;
}
// 加密参数解密
ParameterMap<String, String[]> parameterMap = (ParameterMap<String, String[]>) request.getParameterMap();
//由于ParameterMap在实例化后会被锁定,所以我们要先调用一下它的解锁方法。
parameterMap.setLocked(false);
parameterMap.forEach((k, v) -> {
if ("password".equals(k)) {
parameterMap.put(k, new String[]{decrypt(request.getParameter(k))});
}
});
log.info(JSONObject.toJSONString(parameterMap));
//操作完之后给它锁定
parameterMap.setLocked(true);
chain.doFilter(request, response);
}
private String decrypt(String data) {
try {
// 使用rsa加密后得到的字符串可能含有 + 号,但是在url中 + 号表示空格,所以后端接收到了这个字符串之后要对空格进行替换,否则将解密失败
String s = RSASecurityUtils.decrypt(privateKey, data.replace(" ","+"));
return s;
} catch (Exception e) {
throw new CustomException("密码错误!");
}
}
}