本文开发一用户注册与登录的后端java平台。开发完后的网页效果:http://user-front.66bond.com/

后端主要提供用户的注册、查询、删除等操作。使用到的开发工具:

  • IDEA2021; 关注公众号”青椒工具”,发送”IDEA”,获取windows下的IDEA安装包
  • mysql 5.7;关注公众号”青椒工具”,发送”mysql”,获取windows下的mysql5.7安装包;

1、注册逻辑

写代码之前,我们先罗列下用户的注册逻辑:

1、用户输入的账户和密码不能为空;

2、校验用户的账户、密码是否符合要求:

  • 账户字符不能少于4个;
  • 密码不能小于8位;
  • 账户不能与已有的重复;
  • 账户不能包含特殊字符;
  • 密码和校验密码相同;

3、对密码进行加密;保证后端工作人员不能看到用户密码;

4、向数据库插入数据;

2、代码框架:

书写注册业务代码涉及到文件:

在service中的接口UserService.java中定义注册方法:

1
2
3
public interface UserService extends IService<User> {
long userRegister(String userAccount, String userPassword, String checkPassword);
}

在service的实现类中UserServiceImpl.java实现userRegister的代码:

1
2
3
4
5
6
7
8
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

@Override
public long userRegister(String userAccount, String userPassword, String checkPassword) {
//业务逻辑代码写在这里;
return 0;
}
}

在test/java/com.tfzhang.backend/service/UserServiceTest.java中创建测试文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class UserServiceTest {

@Resource
private UserService userService;

@Test
public void testAddUser(){
User user = new User();
//...
}

@Test
void userRegister() {
//书写测试代码;
}
}
3、业务代码实现:

接下来主要在UserServiceImpl.java中实现各种逻辑的代码;

1、用户输入的账户和密码不能为空;

我们可以这么写:

1
2
3
if(userAccount==null || userPassword==null || checkPassword==null){
return -1;
}

但如果变量太多上述的写法很不方便,我们采用一个common lang类库;怎么引入?访问maven repository官网:

1
https://mvnrepository.com/

输入关键词commons lang查找,找到用户量最大的那个,然后点击,如图1所示,获取其maven配置,并复制到我们的pom.xml文件中,图2所示,刷新maven完成安装。图3.1maven搜common-lang

图1 maven库查找common lang

图3.2commonlang加入到pomxml

图2 common-lang参数加载到pom.xml文件

我们重写检查非空的代码:

1
2
3
if(StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)){
return -1;
}

现在的代码就比较简洁;

账户字符不能少于4个;

密码不能小于8位;

1
2
3
4
5
6
7
if(userAccount.length() < 4){
return -1;
}

if(userPassword.length() < 8 || checkPassword.length() < 8){
return -1;
}

账户不能与已有的重复;

要比较重复,我们要查询数据库;

1
2
3
4
5
6
     QueryWrapper<User> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("userAccount", userAccount);
long count = this.count(queryWrapper);
if(count > 0){
return -1;
}

对于这段代码的解释:这段代码使用了MyBatis-Plus库中的QueryWrapper类来构建一个数据库查询条件,并查询符合这些条件的记录数量。

  1. QueryWrapper<User> queryWrapper=new QueryWrapper<>();
    • 这一行创建了一个新的QueryWrapper对象,该对象用于构建数据库查询条件。这里,User是泛型参数,表示这个QueryWrapper是用来构建针对User表或实体的查询条件的。
  2. queryWrapper.eq("userAccount", userAccount);
    • 使用eq方法给queryWrapper添加了一个等于条件,即查询User表中userAccount字段值等于userAccount变量值的记录。
  3. long count = this.count(queryWrapper);
    • 调用count方法(可能是MyBatis-Plus提供的BaseMapper接口中的方法)来查询符合queryWrapper构建的条件的记录数量,并将结果保存在count变量中。

账户不能包含特殊字符;

不能包含特殊字符串,可以使用正则表达式:

1
2
3
4
5
6
String regex = "^[a-zA-Z0-9]+$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(userAccount);
if(!matcher.matches()){
return -1;
}

上述的正则表达式表示除了数字和英文大小写,其他的都属于特殊字符;

密码和校验密码相同;

1
2
3
if(!userPassword.equals(checkPassword)){
return -1;
}

对密码进行加密(数据脱敏)

不能让后台看到密码的原文,所以要对密码进行加密后再插入到数据库;而且加密方法最好采用非对称加密;

1
2
final String SALT = "tfzhang";
String encrptedPassword = DigestUtils.md5DigestAsHex((SALT+userPassword).getBytes());

代码中,我们采用md5加密,然后同时加了点”盐”,tfzhang,所谓的盐就是加了点杂质,混淆视听的作用;

注意:密码永远不要明文保存;

4、向数据库插入数据;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
User user = new User();
user.setUserAccount(userAccount);
user.setUserPassword(encrptedPassword);
/**
* 注册默认是普通用户,所以userRole设置为0;
*/
user.setUserRole(0);
user.setIsDelete(0);

//4.插入到数据库;
boolean result=this.save(user);
if(!result)
return -1;

return user.getId();
4、业务代码的性能优化及测试

将上述所有的业务代码片段整合后,得到如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

@Override
public long userRegister(String userAccount, String userPassword, String checkPassword) {
/**
* 数据是否为空;
*/
if(StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)){
return -1;
}
/**
* 用户长度不能小于4
*/
if(userAccount.length() < 4){
return -1;
}
/**
* 密码长度不能小于8
*/
if(userPassword.length() < 8 || checkPassword.length() < 8){
return -1;
}
/**
* 查询数据库:用户名是否重复;
*/
QueryWrapper<User> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("userAccount", userAccount);
long count = this.count(queryWrapper);
if(count > 0){
return -1;
}
/**
* 不能包含特殊字符;
*
*/
String regex = "^[a-zA-Z0-9]+$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(userAccount);
if(!matcher.matches()){
return -1;
}
/**
* 密码和校验密码不能重复;
*
*/
if(!userPassword.equals(checkPassword)){
return -1;
}
/**
* 密码加密;
*/
final String SALT = "tfzhang";
String encrptedPassword = DigestUtils.md5DigestAsHex((SALT+userPassword).getBytes());
/**
* 最后插入数据库;
*/
User user = new User();
user.setUserAccount(userAccount);
user.setUserPassword(encrptedPassword);
/**
* 注册默认是普通用户,所以userRole设置为0;
*/
user.setUserRole(0);
user.setIsDelete(0);

//4.插入到数据库;
boolean result=this.save(user);
if(!result)
return -1;
return user.getId();
}
}

上述代码会存在”性能浪费”的问题,因为数据库查询比较耗资源,所以将查询数据库的代码放在下面两个逻辑之后:

  • 不能包含特殊字符;

  • 密码和校验密码不能重复;

接下来我们写测试代码,在test/java/com.tfzhang.backend/service/UserServiceTest.java中书写代码:

我们需要针对每个业务点书写测试代码;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void userRegister() {
/**
* 测试用户名不能为空;
*/
String userAccount="tfzhang2";
String userPassword="";
String checkPassword="123456";
long result=userService.userRegister(userAccount, userPassword, checkPassword);
Assertions.assertEquals(-1, result);

/**
* 测试用户名不能小于4
*/
userAccount = "tf";
result=userService.userRegister(userAccount, userPassword, checkPassword);
Assertions.assertEquals(-1, result);

/**
* 密码不能小于8
*/
userAccount = "tfzhang2";
String userPassowrd = "123456";
result=userService.userRegister(userAccount, userPassword, checkPassword);
Assertions.assertEquals(-1, result);

/**
* 用户名不能包含特殊字符;
*/
userAccount="tf zhang";
result=userService.userRegister(userAccount, userPassword, checkPassword);
Assertions.assertEquals(-1, result);

/**
* 密码要相同;
*/
userAccount="tfzhang2";
userPassowrd="123456789";
checkPassword="12345678";
result=userService.userRegister(userAccount, userPassword, checkPassword);
Assertions.assertEquals(-1, result);

/**
* 账户名不能重复;
* tfzhang已经在数据库中;
*/
userAccount="tfzhang";
userPassowrd="12345678";
checkPassword="12345678";
result=userService.userRegister(userAccount, userPassword, checkPassword);
Assertions.assertEquals(-1, result);
}

点击测试方法运行,所有测试都通过。最后我们再做一个测试,往数据库中添加一个新用户:

1
2
3
4
5
userAccount="tfzhang2";
userPassword="12345678";
checkPassword="12345678";
result=userService.userRegister(userAccount, userPassword, checkPassword);
assertNotEquals(-1, result); //如果插入新数据成功,返回大于0的数;

测试成功,result返回的数值还是2,说明插入数据成功,我们查看数据库的确可以发现新插入的tfzhang2用户。

至此我们完成注册逻辑,后续完成登录逻辑。

5、参考资料:

本文参考自如下知识星球中的视频教程,更多的完整的相关视频教程,见如下的收费知识星球,近3万人的学习社区,

编程有人同行,学习不再迷茫

编程导航知识星球