用户中心项目登录功能
参考笔记
✔登录功能
详细设计
登录接口
- 接收参数:用户账户、密码
- 请求类型:POST
请求参数很长时不建议用 GET
- 请求体:JSON格式的数据
- 返回值:用户信息(脱敏)
逻辑
编写代码
- UserService 编写如下代码,Alt + Enter 实现方法
- 进入 Impl 将 Register 的逻辑复制过来,进行修改
将 final String SALT = "yupi"; 提到前面
添加 @Slf4j 注解,使用 log 【方便后续系统出现问题,到日志中查找问题】
修改部分逻辑 【删除插入数据;账户不能重复修改成查询用户是否存在,放在加密之后】
@Override
public User doLogin(String userAccount, String userPassword) {
}
if(userAccount.length() < 4){
return null;
}
if(userPassword.length() < 8){
return null;
}
//账户不能包含特殊字符
String validPattern = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);
if (matcher.find()) {
return null;
}
/* 2.加密 */
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
//查询用户是否存在
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userAccount",userAccount);
queryWrapper.eq("userPassword",encryptPassword);
User user = userMapper.selectOne(queryWrapper);
if (user == null) {
log.info("user login failed,userAccount cannot match userPassword");
return null;
}
return user;
}
上面查询用户是否存在的代码逻辑是存在问题的 如果用户的 isDelete 字段是删除状态,能否查出来呢?
【MyBatis-Plus 逻辑删除 @TableLogic注解】
要删除一条数据时,不是真正的删除,而是将数据库中的某个字段从 0 置为 1 表示数据失效,无法查询到
- 在 application.yml 中配置 MyBatis-Plus 的全局逻辑删除属性:
mybatis-plus:
global-config:
db-config:
logic-delete-field: isDelete # 全局逻辑删除字段名
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值
- User 实体类中 为 isDelete 字段添加 @TableLogic 注解
该注解用于标记实体类中的字段作为逻辑删除字段。逻辑删除是一种数据管理策略,它不是真正地从数据库中删除记录,而是在记录中标记该记录为已删除状态。通过使用@TableLogic注解,MyBatis-Plus 可以在查询、更新和删除操作中自动处理逻辑删除字段的值。
import com.baomidou.mybatisplus.annotation.TableLogic;
public class User {
// 其他字段...
@TableLogic
private Integer isDelete;
}
后端-登录态管理(Cookie 和 Session)
如何知道是哪个用户登陆了? (JavaWeb)
- 连接服务器端后,得到一个 session 状态(匿名会话),返回给前端
- 登陆成功后,得到了登陆成功的 session ,并且给该 session 设置一些值(比如用户信息),返回给前端一个设置 cookie 的“命令”
- session ⇒ cookie
- 前端接收到后端的命令后,设置 cookie ,保存到浏览器内
- 前端再次请求后端的时候(相同的域名),在请求头中带上 cookie去请求
- 后端拿到前端传来的 cookie ,找到对应的 session
- 后端从 session 中可以取出基于该 session 存储的变量(用户的登陆信息、登录名)
代码编写
- UserService 与 UserServiceImpl 中添加 HttpServletRequest request
- 继续在 Impl 中编写代码
- 记录用户的登录态
- 用户脱敏
- 完整代码
@Override
public User doLogin(String userAccount, String userPassword, HttpServletRequest request) {
queryWrapper.eq("userAccount",userAccount);
queryWrapper.eq("userPassword",encryptPassword);
User user = userMapper.selectOne(queryWrapper);
//用户不存在
if (user == null) {
log.info("user login failed,userAccount cannot match userPassword");
return null;
}
/* 3.用户脱敏 */
User safetyUser = new User();
safetyUser.setId(user.getId());
safetyUser.setUsername(user.getUsername());
safetyUser.setUserAccount(user.getUserAccount());
safetyUser.setAvatarUrl(user.getAvatarUrl());
safetyUser.setGender(user.getGender());
safetyUser.setPhone(user.getPhone());
safetyUser.setEmail(user.getEmail());
safetyUser.setUserStatus(user.getUserStatus());
safetyUser.setCreateTime(user.getCreateTime());
/* 4.记录用户的登录态 */
request.getSession().setAttribute(USER_LOGIN_STATE,safetyUser);
return safetyUser;
}
后端-接口开发及测试
控制层Controller封装请求
application.yml 指定接口全局 api 【也可以可以等 代理这块去做】
servlet:
context-path: /api
@RestController适用于编写restful风格的api,返回值默认为json类型 controller 层倾向于对请求参数本身的校验,不涉及业务逻辑本身(越少越好) service 层是对业务逻辑的校验(有可能被 controller 之外的类调用)
- Controller 包下新建 UserController.java ,
- 添加 @RestController 注解(这个类中所有的请求的接口返回值,相应的数据类型都是 application json
- 添加 @RequestMapping 注解(定义请求的路径)
@RestController
@RequestMapping("/user")
public class UserController {
}
- 下载插件 Auto filling Java call arguments
- UserController 中编写 register请求
编写注册请求
封装专门用来接收请求参数的对象 model.domain 包建立一个 request 包,新建 UserRegisterRequest.java 类
继承 Serializable (序列化),打上注解
实现序列化,添加参数,打上 @lombok 注解(生成get、set方法)
- 回到 UserController
引用 UserRegisterRequest ;打上 @RequestBody 注解(使UserRegisterRequest与前端传来的参数能够对应);判断是否为空
- 完善代码逻辑
@PostMapping("/register")
public Long userRegister(@RequestBody UserRegisterRequest userRegisterRequest){
if (userRegisterRequest == null) {
return null;
}
String userAccount = userRegisterRequest.getUserAccount();
String userPassword = userRegisterRequest.getUserPassword();
String checkPassword = userRegisterRequest.getCheckPassword();
if(StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)){
return null;
}
return userService.userRegister(userAccount, userPassword, checkPassword);
}
- UserController 中编写 login 请求
- 复制 register 请求,进行修改即可【重构 UserService 中的 doLogin 方法为 userLogin 方法】
@PostMapping("/login")
public User userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request){
if (userLoginRequest == null) {
return null;
}
String userAccount = userLoginRequest.getUserAccount();
String userPassword = userLoginRequest.getUserPassword();
if(StringUtils.isAnyBlank(userAccount, userPassword)){
return null;
}
return userService.userLogin(userAccount, userPassword,request);
}
- model.domain 包下面的 request 包,新建 UserLoginRequest.java 类(复制粘贴 UserRegisterRequest.java 删除其中的 checkPassword 即可)
/**
* 用户登录请求体
*
* @Author ***
*/
@Data
public class UserLoginRequest implements Serializable {
private static final long serialVersionUID = 你生成的序列号;
private String userAccount;
private String userPassword;
}
- 测试
- 测试工具(两种方式打开)
- 删除 GET 请求,生成 POST 请求并修改
- Debug 方式启动项目
- 打断点测试
- 测试完成
再测一下逻辑删除
- 数据库中把 yupi 的 isDelete 字段改为 1
- 数据库中把 yupi 的 isDelete 字段改为 1
启动 POST 请求,测试完成