农产品销售管理系统实战——后端登录认证
前提
我们为了测试后端接口,需要用到一个工具叫做Postman,可以从Download Postman | Get Started for Free下载
下载完毕后注册用户并登录即可。
实现登录认证接口
在src/main/java/cn/edu/ujn/argi/controller
下创建两个新的文件夹vm
和request
用于分别存放用户看到的视图类以及请求所需参数类。
在src/main/java/cn/edu/ujn/argi/controller/request
下创建文件LoginRequest.java
文件用于表示登录请求所需参数,并利用注解设置参数校验
package cn.edu.ujn.argi.controller.request;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class LoginRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
这里可以看到我们是用一个新的注解叫做@NotBlank
,它专门作用于字符串参数,如果参数为空、null或只含有空白字符,则验证失败。
添加LoginController.java
文件,还是像之前那样我们声明一个@RestController
注解,然后设置控制器的路径为@RequestMapping("/loginController")
,然后再里面添加login
方法,映射路径为login
请求类型为POST。
package cn.edu.ujn.argi.controller;
import java.io.UnsupportedEncodingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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.RestController;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cn.edu.ujn.argi.controller.request.LoginRequest;
import cn.edu.ujn.argi.mapper.UserMapper;
import cn.edu.ujn.argi.model.UserModel;
import cn.edu.ujn.argi.util.PwdHelper;
import cn.edu.ujn.argi.util.Result;
import cn.edu.ujn.argi.util.ResultHelper;
import cn.edu.ujn.argi.util.jwt.JwtUtil;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/loginController")
public class LoginController {
@Autowired
private UserMapper userMapper;
@Autowired
private JwtUtil jwt;
@PostMapping("login")
public ResponseEntity<Result> login(@Valid @RequestBody LoginRequest req){
String username = req.getUsername();
UserModel user = userMapper.selectOne(
new QueryWrapper<UserModel>()
.eq("name", req.getUsername())
.eq("is_deleted", false)
);
if(user == null){
return ResultHelper.error();
}
String salt = user.getSalt();
String origin = req.getPassword() + salt;
try {
if(!PwdHelper.md5Hash(origin).equals(user.getPassword())){
return ResultHelper.error();
}
String token = jwt.generateToken(user.getName(), user.getRole(), user.getId());
if(token == null){
return ResultHelper.error();
}
return ResultHelper.success(token);
} catch (UnsupportedEncodingException e) {
return ResultHelper.error();
}
}
}
再这个类中我们用@Autowired
依赖注入了之前定义的JwtUtil
和UserMapper
,并利用它们根据用户名查询用户,如果用户存在且没被删除,则进一步验证密码是否正确,如果正确则生成并返回Token。
实现注册接口
我们还是在这个控制器下添加一个注册方法,来实现注册接口。
在此之前我们先定义一个注解,专门为前端表单传来的密码长度以及所包含的字符做一个校验。在src/main/java/cn/edu/ujn/argi/util
下创建valid
文件夹,然后valid
下创建ValidPassword.java
和PasswordValidator.java
// ValidPassword
package cn.edu.ujn.argi.util.valid;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = PasswordValidator.class)
public @interface ValidPassword {
String message() default "密码必须包含至少一个大写字母、一个小写字母和一个数字,长度在 8 到 20 位之间";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// PasswordValidator
package cn.edu.ujn.argi.util.valid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.regex.Pattern;
public class PasswordValidator implements ConstraintValidator<ValidPassword, String> {
private static final String PASSWORD_REGEX = "^(?:\\d{6,20}|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*(),.?\":{}|<>])[A-Za-z\\d!@#$%^&*(),.?\":{}|<>]{6,20})$";
private static final Pattern PASSWORD_PATTERN = Pattern.compile(PASSWORD_REGEX);
@Override
public void initialize(ValidPassword constraintAnnotation) {
// 初始化方法,可用于获取注解中的属性值
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.trim().isEmpty()) {
return false;
}
return PASSWORD_PATTERN.matcher(value).matches();
}
}
还是像之前那样,在src/main/java/cn/edu/ujn/argi/controller/request
中添加请求类RegisterRequest.java
,并使用刚才定义的@ValidPassword
package cn.edu.ujn.argi.controller.request;
import cn.edu.ujn.argi.util.valid.ValidPassword;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class RegisterRequest {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
@ValidPassword
private String password;
}
在src/main/java/cn/edu/ujn/argi/controller/LoginController.java
中实现register
方法,下方代码之前的部分已经省略
@PostMapping("register")
public ResponseEntity<Result> register(@Valid @RequestBody RegisterRequest req){
UserModel user = userMapper.selectOne(
new QueryWrapper<UserModel>()
.eq("name", req.getUsername())
);
if(user != null){
return ResultHelper.error("用户名已经存在", null);
}
String salt = PwdHelper.getRandomSalt();
String origin = req.getPassword() + salt;
UserModel newUser = new UserModel();
newUser.setName(req.getUsername());
newUser.setSalt(salt);
newUser.setRole("user");
try {
String hashVal = PwdHelper.md5Hash(origin);
newUser.setPassword(hashVal);
userMapper.insert(newUser);
return ResultHelper.success();
}catch (Exception e){
return ResultHelper.error();
}
}
我们再来添加一个商家注册的接口
@PostMapping("sellerRegister")
public ResponseEntity<Result> sellerRegister(@Valid @RequestBody RegisterRequest req){
UserModel user = userMapper.selectOne(
new QueryWrapper<UserModel>()
.eq("name", req.getUsername())
);
if(user != null){
return ResultHelper.error("用户名已经存在", null);
}
String salt = PwdHelper.getRandomSalt();
String origin = req.getPassword() + salt;
UserModel newUser = new UserModel();
newUser.setName(req.getUsername());
newUser.setSalt(salt);
newUser.setRole("seller");
try {
String hashVal = PwdHelper.md5Hash(origin);
newUser.setPassword(hashVal);
userMapper.insert(newUser);
return ResultHelper.success();
}catch (Exception e){
return ResultHelper.error();
}
}
Postman测试
启动项目,然后打开Postman,点击Collections->+->Blank collection
然后点击...->Add request,并将请求类型改为POST,URL填写http://localhost:8080/api/loginController/register
,并将请求重命名register
点击body,然后选中raw
,并将右侧设置为json,输入以下内容
保存后点击Send按钮,从下方可以看到请求成功了。
现在我们的数据库中已经存在一个用户了,我们接下来测试登录接口。还是再Postman中添加一个Post请求的测试,命名为login,然后设置URL以及请求参数如下。
很好,看来我们已经可以拿到Token了