【项目实战】前后端分离的SpringCloud项目如何通过AES对称加密算法对登录密码加解密?
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
一、AES对称加密算法简介
1.1 AES对称加密算法是什么
AESAdvanced Encryption Standard高级加密标准是一种对称加密算法常用于加密数据传输和数据存储。它采用了更高级的替代替代数据加密标准DES的技术并且被认为是目前最安全的对称加密算法。
1.2 AES对称加密算法中基本概念
包括密钥长度、分组密码和循环。
概念 | 解析 |
---|---|
密钥 | SECRET_KEY |
密钥长度 | 可以是128、192或256位 |
分组密码 | 意味着明文被分成若干个组每组再分别加密。 |
循环 | 循环指加密过程中重复运算的次数。 |
IV向量 | 可以使用六位十六进制数作为密钥偏移量SECRET_IV |
二、前端如何使用AES算法进行加密
用户登录时对登录密码进行加密传输因此需要在前端提供简单的AES对称加密算法实现加密在后端实现解密。
以下是具体的步骤
2.1 定义前端配置文件
在前端配置文件project-ui\src\config\env.js中定义securityKey的值
注意key 和后端网关配置相同这里打包混淆后相对安全。
import {isSandEnv} from "@/util/util";
// 配置编译环境和线上环境之间的切换
const env = process.env
//前端密码密钥必须16位和nacos配置文件base-gateway-dev.yml中的security.encode.key对应
let securityKey = '1234567891234567'
if (env.NODE_ENV == 'development') {
} else if (env.NODE_ENV == 'production') {
} else if (env.NODE_ENV == 'test') {
}
export {
env,
securityKey
}
2.2 引入前端依赖crypto-js
"dependencies": {
"crypto-js": "^3.1.9-1",
2.3 编写前端加密方法
import * as CryptoJS from'crypto-js'
/**
*加密处理
*/
export const encryption = (params) => {
let {
data,
type,
param,
key
} = params
const result = JSON.parse(JSON.stringify(data))
if (type === 'Base64') {
param.forEach(ele => {
result[ele] = btoa(result[ele])
})
} else {
param.forEach(ele => {
var data = result[ele]
key = CryptoJS.enc.Latin1.parse(key)
var iv = key
// 加密
var encrypted = CryptoJS.AES.encrypt(
data,
key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
})
result[ele] = encrypted.toString()
})
}
return result
}
2.4 新建页面JS逻辑
project-ui\src\store\modules\user.js
2.5 引入securityKey字段和encryption方法
import { encryption} from '@/util/util'
import { securityKey } from '@/config/env'
2.6 使用securityKey 字段
// 根据用户名登录
LoginByUsername({commit}, userInfo) {
const user = encryption({
data: userInfo,
key: securityKey,
param: ['password']
})
return new Promise((resolve, reject) => {
loginByUsername(user.username, user.password, user.code, user.randomStr).then(response => {
const data = response.data
commit('SET_ACCESS_TOKEN', data.access_token)
commit('SET_REFRESH_TOKEN', data.refresh_token)
commit('SET_EXPIRES_IN', data.expires_in)
commit('CLEAR_LOCK')
resolve()
}).catch(error => {
reject(error)
})
})
},
三、后端如何使用AES算法进行解密
3.1 后端 网关定义迷药
在Nacos上的配置中 base-gateway-dev.yml
修改gateway上的配置文件设置好密钥如下
security:
encode:
# 前端密码密钥必须16位
key: '1234567891234567'
3.2 使用hutool提供的工具类进行解密
public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {
private static final String PASSWORD = "password";
private static final String KEY_ALGORITHM = "AES";
@Value("${security.encode.key:1234567812345678}")
private String encodeKey;
@SneakyThrows
private static String decryptAES(String data, String pass) {
AES aes = new AES(Mode.CBC,Padding.NoPadding,
new SecretKeySpec(pass.getBytes(), KEY_ALGORITHM),
new IvParameterSpec(pass.getBytes()));
byte[] result = aes.decrypt(Base64.decode(data.getBytes(StandardCharsets.UTF_8)));
return new String(result, StandardCharsets.UTF_8);
}
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 不是登录请求直接向下执行
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) {
return chain.filter(exchange);
}
URI uri = exchange.getRequest().getURI();
String queryParam = uri.getRawQuery();
Map<String, String> paramMap = HttpUtil.decodeParamMap(queryParam, CharsetUtil.CHARSET_UTF_8);
String password = paramMap.get(PASSWORD);
if (StrUtil.isNotBlank(password)) {
try {
password = decryptAES(password, encodeKey);
} catch (Exception e) {
log.error("密码解密失败:{}", password);
return Mono.error(e);
}
paramMap.put(PASSWORD, password.trim());
}
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(HttpUtil.toParams(paramMap))
.build(true)
.toUri();
ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(newRequest).build());
};
}
}