在项目开发过程中,常常使用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("密码错误!");
        }
    }
}