• Spring Security使用总结五,加密用户密码,不再使用明文保存密码


    上一章我们成功的注册了一个新用户,按照正常逻辑来说,这一章应该是登录了,但是我们也看到了,这数据库保存的居然是明文密码,这谁受得了,这要是用户信息泄露了,这不让人一锅端了啊,还什么security。这一章主要就是讲加密的。

    加密也分为两个部分,第一部分是前台密码的加密,第二部分是后台对密码的加密,这第一部分也是需要后台进行协助的,因为security自带了很多的加密方式,这次就使用BCryptPasswordEncoder的加密方式,而前台密码加密就使用RSA的加密方式,RSA需要有一个私钥一个公钥,在后台生成这对密钥,然后将公钥传到前台用来给密码加密,然后后台接收加密之后的密码,再用私钥解密,判断两次输入的密码是否一致,然后再用BCryptPasswordEncoder的方式对解密后的密码再次进行加密,存入数据库。

    先在后台创建一个RSA解密以及生成秘钥的工具

    1. import org.apache.tomcat.util.codec.binary.Base64;
    2. import javax.crypto.Cipher;
    3. import java.security.*;
    4. import java.security.spec.PKCS8EncodedKeySpec;
    5. public class RSAUtils {
    6. public static final String RSA_ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";
    7. public static final int KEY_SIZE_2048 = 2048;
    8. private static final String ALGORITHM = "RSA";
    9. public static KeyPair generateKeyPair() {
    10. return generateKeyPair(KEY_SIZE_2048);
    11. }
    12. public static KeyPair generateKeyPair(int keySize) {
    13. try {
    14. KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
    15. keyPairGenerator.initialize(keySize);
    16. return keyPairGenerator.generateKeyPair();
    17. } catch (NoSuchAlgorithmException e) {
    18. throw new IllegalArgumentException("Failed to generate key pair!", e);
    19. }
    20. }
    21. public static PrivateKey getPrivateKey(String base64PrivateKey) {
    22. try {
    23. PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(base64PrivateKey));
    24. KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
    25. return keyFactory.generatePrivate(keySpec);
    26. } catch (Exception e) {
    27. throw new IllegalArgumentException("Failed to get private key!", e);
    28. }
    29. }
    30. public static String decrypt(byte[] data, PrivateKey privateKey) {
    31. try {
    32. Cipher cipher = Cipher.getInstance(RSA_ECB_PKCS1_PADDING);
    33. cipher.init(Cipher.DECRYPT_MODE, privateKey);
    34. return new String(cipher.doFinal(data));
    35. } catch (Exception e) {
    36. throw new IllegalArgumentException("Decrypt failed!", e);
    37. }
    38. }
    39. public static String decrypt(String data, String base64PrivateKey) {
    40. return decrypt(Base64.decodeBase64(data), getPrivateKey(base64PrivateKey));
    41. }
    42. }

    在RegistryController将代码修改为如下所示:

    1. @Controller
    2. public class RegisterController {
    3. @Resource
    4. RegisterService registerService;
    5. private static final KeyPair keyPair = RSAUtils.generateKeyPair();
    6. private static final String privateKey = Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
    7. @RequestMapping("/register")
    8. public String register(Model model){
    9. String publicKey = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
    10. model.addAttribute("ssKey",publicKey);
    11. return "register";
    12. }
    13. @ResponseBody
    14. @PostMapping("/registration")
    15. public JSONObject registration(@RequestBody RegisterDTO register){
    16. String decrypt = RSAUtils.decrypt(register.getPassword(), privateKey);
    17. String confirmDecrypt = RSAUtils.decrypt(register.getConfirmPassword(), privateKey);
    18. if(!decrypt.equals(confirmDecrypt)){
    19. return JSONObject.parseObject("两次密码不一致");
    20. }
    21. return registerService.register(register.getName(),decrypt);
    22. }
    23. }

    前台的代码修改如下所示,添加一条引用,将密码等信息在发送时加密:

    1. <script type="text/javascript">
    2. const encrypt = new JSEncrypt();
    3. const ssKey = "[[${ssKey}]]";
    4. encrypt.setPublicKey(ssKey);
    5. $(function() {
    6. $("#register").click(function(){
    7. $.ajax({
    8. type: "POST",
    9. dataType: "json",
    10. url: 'http://localhost:8080/registration',
    11. contentType: "application/json",
    12. data:JSON.stringify({
    13. "name": $("#name").val(),
    14. "password": encrypt.encrypt($("#password").val()),
    15. "confirmPassword":encrypt.encrypt($("#confirmPassword").val())
    16. }),
    17. success: function (result) {
    18. console.log("data is :" + result)
    19. }
    20. });
    21. })
    22. });
    23. script>

    到此,对于前台向后台传输的密码的加密和解密工作就算是完成了,接下来就是使用security的加密,确保后续的认证工作能够顺利进行下去。上边的加密修改到Controller,接下来的加密就从Service开始,修改RegistryService代码如下所示:

    1. public JSONObject register(String name, String password){
    2. BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    3. String encodePassword = bCryptPasswordEncoder.encode(password);
    4. User user = new User();
    5. user.setName(name).setPassword(encodePassword)
    6. userRepository.save(user);
    7. return JSONObject.parseObject("{'userId':"+ userRepository.save(user).getId() +"}");
    8. }

    结束,这样密码就算是加密成功了,我来调试一下,看看加密的效果如何:

    1.启动项目,访问http://localhost:8080/register ,输入test22,123456

    2.在后台打个断点,看看接收到了一个什么玩意儿

     

    确实是加密成功了,然后看看解密之后密码是不是123456

    3.进入service之后重新加密

     

    图里画圈的就是最后存入数据库中的密码,这玩意正常人应该不会用来当密码吧,就是世界记忆大师用这个当密码也得掂量掂量啊。

    4.进入数据库里面确认查看

     应该是没有毛病了。

    此时的文件结构如下所示

     只多了两个和加密相关的文件,一个是RSA工具类,一个是前台引用的加密插件。

  • 相关阅读:
    Hazelcast系列(六): TCP-IP发现机制
    华为云云耀云服务器L实例评测|定时给微信群中推送每日新闻及生活小常识
    【吞噬星空】战神宫全体投票,为罗峰脱罪,徐欣补办婚礼,洪成功恢复脑电波
    R语言ggplot2可视化:基于aes函数中的fill参数和shape参数自定义绘制分组折线图并添加数据点(散点)、设置可视化图像的主题为theme_bw
    Vue--》简易资金管理系统后台项目实战(前端)
    《Linux从练气到飞升》No.28 Linux中的线程同步
    sqlserver 事务
    k8s--基础--12.4--pod--创建自主式pod
    【VM-Tools】Linux快速安装
    200. 岛屿数量
  • 原文地址:https://blog.csdn.net/z449077880/article/details/134239087