用户进行登录时,需要输入明文密码,在传输过程中,有可能被拦截获取,因此使用前端rsa加密,后端rsa解密,使得密码以密文的形式进行传输,增强安全性。
策略:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.5.0</version>
</dependency>
@Service
public class CommonServiceImpl implements CommonService {
@Autowired
private RedisUtils redisUtils;
// 获取公钥
@Override
public Map<String, Object> getPublicKey() {
// 从redis中获取 rsa标识
String rsaTag = redisUtils.get(RedisConstants.RSA_TAG, String.class);
String publicKeyBase64 = null;
Long expire = 60 * 60 * 24L;
if (rsaTag == null) {
// rsa标识为空,生成新的rsa,有效期25个小时,生成rsa标识,有效期24个小时
// 差一个小时的原因是为了防止 刚获取到公钥,停了一会,然后公钥就失效了
rsaTag = NCUtils.getUUID().substring(0,8);
RSA rsa = new RSA();
publicKeyBase64 = rsa.getPublicKeyBase64();
String privateKeyBase64 = rsa.getPrivateKeyBase64();
redisUtils.set(RedisConstants.RSA_TAG, rsaTag, 60 * 60 * 24);
redisUtils.set(RedisConstants.RSA_PUBLIC_KEY + rsaTag, publicKeyBase64, 60 * 60 * 25);
redisUtils.set(RedisConstants.RSA_PRIVATE_KEY + rsaTag, privateKeyBase64, 60 * 60 * 25);
} else {
// 根据rsa标识从redis中获取公钥
publicKeyBase64 = redisUtils.get(RedisConstants.RSA_PUBLIC_KEY + rsaTag, String.class);
expire = redisUtils.getExpire(RedisConstants.RSA_TAG);
}
Map<String, Object> map = new HashMap<>();
map.put("rsaTag", rsaTag);
map.put("publicKeyBase64", publicKeyBase64);
map.put("expire", expire);
return map;
}
// 根据rsaTag获取私钥
@Override
public String getPrivateKey(String rsaTag) {
NCUtils.nullOrEmptyThrow(rsaTag);
String privateKeyBase64 = redisUtils.get(RedisConstants.RSA_PRIVATE_KEY + rsaTag, String.class);
if (privateKeyBase64 == null) {
throw new NCException("-1", "RSA已失效,请重新生成获取!");
}
return privateKeyBase64;
}
// 根据rsaTag和密文解密,返回明文
@Override
public String rsaDecrypt(String rsaTag, String mw) {
NCUtils.nullOrEmptyThrow(rsaTag);
NCUtils.nullOrEmptyThrow(mw);
String privateKeyBase64 = getPrivateKey(rsaTag);
RSA rsa = new RSA(privateKeyBase64, null);
// 密码的密文先进行base64解码,之后再进行解密
byte[] decrypt = rsa.decrypt(Base64.decode(mw), KeyType.PrivateKey);
return StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8);
}
}
@RestController
@RequestMapping("/common")
public class CommonController {
@Autowired
private CommonService commonService;
@PostMapping("/getPublicKey")
public NCResult<String> getKeyOfRSA(HttpServletResponse response) {
Map<String, Object> map = commonService.getPublicKey();
Cookie rsaTag = new Cookie(HeaderConstants.RSA_TAG, map.get("rsaTag").toString());
rsaTag.setMaxAge(Integer.valueOf(map.get("expire").toString()));
rsaTag.setPath("/");
response.addCookie(rsaTag);
return NCResult.ok(map.get("publicKeyBase64"));
}
}
@Autowired
private LoginService loginService;
@Autowired
private CommonService commonService;
@ApiAnnotation(modularName = "用户登陆", description = "用户登陆")
@PostMapping("/login")
public NCResult<NCLoginUserVO> login(@RequestBody LoginVO loginVO, HttpServletResponse response) {
// 登陆验证
loginVO.setPassword(commonService.rsaDecrypt(loginVO.getRsaTag(), loginVO.getPassword()));
NCLoginUserVO token = loginService.login(loginVO);
return NCResult.ok(token);
}
三、前端
npm install node-forge
// 菜单工具类
const MenuUtils = {
// 设置cookie
setCookie (key, value, expire) {
var date = new Date()
date.setSeconds(date.getSeconds() + expire)
document.cookie = key + '=' + escape(value) + '; expires=' + date.toGMTString()
},
// 获取cookie
getCookie (name) {
if (document.cookie.length > 0) {
let start = document.cookie.indexOf(name + '=')
if (start !== -1) {
start = start + name.length + 1
let end = document.cookie.indexOf(';', start)
if (end === -1) end = document.cookie.length
return unescape(document.cookie.substring(start, end))
}
}
return ''
},
// 删除cookie
delCookie (name) {
this.setCookie(name, '', -1)
}
}
export {
MenuUtils
}
import * as Forge from 'node-forge'
operateApi.getPublicKey({}).then((pubRes) => {
const pki = Forge.pki
// 规定格式:publicKey之前需要加'-----BEGIN PUBLIC KEY-----\n',之后需要加'\n-----END PUBLIC KEY-----'
const publicK = pki.publicKeyFromPem('-----BEGIN PUBLIC KEY-----\n' + pubRes.rows[0] + '\n-----END PUBLIC KEY-----');
// forge通过公钥加密后一般会是乱码格式,可进行base64编码操作再进行传输,相应的,后台获取到密文的密码后需要先进行base64解码操作再进行解密
const password = Forge.util.encode64(publicK.encrypt(that.loginParm.password))
operateApi.userLogin({
account: that.loginParm.username,
password: password,
rsaTag: MenuUtils.getCookie('RSA-TAG')
}).then((res) => {
if (res.success) {
} else {
}
})
})