注:另一种方式参考
关于@TableField中TypeHandler属性,自定义的类型处理器的使用(密码加密与解密举例)http://t.csdnimg.cn/NZy4G
1.简介
1.1 序列化与反序列化
学习注解之前,我们可以先了解一下什么是序列化与反序列化?
1.1.1序列化
序列化是将对象的状态信息转换为可以存储或传输的形式的过程。通常,对象会被转换为字节序列,这样它们就可以被写入文件、存储在数据库中,或者通过网络发送给其他计算机。
目的:
- 持久化:将对象的状态保存到文件或数据库中,以便在程序下次运行时可以恢复对象的状态。
- 网络传输:通过将对象转换为字节序列,可以在网络上传输对象的状态,使得分布式系统中的不同部分可以共享数据。
过程:
- 确定对象的状态:选择需要保存的属性或字段。
- 转换为字节序列:将这些状态转换为字节序列,通常是通过某种编码方式。
1.1.2反序列化
反序列化是序列化的逆过程,即将字节序列恢复为对象的过程。在反序列化过程中,字节序列被重新构造为原来的对象,恢复其状态。
目的:
- 恢复对象状态:从文件、数据库或网络中读取字节序列,并将其转换回对象。
- 数据共享:在分布式系统中,接收方可以通过反序列化接收到的数据来恢复对象的状态。
过程:
- 读取字节序列:从存储介质或网络中读取字节序列。
- 构造对象:根据字节序列中的信息重新构造对象。
1.2 @JsonSerialize与@JsonDeserialize
@JsonSerialize
和 @JsonDeserialize
是 Jackson 库提供的注解,用于在序列化(将对象转换为 JSON 字符串)和反序列化(将 JSON 字符串转换为对象)过程中对特定字段进行自定义处理。这些注解允许你控制 JSON 数据的格式和内容,而不需要改变对象本身的数据结构。
1.2.1@JsonSerialize
@JsonSerialize
注解用于指定一个自定义的序列化器,该序列化器用于将 Java 对象转换成 JSON 字符串。当你需要在序列化过程中对某些字段进行特殊处理时,可以使用这个注解。
定义与用途:
- 定义:@JsonSerialize注解用于指定在将Java对象序列化为JSON字符串时使用的序列化器。
- 用途:通过该注解,开发者可以自定义序列化过程,如格式化日期、调整数字的小数位数、将枚举类型序列化为特定的字符串等。
使用场景:
- 当Java对象的某个属性需要按照特定的格式或逻辑进行序列化时,可以使用@JsonSerialize注解。
- 例如,将日期时间格式化为“yyyy-MM-dd HH:mm:ss”格式的字符串,或将金额从元转换为万元等。
使用方式:
- 可以将@JsonSerialize注解应用于字段、get方法或类级别。
- 通过using属性指定自定义的序列化器类。指定一个实现了
JsonSerializer
接口的类,用于处理字段的序列化。
例如:SexSerializer是自定义处理性别的序列化器
@Schema(name="sex",description=" 性别 ")
@JsonSerialize(using = SexSerializer.class)
private Integer sex;
1.2.2@JsonDeserialize
@JsonDeserialize
注解用于指定一个自定义的反序列化器,该反序列化器用于将 JSON 字符串转换成 Java 对象。当你需要在反序列化过程中对某些字段进行特殊处理时,可以使用这个注解。
定义与用途:
- 定义:@JsonDeserialize注解用于指定在将JSON字符串反序列化为Java对象时使用的反序列化器。
- 用途:通过该注解,开发者可以自定义反序列化过程,如将特定格式的字符串转换为日期对象、将JSON中的某个字段映射到Java对象的不同属性等。
使用场景:
- 当JSON数据的格式与Java对象的属性不完全匹配,或者需要按照特定的逻辑将JSON数据转换为Java对象时,可以使用@JsonDeserialize注解。
- 例如,将JSON中的“金额”字段从万元转换为元,或将自定义格式的日期字符串转换为Java的LocalDateTime对象等。
使用方式:
- 可以将@JsonDeserialize注解应用于字段、set方法或类级别。
- 通过using属性指定自定义的反序列化器类。指定一个实现了 JsonDeserializer接口的类,用于处理字段的序列化。
例如:UserAccountDeserializer是自定义处理用户账号的反序列化器
@Schema(name="account",description=" 账号 ")
@JsonDeserialize(using= UserAccountDeserializer.class)
private String account;
2.场景实现
2.1需求分析
(1)数据库中性别字段为数字,将性别转化为汉字给前端进行展示
(2)保存密码时,进行加密存储,查询时给它明文展示(这里只是举例查所有,真正的场景肯定不能这样搞)
开始分析:
(1)关于性别的转化、密码的明文展示。这俩基本一致,都是查询时给前端进行展示。那你想想,我们给前端进行展示,肯定涉及到了传输。那就是将我们的实体对象,转化成Json字符串的形式,那就是在Vo对象字段上加@JsonSerialize注解实现自定义序列化器来操作。
(2)关于密码的加密保存。那你想想,前端传给我们的肯定是明文,而且是Json串,我们将Json转化成java对象进行保存。那这个不就是反序列话操作吗。我们可以在入参Dto对象字段上加@JsonDeserialize注解实现自定义反序列化器来操作
那这里,可能有个疑问,从数据库中查出来,不也涉及到了,反序列化操作,将数据库对象映射成了java对象?保存时,不也涉及到了序列化操作,将java对象映射成Json串?是的没错,但是我这里用了mybatis-plus来实现,我查询了一下,mybatis-plus自带的crud他好像不一定会实现序列化与反序列化,就对导致我们的注解失效,所以这里我没在数据库entity实体上加@JsonSerialize和@JsonDeserialize注解)也有另一种实现方法,使用mybatis-plus支持的typeHandler的形式(typeHandler我在另一篇文章里去写)
2.2通用部分
2.2.1 Entity数据库实体,User对象
package com.zsh.test.swagger3test.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.zsh.test.swagger3test.handler.Sm4TypeHandler;
import lombok.Data;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Date;
/**
* @Description
* @Author ZhaoShuhao
* @Date: 2024-07-21 12:45:39
*/
@Data
@Accessors(chain = true)
@Schema(name="用户信息")
@TableName(value = "user",autoResultMap = true)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id",type = IdType.ASSIGN_ID)
@Schema(name="id",description=" 主键 ")
private Long id;
@TableField(value = "name")
@Schema(name="name",description=" 姓名 ")
private String name;
@TableField(value = "age")
@Schema(name="age",description=" 年龄 ")
private Integer age;
@TableField(value = "phone")
@Schema(name="phone",description=" 电话 ")
private String phone;
// @TableField(value = "account",typeHandler = Sm4TypeHandler.class)
@Schema(name="account",description=" 账号 ")
private String account;
// @TableField(value = "pwd",typeHandler = Sm4TypeHandler.class)
@Schema(name="pwd",description=" 密码 ")
private String pwd;
@TableField(value = "sex")
@Schema(name="sex",description=" 性别 ")
private Integer sex;
@TableField(value = "creat_time")
@Schema(name="reatTime",description=" 创建时间 ")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date creatTime;
@TableField(value = "update_time")
@Schema(name="updateTime",description=" 更新时间 ")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}
2.2.2 Vo实体,UserVo视图对象
package com.zsh.test.swagger3test.model.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.zsh.test.swagger3test.serializer.SexSerializer;
import com.zsh.test.swagger3test.serializer.UserPwdSerializertest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @Description
* @Author ZhaoShuhao
* @Date: 2024-07-21 12:45:39
*/
@Data
@Accessors(chain = true)
@Schema(name=" user ", description=" null ")
public class UserVo implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(name="id",description=" 主键 ")
private Long id;
@Schema(name="name",description=" 姓名 ")
private String name;
@Schema(name="age",description=" 年龄 ")
private Integer age;
@Schema(name="phone",description=" 电话 ")
private String phone;
@Schema(name="account",description=" 账号 ")
private String account;
@Schema(name="pwd",description=" 密码 ")
@JsonSerialize(using= UserPwdSerializertest.class)
private String pwd;
@Schema(name="sex",description=" 性别 ")
@JsonSerialize(using = SexSerializer.class)
private Integer sex;
}
2.2.3 Dto实体,UserDto入参对象
package com.zsh.test.swagger3test.model.dto;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.zsh.test.swagger3test.serializer.UserPwdDeSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* @Description
* @Author ZhaoShuhao
* @Date: 2024-07-21 12:45:39
*/
@Data
public class UserDto implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(name="name",description=" 姓名 ")
private String name;
@Schema(name="age",description=" 年龄 ")
private Integer age;
@Schema(name="phone",description=" 电话 ")
private String phone;
@Schema(name="account",description=" 账号 ")
private String account;
@JsonDeserialize(using= UserPwdDeSerializer.class)
@Schema(name="pwd",description=" 密码 ")
private String pwd;
@Schema(name="sex",description=" 性别 ")
private Integer sex;
}
2.2.4 UserMapper
package com.zsh.test.swagger3test.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zsh.test.swagger3test.model.entity.User;
import org.apache.ibatis.annotations.Mapper;
/**
* @author KeepHappy
* @description 针对表【user】的数据库操作Mapper
* @createDate 2024-07-21 12:55:52
* @Entity src/main/java/com/zsh/test/swagger3test.model.User
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
2.2.5 UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zsh.test.swagger3test.mapper.UserMapper">
</mapper>
2.2.6 UserService
package com.zsh.test.swagger3test.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.zsh.test.swagger3test.model.dto.UserDto;
import com.zsh.test.swagger3test.model.entity.User;
import com.zsh.test.swagger3test.model.vo.UserVo;
import java.util.List;
/**
* @author KeepHappy
* @description 针对表【user】的数据库操作Service
* @createDate 2024-07-21 12:55:52
*/
public interface UserService extends IService<User> {
}
2.2.7 UserServiceImpl
package com.zsh.test.swagger3test.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zsh.test.swagger3test.model.dto.UserDto;
import com.zsh.test.swagger3test.mapper.UserMapper;
import com.zsh.test.swagger3test.model.entity.User;
import com.zsh.test.swagger3test.model.vo.UserVo;
import com.zsh.test.swagger3test.service.UserService;
import io.swagger.v3.core.util.Json;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @author KeepHappy
* @description 针对表【user】的数据库操作Service实现
* @createDate 2024-07-21 12:55:52
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
2.3@JsonDeserialize的使用
UserDto入参对象:
@JsonDeserialize(using= UserPwdDeSerializer.class)
@Schema(name="pwd",description=" 密码 ")
private String pwd;
2.3.1 UserPwdDeSerializer密码加密反序列化器
package com.zsh.test.swagger3test.serializer;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.SM4;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* 反序列化 对用户字段进去加密
*/
public class UserPwdDeSerializer extends JsonDeserializer<String> {
private static final Logger logger = LoggerFactory.getLogger(UserPwdDeSerializer.class);
private static final String hexKey ="79C37CDBCD6FAB9D0619F511B2031234";
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
String text = jsonParser.getText();
String enPwd = StrUtil.isBlank(text) ? "" : getEncryptInfo(text);
return enPwd;
}
public static String getEncryptInfo(String value) {
return getEncryptInfoSm4(value);
}
private static String getEncryptInfoSm4(String value){
SM4 sm4 = new SM4(Mode.ECB, Padding.PKCS5Padding,ByteUtils.fromHexString(hexKey));
return sm4.encryptHex(value);
}
}
2.3.2 UserController用户接口
package com.zsh.test.swagger3test.controller;
import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.zsh.test.swagger3test.config.Result;
import com.zsh.test.swagger3test.model.dto.UserDto;
import com.zsh.test.swagger3test.model.entity.User;
import com.zsh.test.swagger3test.model.vo.UserVo;
import com.zsh.test.swagger3test.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author ZhaoShuhao
* @data 2024/7/21 15:12
*/
@Tag(name = "用户接口")
@RestController
@RequestMapping("/user/api")
public class UserController {
@Resource
private UserService userService;
@PostMapping("/save")
@Operation( summary= "添加用户信息")
public Result saveUserInfo(@RequestBody List<UserDto> userList) {
List<User> users = BeanUtil.copyToList(userList, User.class);
boolean b = userService.saveBatch(users);
return b ? Result.success() : Result.error("添加失败");
}
}
2.3.3 结果展示
2.4@JsonSerialize的使用
UserVo视图对象:
@Schema(name="pwd",description=" 密码 ")
@JsonSerialize(using= UserPwdSerializertest.class)
private String pwd;
@Schema(name="sex",description=" 性别 ")
@JsonSerialize(using = SexSerializer.class)
private Integer sex;
2.4.1SexSerializer性别转化序列化器
package com.zsh.test.swagger3test.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**序列化
* @author ZhaoShuhao
* @data 2024/7/25 23:30
*/
public class SexSerializer extends JsonSerializer<Integer> {
@Override
public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (integer == 1) {
jsonGenerator.writeString("男");
} else if (integer == 2) {
jsonGenerator.writeString("女");
}else {
jsonGenerator.writeString("未知");
}
}
}
2.4.2 UserPwdSerializertest密码解密序列化器
package com.zsh.test.swagger3test.serializer;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.SM4;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 序列化 对用户字段进去解密
*/
public class UserPwdSerializertest extends JsonSerializer<String> {
private static final Logger logger = LoggerFactory.getLogger(UserPwdSerializertest.class);
private static final String hexKey ="79C37CDBCD6FAB9D0619F511B2031234";
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String dePwd = StrUtil.isBlank(s) ? "" : deserializeText(s);
jsonGenerator.writeString(dePwd);
}
public static String deserializeText(String text){
return getDecryptInfoSm4(text) ;
}
private static String getDecryptInfoSm4(String value){
try {
SM4 sm4 = new SM4(Mode.ECB, Padding.PKCS5Padding,ByteUtils.fromHexString(hexKey));
return sm4.decryptStr(value, StandardCharsets.UTF_8);
}catch (Exception e){
logger.error("解密数据{}",value);
return value;
}
}
}
2.4.5 UserController用户接口
package com.zsh.test.swagger3test.controller;
import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.zsh.test.swagger3test.config.Result;
import com.zsh.test.swagger3test.model.dto.UserDto;
import com.zsh.test.swagger3test.model.entity.User;
import com.zsh.test.swagger3test.model.vo.UserVo;
import com.zsh.test.swagger3test.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author ZhaoShuhao
* @data 2024/7/21 15:12
*/
@Tag(name = "用户接口")
@RestController
@RequestMapping("/user/api")
public class UserController {
@Resource
private UserService userService;
@PostMapping("/save")
@Operation( summary= "添加用户信息")
public Result saveUserInfo(@RequestBody List<UserDto> userList) {
List<User> users = BeanUtil.copyToList(userList, User.class);
boolean b = userService.saveBatch(users);
return b ? Result.success() : Result.error("添加失败");
}
@PostMapping("/getAllUserInfo")
@Operation(summary = "查询所有用户信息")
public Result<List<UserVo>> getAllUserInfo(){
List<User> list = userService.list();
List<UserVo> userVos = BeanUtil.copyToList(list, UserVo.class);
return Result.success((userVos));
}
}