使用RSA非对称加密算法对密码进行加密,能够保证传输数据的安全性。RSA公钥和私钥在服务器端生成,并且把私钥保存到服务器,把公钥的模数和指数传递给前端。前端根据模数和指数对密码进行加密,将密码密文传递给服务器。服务器根据私钥对密文进行解密,最后完成登录验证。本文主要介绍:“前端登录页面HTML代码”、“前端请求后端,获取RSA公钥的模数和指数”、“后端生成RSA公钥的模数和指数”、“将公钥的模数和指数返回给前端”、“前端根据公钥的模数和指数对密码进行加密”、“前端将手机号和密码密文传递给后端”、“后端使用私钥对密码密文进行解密”、“后端验证账号密码是否正确,完成登录验证”。
1、前端登录页面HTML代码。
<div id="loginMain">
<div id="loginPic">
<img src="../../images/login.png" alt="">
</div>
<div id="loginInfomation" class="layui-form">
<p>
<img src="../../images/name.png" alt="">
</p>
<h4 style="line-height:50px;">欢迎登录博销宝管理后台</h4>
<h4 style="font-size:15px; line-height:16px; margin-bottom: 15px;">v1.0.0</h4>
<div class="layui-form-item">
<label class="layui-form-label"><strong
class="requiredField">*</strong>公司编号:</label>
<div class="layui-input-block">
<input type="text" class="layui-input"
name="${staffField.FIELD_NAME_companySN}" lay-verify="companySN"
placeholder="请输入公司编号" maxlength="8" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><strong
class="requiredField">*</strong>手机号码:</label>
<div class="layui-input-block">
<input type="text" class="layui-input"
name="${staffField.FIELD_NAME_phone}" lay-verify="phone"
maxlength="11" placeholder="请输入手机号码" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><strong
class="requiredField">*</strong>密码:</label>
<div class="layui-input-block">
<input type="password" class="layui-input"
name="${staffField.FIELD_NAME_pwdEncrypted}" lay-verify="password"
maxlength="16" placeholder="请输入密码" />
</div>
</div>
<p>
<button class="layui-btn layui-btn-lg layui-btn-normal" lay-submit
lay-filter="login">登录</button>
</p>
<p>
<img src="../../images/logoName.png" alt="">
</p>
<input type="hidden" id="CURRENT_ReleaseNbrVersionNO" value="2.0.0" /><br>
</div>
</div>
2、前端请求后端,获取RSA公钥的模数和指数。
将手机号作为参数传递给后端:
//登录系统
form.on("submit(login)", function(data){
var loading = layer.load(1);
var loginInfo = data.field;
var password = loginInfo.pwdEncrypted;
$.ajax({
url: staffGetToken_url,
type: method_post,
dataType: "JSON",
data: {"phone": loginInfo.phone},
cache: false,
async: true,
success : function(data){
var modulus = data.rsa.modulus;
var exponent = data.rsa.exponent;
var rsa = new RSAKey();
rsa.setPublic(modulus, exponent);
var res = rsa.encrypt(password);
if(res){
loginInfo.pwdEncrypted = res;
$.ajax({
url: staffLogin_url,
type: method_post,
async: true,
dataType: "JSON",
data: loginInfo,
success: function(data){
layer.close(loading);
if(data){
if(data.ERROR != "EC_NoError"){
if(data.msg){
layer.msg(data.msg);
}else{
layer.msg("手机号码或密码错误");
}
isToSend = true;
return;
}
window.location.href = "../home.bx";
}else{
isToSend = true;
layer.msg("登录失败,公司编号错误");
}
},
error: function(){
isToSend = true;
layer.close(loading);
layer.msg("服务器错误");
}
});
}else{
isToSend = true;
layer.close(loading);
layer.msg("登录失败");
}
},
error: function(){
isToSend = true;
layer.close(loading);
layer.msg("服务器错误");
}
});
return false;
})
3、后端生成RSA公钥的模数和指数。
(1)将生成的modulus模数和exponent指数放在RSAInfo类:
RSAInfo rsa = generateRSA(staff.getPhone());
public RSAInfo generateRSA(String id) throws Exception {
HashMap<String, Object> map = RSAUtils.getKeys();
RSAPublicKey publicKey = (RSAPublicKey) map.get("public");
mapRSA.put(id, map);
String modulus = publicKey.getModulus().toString(16);
String exponent = publicKey.getPublicExponent().toString(16);
RSAInfo rsa = new RSAInfo();
rsa.setExponent(exponent);
rsa.setModulus(modulus);
return rsa;
}
(2)生成RSA公钥和私钥,放在hashmap中:
/**
* 生成公钥和私钥
*
* @throws NoSuchAlgorithmException
*
*/
public static HashMap<String, Object> getKeys() throws NoSuchAlgorithmException {
HashMap<String, Object> map = new HashMap<String, Object>();
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
map.put("public", publicKey);
map.put("private", privateKey);
return map;
}
(3)将公钥和私钥信息以手机号为key保存到hashmap缓存,用于后面的解密:
HashMap<String, Object> map = RSAUtils.getKeys();
RSAPublicKey publicKey = (RSAPublicKey) map.get("public");
mapRSA.put(id, map);
(4)获取公钥的模数和指数:
String modulus = publicKey.getModulus().toString(16);
String exponent = publicKey.getPublicExponent().toString(16);
RSAInfo rsa = new RSAInfo();
rsa.setExponent(exponent);
rsa.setModulus(modulus);
4、将公钥的模数和指数返回给前端。
params.put("rsa", rsa);
params.put(BaseAction.JSON_ERROR_KEY, EnumErrorCode.EC_NoError.toString());
params.put(KEY_HTMLTable_Parameter_msg, staffBO.getLastErrorMessage());
logger.info("返回的数据=" + params);
return JSONObject.fromObject(params, JsonUtil.jsonConfig).toString();
5、前端根据公钥的模数和指数对密码进行加密。
获取模数和指数:
success : function(data){
var modulus = data.rsa.modulus;
var exponent = data.rsa.exponent;
var rsa = new RSAKey();
rsa.setPublic(modulus, exponent);
var res = rsa.encrypt(password);
对密码进行加密:
// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;
}
6、前端将手机号和密码密文传递给后端。
if(res){
loginInfo.pwdEncrypted = res;
$.ajax({
url: staffLogin_url,
type: method_post,
async: true,
dataType: "JSON",
data: loginInfo,
success: function(data){
layer.close(loading);
if(data){
if(data.ERROR != "EC_NoError"){
if(data.msg){
layer.msg(data.msg);
}else{
layer.msg("手机号码或密码错误");
}
isToSend = true;
return;
7、后端使用私钥对密码密文进行解密。
根据手机号key找到对应的私钥进行解密:
String pwd = decrypt(((BaseAuthenticationModel) bmIn).getKey(), sPasswordEncrypted);
if (pwd == null) {
return null;
}
解密密文:
public String decrypt(String key, String sPasswordEncrypted) {
lastErrorCode = ErrorInfo.EnumErrorCode.EC_NoError;
lastErrorMessage = "";
HashMap<String, Object> map = mapRSA.get(key);
if (map == null) {
return null;
}
RSAPrivateKey privateKey = (RSAPrivateKey) map.get("private");
//
String pwd = "";
try {
pwd = RSAUtils.decryptByPrivateKey(sPasswordEncrypted, privateKey);
if (!FieldFormat.checkRawPassword(pwd)) {
lastErrorCode = ErrorInfo.EnumErrorCode.EC_WrongFormatForInputField;
lastErrorMessage = FieldFormat.FIELD_ERROR_Password;
logger.info(FieldFormat.FIELD_ERROR_Password);
return null;
}
// logger.info("String decrypted=" + pwd);
} catch (Exception e) {
logger.info(e);
lastErrorCode = EnumErrorCode.EC_Hack;
return null;
}
return pwd;
}
8、后端验证账号密码是否正确,完成登录验证。
DataSourceContextHolder.setDbName(dbName);
BaseModel bm = retrieve1Object(BaseBO.SYSTEM, iUseCaseID, bmIn);
String md5 = MD5Util.MD5(pwd + BaseAction.SHADOW);
if (md5 == null) {
lastErrorCode = ErrorInfo.EnumErrorCode.EC_OtherError;
return null;
}
if (md5.equals(((BaseAuthenticationModel) bm).getSalt())) {
return (BaseAuthenticationModel) bm;
} else {
lastErrorCode = ErrorInfo.EnumErrorCode.EC_NoSuchData;
}