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;
    }
}

五、測試

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM