最近遇到上面检查,要求所有的系统进行三级等保检查。由于前期在项目中开发的时候,这样的需求没有提过,所以现在需要紧急进行处理下。今天我们就来演示下对系统做小范围的修改,满足三级等保的要求,实现数据库里面的敏感字段加解密存储和查询的要求。下面我们实战举例实现。
背景:
系统里面有一张用户表,保存了用户的姓名、身份证、手机号码、银行卡等字段,在里面身份证、手机号码、银行卡需要进行加密存储,避免出现数据泄露。
具体实现如下:
一、创建一个maven项目,导入相关的依赖
<!-- 这是mysql的依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 这是lombok的依赖 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 这是mybatis-plus依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.1</version> </dependency> <!-- 这是mybatis-plus的代码自动生成器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!-- AES加密解密需要包--> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
二、编写一个AES加密类,创建一个AesUtil.java的类
package com.mybatis.plus.demo.utils; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import lombok.extern.slf4j.Slf4j; /** * aes 加密的工具类 1.存储 加密的秘钥key 2.实现 aes 加密 3.实现aes解密的功能 */ @Slf4j public class AesUtil { private static final String KEY = "KEYBYACSJAVAZXLL"; /** * 偏移量 */ private static final int OFFSET = 16; private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; private static final String ALGORITHM = "AES"; /** * 加密 * * @param content content * @return String */ public static String encrypt(String content) { return encrypt(content, KEY); } /** * 解密 * * @param content content * @return String */ public static String decrypt(String content) { return decrypt(content, KEY); } /** * 加密 * * @param content 需要加密的内容 * @param key 加密密码 * @return String */ public static String encrypt(String content, String key) { try { SecretKeySpec skey = new SecretKeySpec(key.getBytes(), ALGORITHM); IvParameterSpec iv = new IvParameterSpec(key.getBytes(), 0, OFFSET); Cipher cipher = Cipher.getInstance(TRANSFORMATION); // 定义加密编码 String charset = "utf-8"; byte[] byteContent = content.getBytes(charset); // 初始化 cipher.init(Cipher.ENCRYPT_MODE, skey, iv); byte[] result = cipher.doFinal(byteContent); // 加密 return new Base64().encodeToString(result); } catch (Exception e) { log.debug("加密失败:{}", e.getMessage()); } return null; } /** * AES(256)解密 * * @param content 待解密内容 * @param key 解密密钥 * @return 解密之后 */ public static String decrypt(String content, String key) { try { SecretKeySpec skey = new SecretKeySpec(key.getBytes(), ALGORITHM); IvParameterSpec iv = new IvParameterSpec(key.getBytes(), 0, OFFSET); Cipher cipher = Cipher.getInstance(TRANSFORMATION); // 初始化 cipher.init(Cipher.DECRYPT_MODE, skey, iv); byte[] result = cipher.doFinal(new Base64().decode(content)); // 解密 return new String(result); } catch (Exception e) { log.debug("解密失败:{}", e.getMessage()); } return null; } }
备注一下:
1、这个类主要是实现的实现AES的加解密方法。
2、在这个类里面有key作为盐,这种盐在生产项目里面建议配置到配置文件里面去,千万不要硬编码,这里只是为了演示而已。
3、这里的key作为盐,长度必须为16位的,所以自定义盐的时候,需要把这个盐的长度弄到16位去。
三、去数据库创建一张表
CREATE TABLE `user` ( `id` bigint(16) NOT NULL COMMENT '主键', `name` varchar(20) DEFAULT NULL COMMENT '姓名', `identity` varchar(50) DEFAULT NULL COMMENT '身份证', `phone` varchar(50) DEFAULT NULL COMMENT '手机号码', `bank` varchar(50) DEFAULT NULL COMMENT '银行卡号', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
四、编写mybatis的handler
package com.mybatis.plus.demo.handler; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import com.mybatis.plus.demo.utils.AesUtil; public class TypeHandler extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, AesUtil.encrypt(parameter)); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return AesUtil.decrypt(rs.getString(columnName)); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return AesUtil.decrypt(rs.getString(columnIndex)); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return AesUtil.decrypt(cs.getString(columnIndex)); } }
五、创建数据库对应的model信息
package com.mybatis.plus.demo.entity; import java.io.Serializable; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import com.mybatis.plus.demo.handler.TypeHandler; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; @NoArgsConstructor @AllArgsConstructor @Data @EqualsAndHashCode @ToString @Builder @TableName(value = "user", autoResultMap = true) public class UserPoJo extends Model<UserPoJo> implements Serializable { private static final long serialVersionUID = 1L; /** * 主键 */ @TableId private Long id; /** * 姓名 */ private String name; /** * 身份证 */ @TableField(typeHandler = TypeHandler.class) private String identity; /** * 手机号码 */ @TableField(typeHandler = TypeHandler.class) private String phone; /** * 银行卡号 */ @TableField(typeHandler = TypeHandler.class) private String bank; }
备注一下:
1、我们需要在需要加解密的字段上面添加对应的注解:@TableField(typeHandler = TypeHandler.class)。不需要进行加解密的字段则不用添加此备注
2、在类上一定要添加autoResultMap = true。如果不添加这个的话,则查询出来的结果是未解密的。
3、在低版本的mybatis plus里面没有autoResultMap = true这个属性,所以在maven的pom.xml里面尽量添加最新的mybatis plus。
六、剩下的就是根据web的流程,创建controller、service、dao等。
1、Controller类
package com.mybatis.plus.demo.controller; import java.util.Map; import javax.annotation.Resource; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.mybatis.plus.demo.entity.BaseResponsePoJo; import com.mybatis.plus.demo.entity.UserPoJo; import com.mybatis.plus.demo.service.UserService; import lombok.extern.slf4j.Slf4j; @Slf4j @RestController @RequestMapping("/user") public class UserController { @Resource private UserService sysUserService; /** * 添加数据 */ @RequestMapping("/save") public BaseResponsePoJo saveUserInfo() { UserPoJo user = UserPoJo.builder() .id(IdWorker.getId()) .name("张三") .identity("123456789012345678") .bank("1234567890123456").build(); return sysUserService.saveUserInfo(user); } /** * 获取数据 */ @RequestMapping("/getIdCard") public BaseResponsePoJo getUserInfo(String IdCard) { return sysUserService.getUserInfo(IdCard); } /** * 更新数据 */ @RequestMapping("update") public BaseResponsePoJo updateUserInfo(@RequestBody UserPoJo sysUser) { return sysUserService.updateUserInfo(sysUser); } }
2、service类
package com.mybatis.plus.demo.service.impl; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.mybatis.plus.demo.entity.BaseResponsePoJo; import com.mybatis.plus.demo.entity.UserPoJo; import com.mybatis.plus.demo.mapper.UserMapper; import com.mybatis.plus.demo.service.UserService; import lombok.extern.slf4j.Slf4j; /** * 服务接口实现 * */ @Slf4j @Service public class UserServiceImpl extends ServiceImpl<UserMapper, UserPoJo> implements UserService { @Override public BaseResponsePoJo saveUserInfo(UserPoJo sysUser) { baseMapper.insert(sysUser); return BaseResponsePoJo.success(); } @Override public BaseResponsePoJo getUserInfo(String IdCard) { UserPoJo userPoJo = baseMapper .selectOne(new QueryWrapper<UserPoJo>(UserPoJo.builder().identity(IdCard).build())); return BaseResponsePoJo.success(userPoJo); } @Override public BaseResponsePoJo updateUserInfo(UserPoJo sysUser) { baseMapper.updateById(sysUser); return BaseResponsePoJo.success(); } }
3、dao类
package com.mybatis.plus.demo.mapper; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.mybatis.plus.demo.entity.UserPoJo; @Mapper public interface UserMapper extends BaseMapper<UserPoJo>{ }
以上核心的代码就写完了,接着我们测试一下
测试:
一、测试添加数据
1、请求:http://localhost:8088/user/save
2、可以看到返回了
{"code":200,"msg":"请求成功","data":null}
3、再去看看数据库字段是否已经被加密了
可以看到都已经加密了。
二、测试查询
1、这里我们根据身份证号码进行查询,则请求:http://localhost:8088/user/getIdCard?IdCard=123456789012345678
2、看下返回结果
{"code":200,"msg":"请求成功","data":{"id":1571337592451690497,"name":"张三","identity":"123456789012345678","phone":"13888888888","bank":"1234567890123456"}}
可以看到查询出来的都是明文,不影响前端显示。这样子我们的一个完整的使用mybatis进行敏感字段加解密的demo演示就完成了。
最后:
代码下载:下载
还没有评论,来说两句吧...