第二期 - 中
2024年7月7日约 1628 字大约 5 分钟
第二期 - 中
用户中心笔记第二期 - 中
1、数据库设计
什么是数据库?存储数据
数据库里有什么?数据表(理解为 excel 表格)
java 操作数据库?程序代替人工
1.1 什么是设计数据库表?
- 有哪些表(模型)?
- 表中有哪些字段?
- 字段的类型?
- 数据库字段添加索引?
- 表与表之间的关联?
- 性别是否需要加索引?
- 不需要.区分度不大的字段没必要加索引
1.2 用户表设计
- 可选字段:
字段 | 说明 | 类型 |
---|---|---|
id | 主键,唯一标识 | bigint |
username | 用户名 | varchar |
userAccount | 用户昵称 | varchar |
password | 密码 | varchar |
avatar | 用户头像 | varchar |
gender | 性别 | tinyint |
phone | 电话 | varchar |
邮箱 | varchar | |
status | 账户状态(0 - 正常 1 - 停用) | int |
lastTime | 上次登录时间 | datetime |
userRole | 用户权限(0 - 普通用户 1 - 管理员) | int |
remark | 备注 | varchar |
- 必要字段
字段 | 说明 | 类型 |
---|---|---|
createTime | 创建时间(数据插入时间) | datetime |
updateTime | 更新时间(数据更新时间) | datetime |
isDelete | 是否删除(逻辑删除) | tinyint |
建表语句:
create table user
(
userId bigint auto_increment comment '用户ID' primary key,
userName varchar(256) not null comment '用户名',
nickName varchar(256) null comment '用户昵称',
password varchar(256) not null comment '密码',
avatar varchar(1024) null comment '用户头像',
gender tinyint null comment '性别(0 - 男 1 - 女)',
phone varchar(128) null comment '电话',
email varchar(256) null comment '邮箱',
status int default 0 not null comment '账号状态(0 - 正常 1 - 停用)',
isDelete tinyint default 0 not null comment '是否删除(0 - 正常 1 - 删除)',
lastTime timestamp default CURRENT_TIMESTAMP null comment '最后登录时间',
createTime timestamp default CURRENT_TIMESTAMP null comment '创建时间',
updateTime timestamp default CURRENT_TIMESTAMP null comment '更新时间',
userRole int default 0 null comment '用户角色(0 - 普通用户 1 - 管理员)',
remark varchar(512) null comment '备注',
constraint user_userId_uindex unique (userId)
)
comment '用户表';
1.3 自动生成器的使用
使用 MyBatisX 插件,根据数据库表结构自动生成代码:
- domain:实体对象
- mapper:操作数据库的对象
- mapper.xml:定义了 mapper 对象和数据库的关联,可以在里面自己写 SQL
- service:包含常用的增删改查
- serviceImpl:具体实现 service
将生成的代码拖到对应的包路径下之后,编写测试类,测试类如下:
@Test
void testInsertUser() {
User user = new User();
user.setUserName("16937");
user.setNickName("BraumAce");
user.setPassword("12345678");
user.setAvatar("https://blog.braumace.cn/ByteLighting/BraumAce.jpg");
user.setGender(0);
user.setPhone("12345678900");
user.setEmail("1693717911@qq.com");
user.setUserRole(1);
boolean result = userService.save(user);
System.out.println("新增用户ID:" + user.getUserId());
// 断言,判断是否符合预期结果:assertTrue -- 是否返回为true
Assertions.assertTrue(result);
}
可能的报错
由于在建表时,字段均为驼峰命名,在生成实体类时用 @TableFiled
指定了字段映射关系,而 Mybatis-Plus 会自动进行驼峰命令转换,会导致找不到属性对应的字段,所以需要在配置文件中关闭:
# 自动转换为驼峰
mybatis-plus:
configuration:
map-underscore-to-camel-case: false
2、注册逻辑设计
- 用户在前端输入账户和密码、以及校验码
- 校验用户的账户、密码、校验密码,是否符合要求
- 非空
- 账户长度 不小于 4 位
- 密码 不小于 8 位
- 账户不能重复
- 账户不包含特殊字符
- 密码和校验密码相同
- 用户编号不得大于15位
- 对密码进行加密(密码千万不要直接以明文存储到数据库中)
- 向数据库插入用户数据
引入依赖:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
commons-lang3
Apache Commons Lang3 是 Apache 软件基金会开发的一组 Java 工具类库,提供各种常用字符串
处理、数学运算、数据转换、校验等常用功能实现的工具类
盐值加密
Java 中的盐值和密码混淆都是用来提高密码安全性的防护措施。盐值是一种用于增加密码破解难度的技术,在存储用户密码时,会将用户密码和一个随机生成的字符串(称为盐)进行组合,然后再进行加密存储。每个用户的盐值都是随机生成的,这样可以防止攻击者使用相同的方式对一组用户的密码执行攻击。在验证用户登录时,系统会使用相同的盐和用户输入的密码进行组合,并与存储的加密密码进行比对,以验证密码是否正确。
完整代码如下:
/**
* 用户逻辑实现
* @author BraumAce
*/
@Service
@slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Resource
private UserMapper userMapper;
// 盐值,将密码进行混淆
private static final String SALT = "braumace";
/**
* 用户注册实现
* @param userName 用户名
* @param password 用户密码
* @param checkPassword 校验密码
* @return 用户ID
*/
@Override
public long userRegister(String userName, String password, String checkPassword) {
// 1.校验用户的用户名、密码、校验密码,是否符合要求
// 1.1 非空校验
if (StringUtils.isAnyBlank(userName, password, checkPassword)) {
return -1;
}
// 1.2 用户名长度不小于4位
if (userName.length() < 4) {
return -1;
}
// 1.3 密码不小于8位
if (password.length() < 8 || checkPassword.length() < 8) {
return -1;
}
// 1.4 用户名不包含特殊字符
String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
// 使用正则表达式进行校验
Matcher matcher = Pattern.compile(validPattern).matcher(userName);
if (matcher.find()) {
return -1;
}
// 1.5 密码和校验密码是否相同
if (!password.equals(checkPassword)) {
return -1;
}
// 1.6 用户名不能重复,查询数据库当中是否存在相同用户名
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userName", userName);
long count = userMapper.selectCount(queryWrapper);
if (count > 0) {
return -1;
}
// 2.对密码进行md5盐值加密(密码千万不要直接明文存到数据库中)
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
// 3.将用户数据插入到数据库
User user = new User();
user.setUserName(userName);
user.setPassword(encryptPassword);
int result = userMapper.insert(user);
if (result < 0) {
return -1;
}
return user.getUserId();
}
}
测试注册功能:
/**
* 测试出错的情况
*/
@Test
void testUserRegister() {
// 测试非空
String usernName = "zhangsan";
String passward = "";
String checkPassword = "12345678";
long result = userService.userRegister(usernName, passward, checkPassword);
Assertions.assertEquals(-1, result);
// 测试账户长度小于4
usernName = "zhang";
result = userService.userRegister(usernName, passward, checkPassword);
Assertions.assertEquals(-1, result);
// 测试密码小于6位
usernName = "zhangsan";
passward = "1234";
result = userService.userRegister(usernName, passward, checkPassword);
Assertions.assertEquals(-1, result);
// 测试特殊字符
usernName = "zhangsan@";
passward = "12345678";
result = userService.userRegister(usernName, passward, checkPassword);
Assertions.assertEquals(-1, result);
// 测试密码和校验密码不相同
usernName = "zhangsan";
checkPassword = "123457899";
result = userService.userRegister(usernName, passward, checkPassword);
Assertions.assertEquals(-1, result);
// 测试账号不重复
usernName = "zhangsan";
checkPassword = "12345678";
result = userService.userRegister(usernName, passward, checkPassword);
Assertions.assertEquals(-1, result);
// 插入数据,如果已有则插入失败,返回-1
usernName = "16937";
passward = "12345678";
checkPassword = "12345678";
result = userService.userRegister(usernName, passward, checkPassword);
Assertions.assertEquals(-1, result);
}