有缺陷放弃
传输密码动态加密解密
这个模块分离至项目api权限管理系统与前后端分离实践,感觉那样写文章太长了找不到重点,分离出来要好点。
在用户密码登录认证中,明文传输用户输入的密码是不可取的。在没有用https的情况下,这里需要对用户密码加密传输,保证即使密码泄露也不影响。
这里的前后端加密解密下图:
由于介绍的是动态加密解密传输信息方案,这里并不会涉及之后的JWT签发等。
下面是实现细节:
angular 前端发送get动态秘钥请求后会对对象进行监听,在回调函数里获取后端返回的秘钥后再进行加密处理,之后再发送登录请求。在angular我把请求服务化了,下面的代码片段会有点凌乱。
// 调用获取tokenKey秘钥服务
this.loginService.getTokenKey().subscribe(
data => {
this.responseData = data;
if (this.responseData.data.tokenKey !== undefined) {
const tokenKey = this.responseData.data.tokenKey;
// 调用服务,发送认证请求
this.loginService.login(this.appId, this.password, tokenKey).subscribe(
data2 => {
// 认证成功返回jwt
this.responseData = data2;
if (this.responseData.meta.code === 1003 && this.responseData.data.jwt != null) {
this.authService.updateAuthorizationToken(this.responseData.data.jwt);
this.authService.updateUid(this.appId);
this.authService.updateUser(this.responseData.data.user);
this.router.navigateByUrl('/index');
} else {
this.msg = '用户名密码错误';
this.isDisabled = true;
}
},
error => {
console.error(error);
this.msg = error;
this.isDisabled = true;
}
);
}
}
);
@Injectable()
export class LoginService {
constructor(private httpUtil: HttpUtil) {
}
getTokenKey() {
const url = 'account/login?tokenKey=get';
// 先向后台申请加密tokenKey tokenKey=get
// const getKeyParam = new HttpParams().set('tokenKey', 'get');
return this.httpUtil.get(url);
}
login(appId: string, password: string, tokenKey: string) {
const url = 'account/login';
tokenKey = CryptoJS.enc.Utf8.parse(tokenKey);
password = CryptoJS.enc.Utf8.parse(password);
password = CryptoJS.AES.encrypt(password, tokenKey, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}).toString();
console.log(password);
const param = new HttpParams().append('appId', appId)
.append('password', password)
.append('methodName', 'login')
.append('timestamp', new Date().toUTCString());
return this.httpUtil.post(url, param);
}
}
后端是在一个filter中对登录注册请求进行拦截,判断其是正常登录注册还是获取动态加密秘钥请求,正常认证就走shiro,判断为获取秘钥则生成16随机码默认AES加密秘钥为约定16位,小于16位会报错
,将秘钥以<远程IP,秘钥>的5秒看自己情况不要太大也不要太短,设置有效期是为了防止被其他人截取到加密密码冒充用户的情况,把风险降更低
。
// 判断若为获取登录注册加密动态秘钥请求
if (isPasswordTokenGet(request)) {
//动态生成秘钥,redis存储秘钥供之后秘钥验证使用,设置有效期5秒用完即丢弃
String tokenKey = CommonUtil.getRandomString(16);
try {
redisTemplate.opsForValue().set("PASSWORD_TOKEN_KEY_"+request.getRemoteAddr().toUpperCase(),tokenKey,5, TimeUnit.SECONDS);
// 动态秘钥response返回给前端
Message message = new Message();
message.ok(1000,"issued tokenKey success")
.addData("tokenKey",tokenKey);
RequestResponseUtil.responseWrite(JSON.toJSONString(message),response);
}catch (Exception e) {
LOGGER.warn(e.getMessage(),e);
// 动态秘钥response返回给前端
Message message = new Message();
message.ok(1000,"issued tokenKey fail");
RequestResponseUtil.responseWrite(JSON.toJSONString(message),response);
}
return false;
}
// 创建认证信息,其中就有包括获取redis中对应IP的动态秘钥
private AuthenticationToken createPasswordToken(ServletRequest request) {
Map map = RequestResponseUtil.getRequestParameters(request);
String appId = map.get("appId");
String timestamp = map.get("timestamp");
String password = map.get("password");
String host = request.getRemoteAddr();
String tokenKey = redisTemplate.opsForValue().get("PASSWORD_TOKEN_KEY_"+host.toUpperCase());
return new PasswordToken(appId,password,timestamp,host,tokenKey);
}
效果展示
持续更新。。。。。。
分享一波阿里云代金券快速上云
转载请注明 from tomsun28