This is the fifth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

preface

This section introduces the SysProfileController section of the ruoyi-Admin module of Ruoyi-Vue. So let’s talk about resetting the password and how the foreground password is going to be passed to the background.

/** * reset password */
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword) {
    LoginUser loginUser = getLoginUser();
    String userName = loginUser.getUsername();
    String password = loginUser.getPassword();
    if(! SecurityUtils.matchesPassword(oldPassword, password)) {return AjaxResult.error("Failed to change password. Old password is incorrect.");
    }
    if (SecurityUtils.matchesPassword(newPassword, password)) {
        return AjaxResult.error("New password cannot be the same as old password");
    }
    if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) {
        // Update the cache user password
        loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
        tokenService.setLoginUser(loginUser);
        return AjaxResult.success();
    }
    return AjaxResult.error("Failed to change password. Please contact administrator.");
}
Copy the code

Here we can see the incoming is the password for user input, the front there is no operation, no encryption or hash, but in the background. Only use the SecurityUtils encryptPassword on the user’s password encryption.

This raises an interesting question: How should the foreground password be passed to the background?

How does the foreground password pass to the background

How to transfer the foreground password to the background is a very interesting question. There are many methods, including direct transfer to the back end, md5 encryption to the background, and RSA encryption to the back end decryption

directly

Directly plaintext password is one of the most simple method, but also can bring certain risks at the same time, if you are not using HTTPS, so for all the people on the path of your password is transparent, even though the site may not have what important role, a malicious person also can use this password to other websites of library operation. It is also possible to print out your password when printing a log. After all, as in Ruoyi, the password is directly put into the URL. If Nginx logs access, then your password is at risk of leakage.

MD5 digest algorithm

We use Hutool to simulate the existing MD5 password of the back-end database.

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.15</version>
</dependency>
Copy the code

Front end to use MD5js we use BlueIMP – MD5

https://www.bootcdn.cn/blueimp-md5/
<script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.18.0/js/md5.js"></script>
Copy the code

Let’s set up a front-end HTML that does MD5 once for the password we want to pass to the background, or MD5 twice for security.

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">   
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.18.0/js/md5.js"></script>
</head>
<body>
<button>Md5 Sending request</button>
<script>
    $("button").click(function () {
        var password = md5("123456");
        $.ajax({
            type: "POST".url: "/api/testMd5".data: {password: password}
        }).done(function (msg) {
            alert("Data Saved: " + msg);
        });
    });
</script>

</body>
</html>
Copy the code

The back end uses the interface to receive and compare with the data after md5 on the back end. Here we use Hutool’s digestUtil.md5HEX method.

@PostMapping("/testMd5")
public boolean testMd5(@RequestParam("password") String password) {
    String backPassword = DigestUtil.md5Hex("123456");
    if (backPassword.equals(password)) {
        return true;
    } else {
        return false; }}Copy the code

The advantage of using MD5 is that it is simple and convenient, but the disadvantage is that we can’t know whether the password used by the user meets our requirements in the back end. We can only verify it in the front end, because we can’t know what the user typed after MD5. In addition, the problem brought by MD5 is that if the user uses a common password, it may be bumped out, which brings a hidden danger to the security of the user’s other websites.

RSA encryption and decryption

Front end we encrypt RSAjs using jsENCRYPT

https://www.bootcdn.cn/jsencrypt/
<script src="https://cdn.bootcdn.net/ajax/libs/jsencrypt/3.2.1/jsencrypt.js"></script>
Copy the code

Website is travistidwell.com/jsencrypt/ we first need to generate the RSA public key and key can use www.metools.info/code/c80.ht… To generate

The length I chose is 1024 and the format is PKCS#8, which adds the validation data section to ensure the key is correct.

Front-end public key encryption

To perform RSA encryption, we want to expose a public key in the front end, so we use a fixed public key for simplicity.

Jsencrypt will generate a string of Base64. As you can see from the parameter comment in the decryption part of the code, the string of Base64 is converted to hex.

var encrypt = new JSEncrypt();
debugger
encrypt.setPublicKey($("#publicKey").html());
var encrypted = encrypt.encrypt("123456");
Copy the code

Using the above code, we have encrypted the password with a public key, and now it is sent to the background for decryption.

Background private key decryption

Background decryption we also use Hutool originally I thought it would be very simple, because the front end according to the public key encryption has been completed, Hutool also gave the private key decryption example, just copy it.

But here’s the bad partHexUtil.decodeHexHere it is, here it isString aIt’s actually a hexadecimal string, so I’m passing in a Base64 string, which I started with

byte[] aByte = HexUtil.decodeHex(Base64.decodeStr(password));
Copy the code

DecodeStr is not a hexadecimal string, HexUtil. DecodeHex is not a byte array, but I thought it was incompatible without annotations, so I tried a lot of different things until I traced the decryption part of the front-end JS code

Let’s take the values in ret asHexUtil.decodeHexBase64tohex = base64Tohex = base64Tohex = base64Tohex = base64Tohex = base64Tohex

byte[] decoded = Base64.getDecoder().decode(password);
Copy the code

In this way, our RSA encryption and decryption is complete! The backend can know whether the password entered by the user meets our requirements of the password format and strength, while in the process of transmission will not be leaked.