首页 > 解决方案 > Java 更改序列化的属性类型

问题描述

我想创建一个用于向数据库添加新用户的休息服务。所以基本上,服务会接受这样的请求:

{"login":"testUser","mail":"testUser@gmail.com","password":"123abc"}

现在的问题是:我听说我们不应该将密码存储为字符串,而应存储为 Java 中的字符数组,所以这就是我所做的。

这是我的用户类

package com.mailReminder.restservice.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import javax.persistence.*;
import java.util.Arrays;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String login;
    private String mail;

    private char[] password;

    private byte[] salt;

    public User(String login, String mail, char[] password) {
        this.login = login;
        this.mail = mail;
        this.password = password;
    }

    public User() {
        this.id = 0L;
        this.login = "";
        this.mail = "";
        this.password = null;
        this.salt = null;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getMail() {
        return mail;
    }

    public void setMail(String mail) {
        this.mail = mail;
    }

    public char[] getPassword() {
        return password;
    }

    public void setPassword(char[] password) {
        this.password = password;
    }

    public byte[] getSalt() {
        return salt;
    }

    public void setSalt(byte[] salt) {
        this.salt = salt;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", login='" + login + '\'' +
                ", mail='" + mail + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

为了保护密码,我使用了这个加密器:

package com.mailReminder.restservice.security;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public class PasswordSecurer{

    public static byte[] getSalt() {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);

        return salt;
    }

    public static byte[] hashPassword(char[] password, byte[] salt) {
        int iterationCount = 65536;
        int keyLength = 640;

        KeySpec spec = new PBEKeySpec(password, salt, iterationCount, keyLength);
        SecretKeyFactory factory = null;
        try {
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            return factory.generateSecret(spec).getEncoded();
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }

        return null;
    }
}

所以我需要将 char 数组转换为 byte 数组,这样就可以了。问题是,在对密码进行哈希处理后,char 数组中填充了类似这样的内容

[-108, -71, 126, -39, -6, 65, 50, 42, -51, 55, -88, -121, -103, 55, 109, 22, 12, -21, 33, 72, 122、-127、-31、90、49、75、90、-79、83、-99、-50、-100、-66、29、45、-60、8、41、1、115、-65 , -124, -5, -47, 71, -105, 28, -68, -128, 12, -111, -93, -27, -102, 51, -119, -99, 49, -23, -27、96、-42、-128、124、61、-87、17、-15、-46、-85、-16、-62、106、-1、-79、79、53、108、- 56, -128]

这就是现在被插入到数据库中的 varchar 字段,它被杰克逊像这样解析。

这是控制器

package com.mailReminder.restservice.controller;

import com.mailReminder.restservice.model.User;
import com.mailReminder.restservice.repository.UserRepository;
import com.mailReminder.restservice.security.PasswordSecurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;

@RestController
public class UserController {
    private final UserRepository userRepository;

    @Autowired
    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @PostMapping("/register")
    public ResponseEntity<Object> register(@RequestBody User newUser) {
        byte[] salt = PasswordSecurer.getSalt();
        byte[] hashedPassword = PasswordSecurer.hashPassword(Arrays.toString(newUser.getPassword()).toCharArray(), salt);
        System.out.println(Arrays.toString(hashedPassword));
        if (hashedPassword == null)
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("");

        if (userRepository.findByLogin(newUser.getLogin()) != null)
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body("{\"errorCode\":400,\"errorMessage\":\"User already exists\"}");

        System.out.println(Arrays.toString(hashedPassword));
        newUser.setPassword(Arrays.toString(hashedPassword).toCharArray());
        newUser.setSalt(salt);
        userRepository.save(newUser);

        return ResponseEntity.ok(newUser);
    }

    @GetMapping("/register")
    public @ResponseBody
    Iterable<User> getAllUsers() {

        return userRepository.findAll();
    }
}

在我决定接受 json 作为请求之前,我使用 byte[] 作为密码,这就是它在数据库中的样子,也是我现在想要的样子。

bAz}+ø¶Eí|¿ºmƇJ¾½7HÏuBƒpl¾¤A4lypyí:Ç-eqݸӨEó4¹ÝÍ6 žÊ©§_~9W¸Ô%œL—`Œ©cX§

但我无法将其更改为 User 类中的 byte[],因为 Jackson 因错误而崩溃:JSON 解析错误:无法byte[]从 String反序列化类型的值

所以我的问题是:我能做些什么,让我的密码看起来再次散列而不将 char[] 更改为字符串?

标签: javamysqljsonspringspring-boot

解决方案


获取字节数组并将其转换为 Base64:

byte[] bytes = {-108, -71, 126, -39, -6, 65, 50, 42, -51, 55, -88, -121, -103, 55, 109, 22, 12, -21, 33, 72, 122, -127, -31, 90, 49, 75, 90, -79, 83, -99, -50, -100, -66, 29, 45, -60, 8, 41, 1, 115, -65, -124, -5, -47, 71, -105, 28, -68, -128, 12, -111, -93, -27, -102, 51, -119, -99, 49, -23, -27, 96, -42, -128, 124, 61, -87, 17, -15, -46, -85, -16, -62, 106, -1, -79, 79, 53, 108, -56, -128};

String output = Base64.getEncoder().encodeToString(bytes);

System.out.println( "base64 is " + output );

然后你会得到:

base64 is lLl+2fpBMirNN6iHmTdtFgzrIUh6geFaMUtasVOdzpy+HS3ECCkBc7+E+9FHlxy8gAyRo+WaM4mdMenlYNaAfD2pEfHSq/DCav+xTzVsyIA=

将此作为字符串存储在您的数据库中,并且仅与它进行比较。密码应该是一种方式——您现在也需要一种方法来重置密码,以防忘记密码。

请注意,您还需要以相同的方式保存盐,以便稍后再次验证哈希。


推荐阅读