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

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

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

前面一章我们实现了controller中的login和register接口,并且测试通过。

本章我们用户管理的接口:查找用户和删除用户。

1、整体逻辑

查找用户是根据用户的账户名查询数据库,如果用户没有提供用户名,则返回所有用户的数据;

删除用户,前端给出用户的id,然后查询数据库删除,注意此处的删除是逻辑删除;

上述两个操作的接口都要”鉴权”,只有管理员才有权限调用上述这两个接口;一般用户没有权限调用;

所以,我们的接口代码中要判断当前登录用户是否是admin用户。

2、代码框架:

在/com.tfzhang.backend/controller/中新建UserController.java文件,代码结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RestController
@RequestMapping("/user")
public class UserController {

@Resource
private UserService userService;

/**
* 用户注册
*/
@GetMapping("/search")
public List<User> searchUsers(String userName, HttpServletRequest request) {
//调用service接口;
}

/**
* 用户登录
*/
@PostMapping("/delete")
public Boolean deleteUser(@RequestBody long id, HttpServletRequest request) {
//调用service接口;
}

因为searchUsers是GET类型访问,所以采用的标签是 @GetMapping,同时参数中也不需要加@Requestbody标签。

两个方法的参数中都要添加request,因为要判断用户的权限。

3、接口代码实现:

我们先从searchUsers入手开始编写代码,要解决的两个问题:

  • 根据userName查询数据库;
  • 鉴权,判断当前用户是否为管理员;

我们先来看下如何鉴权,用户在login的时候,服务器端会设置session,其中我们要将userRole这个参数也设置进去,即在UserServiceImpl.java中的getSafetyUser()方法中加入如下代码:

1
user.setUserRole(originalUser.getUserRole());

当用户访问searchUsers接口时,将request的session属性中的userRole变量取出来,判读是否为1,就可以判断当前用户是否为admin用户,代码片段如下:

1
2
3
4
5
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
User user = (User) userObj;
if(user == null || user.getUserRole() != 1){
return new ArrayList<>(); //非管理员,返回一个空的列表;
}

在我们的数据库中userRole为1则表示管理员,否则是普通用户。

如果是管理员,接下来可以使用mybatis-plus的queryWrapper对象访问数据库,代码片段如下:

1
2
3
4
5
6
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username", username);
}
List<User> userList = userService.list(queryWrapper);
List<User> list = userList.stream().map(item -> userService.getSafetyUser(item)).collect(Collectors.toList());

queryWrapper.like(),对于mybatis-plus而言,生成的SQL查询中包含一个条件,该条件会对username字段进行模糊匹配。

如果username为空,则查询全部用户,并采用list返回;最后一行代码的主要作用是是对list中的每一个user进行脱敏处理。

我们将上述两个代码片段整合,获得searchUsers的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@GetMapping("/search")
public List<User> searchUsers(String username, HttpServletRequest request){

Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
User user = (User) userObj;
if(user == null || user.getUserRole() != 1){
return new ArrayList<>(); //非管理员,返回一个空的列表;
}

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username", username);
}
List<User> userList = userService.list(queryWrapper);
List<User> list = userList.stream().map(item -> userService.getSafetyUser(item)).collect(Collectors.toList());
return list;
}

此处我们将原来应该写在service包的业务代码一并写到接口这边,主要原因是业务很简单,就直接写在controller这层。

==代码可优化的地方==:

  • 上述代码中出现常量1,可以提取到constant包中;
  • 上述判断是否管理员的代码,是一个常用的操作,值得提取出来封装为一个方法;

我们在constant包的UserConstant.java中添加:

1
int ADMIN_ROLE=1;

将判断admin的语句提取为UserController.java中单独的方法isAdmin():

1
2
3
4
5
6
7
8
9
private boolean isAdmin(HttpServletRequest request){
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
User user = (User) userObj;

if(user != null && user.getUserRole() == ADMIN_ROLE){
return true;
}
return false;
}

经过上述的优化,searchUsers的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@GetMapping("/search")
public List<User> searchUsers(String username, HttpServletRequest request){

if(!isAdmin(request)){
return new ArrayList<>(); //非管理员,返回一个空的列表;
}

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username", username);
}
List<User> userList = userService.list(queryWrapper);
List<User> list = userList.stream().map(item -> userService.getSafetyUser(item)).collect(Collectors.toList());
return list;
}

参照searchUsers,我们再完成deleteUser的代码编写:

1
2
3
4
5
6
7
8
9
10
11
@PostMapping("/delete")
public boolean deleteUser(@RequestBody long id, HttpServletRequest request){
if(!isAdmin(request)){
return false;
}
if(id <= 0){
return false;
}
boolean res = userService.removeById(id);
return res;
}

mybatis-plus自带按id删除的方法removeById,所以不需要我们自己来写,并且这个removeById操作是逻辑删除。

4、代码测试

测试的步骤与login和register接口的测试步骤相同;

注意在测试search接口时,先要采用login接口登录获取session,然后还要保证登录的账户的userRole被设置为1。

按照上述步骤,我们的search接口测试成功:在不输入username的情况下,的确获取到所有脱敏后的用户信息,测试通过。

现在测试delete接口,我们采用如下的request:

1
2
3
4
5
6
POST http://localhost:8080/user/delete
Content-Type: application/json

{
"id": "1"
}

访问后出现如下的错误:

1
Required request parameter 'id' for method parameter type long is not present

后端没有拿到id这个参数,而我们的request中的确已经给出这个数据。我们的id按字符串对形式提供,猜测后端没有那么智能将1这个整形数据直接提取出来。那么我们将deleteUser接口重写,书写代码提取其中的id整型数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@PostMapping("/delete")
public boolean deleteUser(@RequestBody Map<String, String> dataBody, HttpServletRequest request){
if(!isAdmin(request)){
return false;
}

String idstr = dataBody.get("id");
if(idstr==null || idstr.isEmpty()){
return false;
}

long id=-1;
try{
id = Long.parseLong(idstr);
}catch (NumberFormatException e){
return false;
}

if(id <= 0){
return false;
}
boolean res = userService.removeById(id);
return res;
}

保存重新启动测试,这次对了,返回的值为true,图1所示,再查看数据库id=1的数据的isDelete位的确被置为1,说明逻辑删除正确执行。

图6.1deleteUser测试正确

图1 deleteUser测试成功

之前还有一遗漏的点,即session没有设置有效时间,我们在application.yml中添加配置:

1
2
3
# session 失效时间
session:
timeout: 86400 ###以秒为单元,一天86400秒;
5、参考资料:

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

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

编程导航知识星球