zoukankan      html  css  js  c++  java
  • 前端登录密码加密传输

    在HTTPS还没有普及的时候,前端采用HTTP协议,登录用户名和密码在不做任何控制的情况下是明文传输的,大量的网站都需要登录,大量的人使用同样的用户名和密码。

    目的:防止登录密码名文传输(仅仅只是防止明文传输,加密效果取决于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,key: $key},
    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";
    
        public static String encrypt(String content, String key) {
            try {
                byte[] raw = key.getBytes();  //获得密码的字节数组
                SecretKeySpec skey = new SecretKeySpec(raw, "AES"); //根据密码生成AES密钥
                Cipher cipher = Cipher.getInstance(ALGORITHMSTR);  //根据指定算法ALGORITHM自成密码器
                cipher.init(Cipher.ENCRYPT_MODE, skey); //初始化密码器,第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二个参数为生成的AES密钥
                byte [] byte_content = content.getBytes("utf-8"); //获取加密内容的字节数组(设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
                byte [] encode_content = cipher.doFinal(byte_content); //密码器加密数据
                return Base64.encodeBase64String(encode_content); //将加密后的数据转换为字符串返回
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public static String decrypt(String encryptStr, String decryptKey) {
            try {
                byte[] raw = decryptKey.getBytes();  //获得密码的字节数组
                SecretKeySpec skey = new SecretKeySpec(raw, "AES"); //根据密码生成AES密钥
                Cipher cipher = Cipher.getInstance(ALGORITHMSTR);  //根据指定算法ALGORITHM自成密码器
                cipher.init(Cipher.DECRYPT_MODE, skey); //初始化密码器,第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作,第二个参数为生成的AES密钥
                byte [] encode_content = Base64.decodeBase64(encryptStr); //把密文字符串转回密文字节数组
                byte [] byte_content = cipher.doFinal(encode_content); //密码器解密数据
                return new String(byte_content,"utf-8"); //将解密后的数据转换为字符串返回
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }

    四、controller贴一把

    @Controller
    public class HomeController {
        @Resource
        private LoginService loginService;
    
        @Resource
        DefaultKaptcha defaultKaptcha;
    
        @Resource
        LogService logService;
    
        private long verifyTTL = 60;//验证码过期时间60秒
    
        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();        
            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 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";
            }
        }
    }
  • 相关阅读:
    Chrome浏览器设置默认编码
    linux上安装subversion
    详解Linux命令行下常用svn命令
    css 使容器宽度适应内容宽
    Windsor Spring
    T4 Generate POCO Class for MSSQ
    MSSQ 树型结构数据 循环操作
    System.Reflection.Emit 动态实现接口
    T4 SqlSugar MySql
    微信多开
  • 原文地址:https://www.cnblogs.com/asker009/p/10018037.html
Copyright © 2011-2022 走看看