Skip to content

Commit bfcf61d

Browse files
committedMar 24, 2025
Add RSA encryption and decryption for password field
1 parent f754f4f commit bfcf61d

File tree

5 files changed

+129
-10
lines changed

5 files changed

+129
-10
lines changed
 

‎tang-admin/src/main/java/com/tang/admin/controller/#Controller.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package com.tang.admin.controller;
22

3-
import java.util.Base64;
43
import java.util.Map;
54

5+
import com.tang.commons.utils.RsaUtils;
66
import org.springframework.web.bind.annotation.GetMapping;
77
import org.springframework.web.bind.annotation.PostMapping;
88
import org.springframework.web.bind.annotation.RequestBody;
99
import org.springframework.web.bind.annotation.RestController;
1010

1111
import com.tang.commons.model.LoginModel;
1212
import com.tang.commons.utils.AjaxResult;
13-
import com.tang.commons.utils.ByteUtils;
1413
import com.tang.commons.utils.SecurityUtils;
1514
import com.tang.framework.web.service.LoginService;
1615
import com.tang.system.service.SysMenuService;
@@ -42,7 +41,7 @@ public LoginController(LoginService loginService, SysMenuService menuService) {
4241
*/
4342
@PostMapping("/#")
4443
public AjaxResult login(@Valid @RequestBody LoginModel loginModel) {
45-
loginModel.setPassword(ByteUtils.byteToHex(Base64.getDecoder().decode(loginModel.getPassword())));
44+
loginModel.setPassword(RsaUtils.decrypt(loginModel.getPassword()));
4645
var token = loginService.login(loginModel);
4746
return AjaxResult.success(Map.of("token", token));
4847
}

‎tang-admin/src/main/java/com/tang/admin/controller/ProfileController.java

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Objects;
44

5+
import com.tang.commons.utils.RsaUtils;
56
import org.springframework.beans.BeanUtils;
67
import org.springframework.security.access.prepost.PreAuthorize;
78
import org.springframework.web.bind.annotation.GetMapping;
@@ -73,6 +74,9 @@ public AjaxResult editInfo(@RequestBody SysUser user) {
7374
@PreAuthorize("@auth.hasPermission('system:user:edit')")
7475
@PutMapping("/edit-password")
7576
public AjaxResult editPassword(@Valid @RequestBody PasswordVo passwordVo) {
77+
passwordVo.setOldPassword(RsaUtils.decrypt(passwordVo.getOldPassword()));
78+
passwordVo.setNewPassword(RsaUtils.decrypt(passwordVo.getNewPassword()));
79+
passwordVo.setConfirmPassword(RsaUtils.decrypt(passwordVo.getConfirmPassword()));
7680
return AjaxResult.rows(userService.updatePasswordByUserId(passwordVo));
7781
}
7882

‎tang-commons/src/main/java/com/tang/commons/domain/vo/PasswordVo.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.tang.commons.domain.vo;
22

3-
import org.hibernate.validator.constraints.Length;
4-
53
import jakarta.validation.constraints.NotBlank;
64

75
/**
@@ -20,19 +18,19 @@ public class PasswordVo {
2018
/**
2119
* 旧密码
2220
*/
23-
@Length(min = 4, max = 32, message = "旧密码长度应在 4 到 32 之间")
21+
@NotBlank(message = "密码不能为空")
2422
private String oldPassword;
2523

2624
/**
2725
* 新密码
2826
*/
29-
@Length(min = 4, max = 32, message = "新密码长度应在 4 到 32 之间")
27+
@NotBlank(message = "密码不能为空")
3028
private String newPassword;
3129

3230
/**
3331
* 确认密码
3432
*/
35-
@Length(min = 4, max = 32, message = "确认密码长度应在 4 到 32 之间")
33+
@NotBlank(message = "密码不能为空")
3634
private String confirmPassword;
3735

3836

‎tang-commons/src/main/java/com/tang/commons/model/#Model.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class LoginModel implements Serializable {
2020
/**
2121
* 用户名
2222
*/
23-
@Length(min = 4, max = 32, message = "用户名长度应在 4 到 32 之间")
23+
@Length(min = 2, max = 32, message = "用户名长度应在 2 到 32 之间")
2424
private String username;
2525

2626
/**
@@ -33,7 +33,7 @@ public class LoginModel implements Serializable {
3333
/**
3434
* 密码
3535
*/
36-
@Length(min = 4, max = 64, message = "密码长度应在 4 到 64 之间")
36+
@NotBlank(message = "密码不能为空")
3737
private String password;
3838

3939
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.tang.commons.utils
2+
3+
import java.security.KeyFactory
4+
import java.security.KeyPairGenerator
5+
import java.security.spec.MGF1ParameterSpec
6+
import java.security.spec.PKCS8EncodedKeySpec
7+
import java.security.spec.X509EncodedKeySpec
8+
import javax.crypto.Cipher
9+
import javax.crypto.spec.OAEPParameterSpec
10+
import javax.crypto.spec.PSource
11+
import kotlin.io.encoding.Base64
12+
import kotlin.io.encoding.ExperimentalEncodingApi
13+
14+
/**
15+
* RSA 加密解密工具类
16+
*
17+
* @author Tang
18+
*/
19+
object RsaUtils {
20+
21+
private const val PRIVATE_KEY =
22+
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0dgj9afv1UTRGTgDIVy" +
23+
"dSgV04vfaIwcGpQ7jxvQXPdK2VjlbcRoIhza6cWjs/WbjlTISoI5Tl2vQdrEXdnMq4o3YK" +
24+
"QppJMWCtL8O8g5oi2FI5lPLVduQFoPR9Ist6eyTytQSfPm164JDPBZQVXSnwor0oKaGXUc" +
25+
"bvgYfhbOmzQjzdnamxRte/nBcbs+7qEKSw5NRlXT36PK9+O8bTaxd/Br3SXsMSmBH0i+Gg" +
26+
"0DjbXfshimZInFYiiYljp4nobYPo/cqxv6TpsktOYvuETlmVovuC83XBT3CrFWt+gazcDh" +
27+
"eGvQqevLqh6tIxm2zdPMmnjMQNw3Ybs+NYa9eY+E+1AgMBAAECggEAH0wRRxHodqU8say5" +
28+
"igVDdpWk+0BGz3T7B0YNfy9PIKmVqUhkXBOGYiJv1AH6IISJAouAvkBdhHeyuqqz+zlEFz" +
29+
"2SLTlb1LHRmHeeNMWGJ+DoccAZVVgnN6qwfDtamsVcpRMr2ApVpmfn9V98TTA5I9i1gY+m" +
30+
"zL2MCOGoFTp0VXguWuSa1nZTfoSg1uG2vk1+OrxWYxWR4BjX6fTg/qPZ2Hvvn6XHdooNwr" +
31+
"7m2CaDcX5oTgpSVfo/7HVebrOs+V1PKwuyVzpcQ9udNZabxPjsN2A8wwtq0RYW/9GL0DdN" +
32+
"BegLwp4mMMhaPhs0kuXRr6Xxa0u7pzCRb7K6XOK1uOFKiQKBgQDwmQgtQq//un2l3VjE2D" +
33+
"qY53kkuviMsrNLWYsxv5uDVvppRlsaQp0wUcsAGWGbS1Dt0z2TTDwRzN44Bn2kxfve7sHA" +
34+
"+KDA5T/Iqlfu3+zl6bRQwCUOqAibmFkMwagKoYB7p2+ao+Z2uGYRG1tuvTnwGGNiClxtUf" +
35+
"cfaqDrap4xewKBgQDAA3hNSge7Tmgyuto/06qb7k0TAaZNx2hqs71xugONNjKeEol3y2Vo" +
36+
"8BVlzqpPuNJQ0hepIXs1ALopxC7P+/e8e9v3o3uEmoufTvRFL2EFB8Wr5z61BJ/6SKT0QL" +
37+
"i4xRevLq07v/LkUBoomgwtB9RWchSj2cY/saI7RB92H4JEjwKBgCUBZKCRgUB0Dp9UTDL6" +
38+
"jwi1kYx0tYXudmVAgIhGUEUDO8C1cY24cTTdX7vEK58XFnt94hqlvxd9yzASz4BoczT2xB" +
39+
"ZKJ2+D0yuqB5xWFLnIGFPTOd/nOGu2IvwzTQVVkc1zE1dVnjzkX86Bxq5hrGehWKfbsBug" +
40+
"X8IVRGrxGyPBAoGAfa6HwwdO8kJLH9GAY8DXboNXvbYZtdVtOlJ2EQexpW+xSBhYFKp0sX" +
41+
"BcgSv5/H68YxxxUkpRDAtyzz3Tal3B9YSZIYnHoq9J7rfOWa6+cX153KBbQj9Ju5hrKFlo" +
42+
"z8BqVUdXKsHkaZ8o0CStDZiPWxoG+ozkH/LUfriDY10Sdm8CgYEAvY6tcfAKCWS+DDAvKa" +
43+
"99Gkdzzw8hOc1jrml/GNGlAlYJyB1ZOwHWySLpwMIIsYZ8mcG1cz4JWhbQEMAXQqMo1bL7" +
44+
"b9gBQIWIYgA64kIKyW8rUc15wWN/kTEgGJ9K6LrOgk4eWiom4iQWrP/9yrtdJVJcGtff2o" +
45+
"YCqdG2v1Isfu8="
46+
47+
private const val PUBLIC_KEY =
48+
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtHYI/Wn79VE0Rk4AyFcnUoFdOL" +
49+
"32iMHBqUO48b0Fz3StlY5W3EaCIc2unFo7P1m45UyEqCOU5dr0HaxF3ZzKuKN2CkKaSTFg" +
50+
"rS/DvIOaIthSOZTy1XbkBaD0fSLLensk8rUEnz5teuCQzwWUFV0p8KK9KCmhl1HG74GH4W" +
51+
"zps0I83Z2psUbXv5wXG7Pu6hCksOTUZV09+jyvfjvG02sXfwa90l7DEpgR9IvhoNA42137" +
52+
"IYpmSJxWIomJY6eJ6G2D6P3Ksb+k6bJLTmL7hE5ZlaL7gvN1wU9wqxVrfoGs3A4Xhr0Knr" +
53+
"y6oerSMZts3TzJp4zEDcN2G7PjWGvXmPhPtQIDAQAB"
54+
55+
private const val ALGORITHM = "RSA"
56+
57+
private const val TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
58+
59+
private val parameterSpec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)
60+
61+
@OptIn(ExperimentalEncodingApi::class)
62+
private fun encodeBase64(source: ByteArray): String {
63+
return Base64.encode(source)
64+
}
65+
66+
@OptIn(ExperimentalEncodingApi::class)
67+
private fun decodeBase64(source: CharSequence): ByteArray {
68+
return Base64.decode(source)
69+
}
70+
71+
/**
72+
* Generate RSA key pair with 2048 bits
73+
*
74+
* @return Pair of first element is private key, second element is public key
75+
*/
76+
@JvmStatic
77+
fun generateKeyPair(): Pair<String, String> {
78+
val keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM)
79+
keyPairGenerator.initialize(2048)
80+
val keyPair = keyPairGenerator.genKeyPair()
81+
val privateKey = encodeBase64(keyPair.private.encoded)
82+
val publicKey = encodeBase64(keyPair.public.encoded)
83+
return Pair(privateKey, publicKey)
84+
}
85+
86+
@JvmStatic
87+
fun encrypt(text: String, publicKey: String = PUBLIC_KEY): String {
88+
val keySpec = X509EncodedKeySpec(decodeBase64(publicKey))
89+
val keyFactory = KeyFactory.getInstance(ALGORITHM)
90+
val publicKey = keyFactory.generatePublic(keySpec)
91+
val cipher = Cipher.getInstance(TRANSFORMATION)
92+
cipher.init(Cipher.ENCRYPT_MODE, publicKey, parameterSpec)
93+
val encryptedBytes = cipher.doFinal(text.toByteArray())
94+
return encodeBase64(encryptedBytes)
95+
}
96+
97+
@JvmStatic
98+
fun encrypt(text: String): String {
99+
return encrypt(text, PUBLIC_KEY)
100+
}
101+
102+
@JvmStatic
103+
fun decrypt(text: String, privateKey: String = PRIVATE_KEY): String {
104+
val keySpec = PKCS8EncodedKeySpec(decodeBase64(privateKey))
105+
val keyFactory = KeyFactory.getInstance(ALGORITHM)
106+
val privateKey = keyFactory.generatePrivate(keySpec)
107+
val cipher = Cipher.getInstance(TRANSFORMATION)
108+
cipher.init(Cipher.DECRYPT_MODE, privateKey, parameterSpec)
109+
val decryptedBytes = cipher.doFinal(decodeBase64(text))
110+
return String(decryptedBytes)
111+
}
112+
113+
@JvmStatic
114+
fun decrypt(text: String): String {
115+
return decrypt(text, PRIVATE_KEY)
116+
}
117+
118+
}

0 commit comments

Comments
 (0)