• RSA+Springboot+ThymeLeaf前端加密后台解密(密码加密传输)


    不多说废话,直接上代码,这个调通的整个过程还是花了半天时间的,参考了大佬们的博客,亲测可用

    参考资料:

    https://blog.csdn.net/qq_39027229/article/details/85003390
    https://blog.csdn.net/weixin_42127766/article/details/82802189
    https://blog.csdn.net/qq_39420411/article/details/94056654

    博客园、CSDN同步更新

    博客园:https://www.cnblogs.com/tangliping/p/14766846.html

    CSDN:https://blog.csdn.net/qq_37023928/article/details/116777630

    通过RSA实现密码加密传输,核心思路:

    • 点击登录,先请求后端,生成一对公私钥,将公钥返回给前台
    • 前台使用开源的jsencrypt.js对密码进行加密,加密后传输到后台
    • 后台对加密的密码进行解密

    一、项目依赖

    使用的是thymeleaf模板进行整合的。

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.8.RELEASE</version>
            <relativePath/>
        </parent>
        
         <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--StringUtils等工具类-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
            </dependency>
            <!--fastjson-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.76</version>
            </dependency>
            <!--thymeleaf启动器-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>        
         </dependencies>
       <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.4.5</version>
                </plugin>
            </plugins>
        </build>     
    

    二、前端代码

    获取开源的js文件:https://github.com/travist/jsencrypt

    js文件在上面开源项目的bin目录下。文件名称为:jsencrypt.js

    https://github.com/travist/jsencrypt/tree/master/bin

    获取到开源文件后,我们把它放在:static/js/

    templates/login.html文件:

    <!DOCTYPE html>
    <html lang="en"  xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    <script src="js/jsencrypt.js"></script>
    
    <body>
        <h1 th:text="${msg}">RSA测试</h1>
    <form>
        用户账号:
        <input type="text" name="username" id="username">
        <br>
        用户密码:
        <input type="text" name="password" id="password">
        <br>
        <input type="button" th:onclick="login()" th:value="提交">
    </form>
    </body>
    <script>
        function login() {
            var username = $('#username').val();
            var password = $('#password').val();
    
            var encrypt = new JSEncrypt();
    
            $.ajax({
                type: "get",  //提交方式
                url: "/getPublicKey",//访问路径
                contentType: 'application/json;charset=utf-8',//返回json结果
                success: function (data) {
                    console.log(data)
                    encrypt.setPublicKey(data)
                    var encryptPwd = encrypt.encrypt(password)
                    console.log("encryptPwd:"+encryptPwd)
                    $.ajax({
                        type: "post",  //提交方式
                        url: "/loginRequest",//访问路径
                        contentType: 'application/json;charset=utf-8',//返回json结果
                        data: JSON.stringify({"username":username,"password":encryptPwd}),
                        success: function (data) {
                            console.log(data)
    
                        }
                    });
                }
            });
    
            }
    </script>
    </html>
    

    三、RSA工具类

    /**
     * RSA工具类
     * 参考:https://blog.csdn.net/qq_39027229/article/details/85003390
     *      https://blog.csdn.net/weixin_42127766/article/details/82802189
     *      https://blog.csdn.net/qq_39420411/article/details/94056654
     * 备注,解密前台公钥加密的数据,请调用decryptWithPrivate方法。
     * 每次重启之后,都会生成一个一对新的公私钥
     */
    public class RSAUtil {
        //秘钥大小
        private static final int KEY_SIZE = 1024;
    
        //后续放到常量类中去
        public static final String PRIVATE_KEY = "privateKey";
        public static final String PUBLIC_KEY = "publicKey";
    
        private static KeyPair keyPair;
    
        private static Map<String,String> rsaMap;
    
        //生成RSA,并存放
        static {
            try {
                Provider provider =new org.bouncycastle.jce.provider.BouncyCastleProvider();
                Security.addProvider(provider);
                SecureRandom random = new SecureRandom();
                KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
                generator.initialize(KEY_SIZE,random);
                keyPair = generator.generateKeyPair();
                //将公钥和私钥存放,登录时会不断请求获取公钥,我们可以将其放到缓存中,而不放入数据库了
                //我在想,这个是不是有必要存放到Redis,在分布式场景中?
                //貌似有些必要,万一获取到的pubkey是server1中的,拿着server1的pubkey去server2去解密?
                storeRSA();
            } catch(NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 将RSA存入缓存
         */
        private static void storeRSA() {
            rsaMap = new HashMap<>();
            PublicKey publicKey = keyPair.getPublic();
            String publicKeyStr = new String(Base64.encodeBase64(publicKey.getEncoded()));
            rsaMap.put(PRIVATE_KEY, publicKeyStr);
    
            PrivateKey privateKey = keyPair.getPrivate();
            String privateKeyStr = new String(Base64.encodeBase64(privateKey.getEncoded()));
            rsaMap.put(PUBLIC_KEY, privateKeyStr);
        }
    
        /**
         * 私钥解密(解密前台公钥加密的密文)
         *
         * @param encryptText 公钥加密的数据
         * @return 私钥解密出来的数据
         * @throws Exception e
         */
        public static String decryptWithPrivate(String encryptText) throws Exception {
            if(StringUtils.isBlank(encryptText)){
                return null;
            }
            byte[] en_byte = Base64.decodeBase64(encryptText.getBytes());
            //byte[] en_byte = Hex.decode(encryptText);
            Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
            Security.addProvider(provider);
            //之前我只写了RSA,出现了乱码+明文 参考:https://blog.csdn.net/qq_39420411/article/details/94056654
            Cipher ci = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
            PrivateKey privateKey = keyPair.getPrivate();
            ci.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] res = ci.doFinal(en_byte);
            return new String(res);
        }
    
        /**
         * java端 使用公钥加密(此方法暂时用不到)
         * @param plaintext 明文内容
         * @return byte[]
         * @throws UnsupportedEncodingException e
         */
        public  static byte[] encrypt(String plaintext) throws UnsupportedEncodingException {
            String encode = URLEncoder.encode(plaintext, "utf-8");
            RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
            //获取公钥指数
            BigInteger e = rsaPublicKey.getPublicExponent();
            //获取公钥系数
            BigInteger n = rsaPublicKey.getModulus();
            //获取明文字节数组
            BigInteger m = new BigInteger(encode.getBytes());
            //进行明文加密
            BigInteger res = m.modPow(e, n);
            return res.toByteArray();
    
        }
    
        /**
         * java端 使用私钥解密(此方法暂时用不到)
         * @param cipherText 加密后的字节数组
         * @return 解密后的数据
         * @throws UnsupportedEncodingException e
         */
        public static String decrypt(byte[] cipherText) throws UnsupportedEncodingException {
            RSAPrivateKey prk = (RSAPrivateKey) keyPair.getPrivate();
            // 获取私钥参数-指数/系数
            BigInteger d = prk.getPrivateExponent();
            BigInteger n = prk.getModulus();
            // 读取密文
            BigInteger c = new BigInteger(cipherText);
            // 进行解密
            BigInteger m = c.modPow(d, n);
            // 解密结果-字节数组
            byte[] mt = m.toByteArray();
            //转成String,此时是乱码
            String en = new String(mt);
            //再进行编码,最后返回解密后得到的明文
            return URLDecoder.decode(en, "UTF-8");
        }
    
        /**
         * 获取公钥
         * @return 公钥
         */
        public static String getPublicKey(){
            return rsaMap.get(PUBLIC_KEY);
        }
    
        /**
         * 获取私钥
         * @return 私钥
         */
        public static String getPrivateKey(){
            return  rsaMap.get(PRIVATE_KEY);
        }
    
        public static void main(String[] args) throws UnsupportedEncodingException {
            System.out.println(RSAUtil.getPrivateKey());
            System.out.println(RSAUtil.getPublicKey());
            byte[] usernames = RSAUtil.encrypt("username66");
            System.out.println(RSAUtil.decrypt(usernames));
        }
    }
    

    四、控制层

    @RestController
    public class RSAController {
    
        @RequestMapping("/getPublicKey")
        public String getPublicKey(){
            return RSAUtil.getPublicKey();
        }
    }
    
    @Controller
    public class LoginController {
    
        @RequestMapping("/login")
        public String login(Model model){
            model.addAttribute("msg", "RSA前端加密,后端解密测试");
            return "login";
        }
    
        @RequestMapping(value = "/loginRequest",method= RequestMethod.POST)
        @ResponseBody
        public String loginRequest(@RequestBody JSONObject json){
            String username = json.getString("username");
            String password = json.getString("password");
            System.out.println(username);
            System.out.println(password);
    
            String res = null;
            try {
                res = RSAUtil.decryptWithPrivate(password);
                //这里就是解密后的密码了
                System.out.println(res);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return res;
        }
    }
    

    五、测试

    好了,上述结果就是解密成功的结果了。

  • 相关阅读:
    年度总结会议
    2018年度移动创新竞赛组总结
    移动竞赛组2019计划
    task ':app:mergeDebugResources' property 'aapt2FromMaven'
    SQL SERVER : 'GO' 附近有语法错误问题
    Visual C# 访问 SQLserver 数据库
    数据库系统概论 第三章 课后作业
    数据库系统概论 第三章 建表&插入SQL语句
    【pyqt5 QtDesigner SQLserver2017】 Python3.6 Demo
    【Win10 + PyCharm + Python + PyQt5 + pymssql】 Python 3.6 访问 SQLserver 2017(对话框界面)1
  • 原文地址:https://www.cnblogs.com/tangliping/p/14766846.html
Copyright © 2020-2023  润新知