
流程图中左边为现有的登录,存在明文传输密码,通过抓包形式很容易破解,右图为RSA加密登录避免了密码明文传输,抓包获取不到真实密码,而且加密私钥一般在生成后五秒自动失效(自定义时间大小),破解难度大。
密钥生成工具类如下
package com.gaojin.common.utils;import org.apache.commons.codec.binary.Base64;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import javax.annotation.Resource;import javax.crypto.Cipher;import java.nio.charset.StandardCharsets;import java.security.*;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.concurrent.TimeUnit;/*** @author zhouq* @date 2021/11/26*/@Componentpublic class RSAEncrypt {@Resourceprivate RedisTemplate<String, Object> redisTemplate;// 自定义redis的keyprivate static final String PREFIX = "gaojin-business:wmsPublicKey";/*** 随机生成密钥对** @throws NoSuchAlgorithmException 异常*/public String genKeyPair(String user) throws NoSuchAlgorithmException {// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");// 初始化密钥对生成器,密钥大小为96-1024位keyPairGen.initialize(1024, new SecureRandom());// 生成一个密钥对,保存在keyPair中KeyPair keyPair = keyPairGen.generateKeyPair();// 得到私钥RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();// 得到公钥RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));// 得到私钥字符串String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));redisTemplate.opsForValue().set(PREFIX + user, privateKeyString, 5, TimeUnit.SECONDS);return publicKeyString;}/*** RSA私钥解密** @param str 加密字符串* @param privateKey 私钥* @return 铭文* @throws Exception 解密过程中的异常信息*/public static String decrypt(String str, String privateKey) throws Exception {//64位解码加密后的字符串byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));//base64编码的私钥byte[] decoded = Base64.decodeBase64(privateKey);RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));//RSA解密Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE, priKey);return new String(cipher.doFinal(inputByte));}}
以下为登陆方法需要修改的地方
@PostMapping("/login")public AjaxResult login(@RequestBody LoginBody loginBody) throws Exception {AjaxResult ajax = AjaxResult.success();// 获取redis中用户对应的私钥,对密码进行解密,String privateKey = (String) redisTemplate.opsForValue().get(PREFIX + loginBody.getUsername());// 对前端加密后的密码进行解密,获取到真实的密码String password = RSAEncrypt.decrypt(loginBody.getPassword(), privateKey);// 将真实的密码设置到登录参数中loginBody.setPassword(password);// 生成令牌String token = loginService.login(loginBody.getUserName(), loginBody.getPassword(),loginBody.getUuid());ajax.put(Constants.TOKEN, token);return ajax;}@GetMapping("/getPublicKey")public AjaxResult getPublicKey(String user) throws NoSuchAlgorithmException {return AjaxResult.success(rsaEncrypt.genKeyPair(user));}
前端登录方法按照以下写法
encryptedData(data, query) {var publicKey = query// 新建JSEncrypt对象const encryptor = new JSEncrypt()// 设置公钥encryptor.setPublicKey(publicKey)// 加密数据this.loginForm.password = encryptor.encrypt(data)},handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = trueif (this.loginForm.rememberMe) {Cookies.set('username', this.loginForm.username, { expires: 30 })Cookies.set('password', encrypt(this.loginForm.password), { expires: 30 })// Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 })} else {Cookies.remove('username')Cookies.remove('password')// Cookies.remove('rememberMe')}getPublicKey({ user: this.loginForm.username }).then(response => {console.log(response)this.encryptedData(this.loginForm.password, response.msg)this.$store.dispatch('Login', this.loginForm).then(() => {this.$router.push({ path: this.redirect || '/' }).catch(() => {})}).catch(() => {this.loading = false// this.getCode()})})}})}
