农产品销售管理系统实战——后端登录认证

前提

我们为了测试后端接口,需要用到一个工具叫做Postman,可以从Download Postman | Get Started for Free下载

下载完毕后注册用户并登录即可。

实现登录认证接口

src/main/java/cn/edu/ujn/argi/controller下创建两个新的文件夹vmrequest用于分别存放用户看到的视图类以及请求所需参数类。

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依赖注入了之前定义的JwtUtilUserMapper,并利用它们根据用户名查询用户,如果用户存在且没被删除,则进一步验证密码是否正确,如果正确则生成并返回Token。

实现注册接口

我们还是在这个控制器下添加一个注册方法,来实现注册接口。

在此之前我们先定义一个注解,专门为前端表单传来的密码长度以及所包含的字符做一个校验。在src/main/java/cn/edu/ujn/argi/util下创建valid文件夹,然后valid下创建ValidPassword.javaPasswordValidator.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了

最后修改:2025 年 04 月 15 日
如果觉得我的文章对你有用,请随意赞赏