1. 程式人生 > >前端登錄密碼加密傳輸

前端登錄密碼加密傳輸

ted ima 直接 decrypt service success .get solid session

目的:防止登錄密碼名文傳輸(僅僅只是防止明文傳輸,加密效果取決於key,而key對於前臺是透明的)

方式:前端頁面用js加密前端登錄密碼,采用AES對稱加密

一、前端JS加密庫crypto-js

因為懶,所以直接引入整個加密庫,可以根據所需要的加密算法分別引入,下載地址crypto-js

如果是分別引入,記得別忘記引入核心庫core

二、前端頁面代碼

因為需要加密密碼,所以把提交方式換成了ajax

AES加密的key為128bit,可以理解為16個字符。key由後端生成並送入前端。

<!DOCTYPE html>
<html lang="en"
      xmlns:th
="http://www.w3.org/1999/xhtml" > <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="../../static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}"
> <script src="../../static/js/jquery-3.3.1.min.js" th:src="@{/js/jquery-3.3.1.min.js}"></script> <script src="../../static/js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script> <script src="../../static/js/crypto-js.js" th:src="@{/js/crypto-js.js}"></script>
<title>用戶登錄</title> <style> .bgColor { background-color: rgba(243, 66, 111, 0.15) } .divBorder { border: solid 1px rgba(12, 24, 255, 0.15); padding: 10px; margin-top: 10px; border-radius: 10px; text-align: center; vertical-align: middle; } .h4font { margin-top: 0px; font-family: 微軟雅黑; font-weight: 500; } .center { padding: 20% 0; } .verifyInput { vertical-align: middle; font-size: 14px; font-weight: normal; line-height: 1; /*border:1px solid #999;*/ float: left; width: 180px; height: 30px; } .verifyImage { vertical-align: middle; float: right; height: 30px; } </style> </head> <body> <div class="container"> <div class="row center"> <div class="divBorder col-sm-offset-4 col-sm-4"> <h3 class="panel panel-heading h4font"> 用戶登錄 </h3> <h4 name="msg" th:text="${msg}"></h4> <input type="hidden" name="key" th:value="${key}"> <!--<form class="form-horizontal" th:action="@{/login}" method="post">--> <form class="form-horizontal" method="post"> <div class="input-group"> <span class="input-group-addon"><i class="glyphicon glyphicon-user" aria-hidden="true"></i></span> <input type="text" class="form-control" name="userName" placeholder="請輸入用戶名稱" th:value="${userName}"/> </div> <br> <div class="input-group"> <span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span> <input type="password" class="form-control" name="password" th:value="${password}" placeholder="請輸入密碼"/> </div> <br/> <div class="input-group"> <span class="input-group-addon"><i class="glyphicon glyphicon-font"></i></span> <input type="text" class="verifyInput" name="verifyCode" placeholder="驗證碼"/> <img class="verifyImage" alt="驗證碼" onclick="this.src=‘/getVerifyCode?d=‘+new Date()*1" src="/getVerifyCode"> </div> <br> <input type="button" name="btnLogin" class="btn btn-lg btn-block btn-info" value="登 錄"> </form> </div> </div> </div> <script th:inline="javascript"> $(function () { $(input[name="btnLogin"]).click(function () { var $key = $(input[name="key"]).val(); var $userName = $(input[name="userName"]).val(); var $password = $(input[name="password"]).val(); var $verifyCode = $(input[name="verifyCode"]).val(); // console.log($userName + ", " + $password + ", " + $verifyCode + ", " + $key); if ($userName == "" || $password == "" || $verifyCode == "") { alert("用戶名、密碼、驗證碼不能為空!"); return false; } var key = CryptoJS.enc.Utf8.parse($key); console.log("key:" + key + ",$key:" + $key); var password = CryptoJS.enc.Utf8.parse($password); var encrypted = CryptoJS.AES.encrypt(password, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}); var encryptedPwd = encrypted.toString(); // console.log("encrypted:" + encrypted); // console.log("encryptedPwd:" + encryptedPwd); var decrypt = CryptoJS.AES.decrypt(encryptedPwd, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); var testDecryptStr = CryptoJS.enc.Utf8.stringify(decrypt).toString(); // console.log("decrypt:" + decrypt); // console.log("testDecryptStr:" + testDecryptStr); $.ajax({ type: "post", url: "/login", data: {userName: $userName, password: encryptedPwd, verifyCode: $verifyCode}, dataType: "json", success: function (result) { // console.log(result); if(result.success) { window.location.href=result.url; } else { $(h4[name="msg"]).html(result.msg); alert(result.msg); } // window.location.replace(result.url); // $(‘#container‘).load(result.url); }, error: function (result) { // console.log(result); alert(result.responseText); } }); }) }) </script> </body> </html>

三、後端解密代碼

後端PKCS5Padding補碼方式和前端的CryptoJS.pad.Pkcs7效果一致。

public class AesUtils {

    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";

    private static String base64Encode(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    private static byte[] base64Decode(String base64Code) throws Exception {
        return new BASE64Decoder().decodeBuffer(base64Code);
    }

    private static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));

        return cipher.doFinal(content.getBytes("utf-8"));
    }

    public static String encrypt(String content, String encryptKey) throws Exception {
        return base64Encode(aesEncryptToBytes(content, encryptKey));
    }

    private static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);

        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
        byte[] decryptBytes = cipher.doFinal(encryptBytes);

        return new String(decryptBytes);
    }

    public static String decrypt(String encryptStr, String decryptKey) throws Exception {
        return aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
    }

}

四、controller貼一把

@Controller
public class HomeController {
    @Resource
    private LoginService loginService;

    @Resource
    DefaultKaptcha defaultKaptcha;

    @Resource
    LogService logService;

    private long verifyTTL = 60;//驗證碼過期時間60秒

    private char[] key16; //保存密鑰

    private String create16String()
    {
        return RandomUtils.generateString(16);
    }

    @RequestMapping({"/", "/index"})
    public String index() {
        return "index";
    }


    /**
     * 2、生成驗證碼
     *
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping("/getVerifyCode")
    public void defaultKaptcha(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
       略...   
    }


    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String toLogin(Map<String, Object> map, HttpServletRequest request) {
        String key = create16String();
        key16 = key.toCharArray();
        map.put("key",key);
        return "/user/login";
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public Object login(HttpServletRequest request) throws Exception {
        System.out.println("login()");
        Map<String, Object> map = new HashMap<>();
        String userName = request.getParameter("userName");
        String encryptedPassword = request.getParameter("password");
//        String key = request.getParameter("key");
        String key = new String(key16);


        String verifyCode = request.getParameter("verifyCode");
        String rightCode = (String) request.getSession().getAttribute("verifyCode");
        Long verifyCodeTTL = (Long) request.getSession().getAttribute("verifyCodeTTL");

        String password = AesUtils.decrypt(encryptedPassword,key);

        Long currentMillis = System.currentTimeMillis();
        if (rightCode == null || verifyCodeTTL == null) {
            map.put("msg", "請刷新圖片,輸入驗證碼!");
            map.put("userName", userName);
            map.put("success",false);
            map.put("url","/user/login");
            return map;
        }
        Long expiredTime = (currentMillis - verifyCodeTTL) / 1000;
        if (expiredTime > this.verifyTTL) {
            map.put("msg", "驗證碼過期,請刷新圖片重新輸入!");
            map.put("userName", userName);
            map.put("success",false);
            map.put("url","/user/login");
            return map;
        }

        if (!verifyCode.equalsIgnoreCase(rightCode)) {
            map.put("msg", "驗證碼錯誤,請刷新圖片重新輸入!");
            map.put("userName", userName);
            map.put("success",false);
            map.put("url","/user/login");
            return map;
//            return "/user/login";
        }

        LoginResult loginResult = loginService.login(userName, password);
        if (loginResult.isLogin()) {
            map.put("userName", userName);
            SysLog sysLog = LogFactory.createSysLog("登錄","登錄成功");
            logService.writeLog(sysLog);
            map.put("success",true);
            map.put("url","/index");
            return map;
//            return "/index";
        } else {
            map.put("msg", loginResult.getResult());
            map.put("userName", userName);
            map.put("success",false);
            map.put("url","/user/login");
            return map;
//            return "/user/login";
        }
    }


}

前端登錄密碼加密傳輸