SpringBoot front-end separation entry-level project from scratch (5)

Review

In the previous blog, we successfully wrote the login and registration interface, but there are still some problems. In this blog, we will explain the login and registration in more depth.

Data Transfer Object (DTO)

When we log in or register, we actually only use some attributes of the User object. When accessing other users, we should not query other users' passwords and other fields. At this time, if we all use the User object, it will be generated. Part of the redundant fields, these redundant parts will reduce the efficiency of data transmission as we said before, so we first start from here to create the DTO that needs to be used in different situations.

Create DTO for login and registration functions

UserLoginDTO

When the user logs in, we only need the user to enter the user name and password, so there are only two fields in UserLoginDTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserLoginDTO {
    
    
    @NotNull(message = "账号不允许为空")
    private String username;
    @NotNull(message = "密码不允许为空")
    private String password;
}

When registering, we only want the user to fill in the user name, password, and nickname to complete the registration:

UserRegisterDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRegisterDTO {
    
    
    @NotNull(message = "用户名不允许为空")
    private String username;
    @NotNull(message = "密码不允许为空")
    private String password;
    @NotNull(message = "昵称不允许为空")
    private String nickname;
}

After completing the writing of the two fields, we will go to the Controller layer and the Service layer to modify our code, and convert the originally accepted User object into the corresponding DTO object

Modify UserController

	@PostMapping("/register")
    public CommonResult<String> register(@RequestBody UserRegisterDTO urDTO) {
    
    
        userService.userRegister(urDTO);
        return new CommonResult<>(20000, "OK", "注册成功");
    }

    @PostMapping("/login")
    public CommonResult<String> login(@RequestBody UserLoginDTO ulDTO) {
    
    
        String res = userService.userLogin(ulDTO);
        return new CommonResult<>(20000, "OK", res);
    }

Modify UserService

    public void userRegister(UserRegisterDTO urDTO) {
    
    
        User user = new User();
        BeanUtils.copyProperties(urDTO,user);
        try {
    
    
            user.setId(idWorker.nextId() + "");
            user.setEnable(1);
            user.setAddtime(DateTimeTransferUtil.getNowTimeStamp());
            userDao.userRegister(user);
        } catch (Exception e) {
    
    
            throw new DuplicateKeyException("用户名重复");
        }
    }

    public String userLogin(UserLoginDTO ulDTO) {
    
    
        User trueUser = userDao.userLogin(ulDTO.getUsername());
        if (trueUser.getPassword().equals(ulDTO.getPassword())){
    
    
            return "登陆成功";
        }
        throw new InternalAuthenticationServiceException("用户名或密码有误");
    }

In the userRegister method, we use the BeanUtils.copyPropertiesmethod, which is a method provided by Spring for us, which can help us quickly read the value from a DTO and inject it into a pojo, and vice versa. For this method, you can check on the Internet. data.
Then we have completed the conversion from receiving pojo to receiving DTO, but is that the only problem? Let's go to the next part of this blog-setting user permissions

User initial permission setting

In our previous blog, we told that the permission verification of this project is mainly achieved through tokens, but the code we wrote last time did not provide users with initial permissions when registering, nor did they generate one when users log in. The token is returned to the front end, which we will slowly describe in this section.

Initial permission setting

We need to complete the initial authorization of the user after the user has successfully registered, so let us add the corresponding method to UserDao to complete this function.

Add initial permissions and query user roles

    /**
     * 用户注册时分配用户角色
     *
     * @param uid 用户id
     */
    @Insert("INSERT INTO tb_role(uid,rolename) VALUES(#{uid},default)")
    void allocateUserRole(@Param("uid") String uid);
    
	/**
     * 通过用户id查询用户角色
     *
     * @param id 用户id
     * @return 用户角色
     */
    @Select("SELECT rolename FROM tb_role WHERE uid = #{id}")
    String getUserRole(@Param("id") String id);

Here we use default because we set the default role name in the tb_role table of the database to be MEMBER

Grant initial permissions

After adding related methods to UserDao, we turned our attention back to UserService:

	public void userRegister(UserRegisterDTO urDTO) {
    
    
        User user = new User();
        BeanUtils.copyProperties(urDTO,user);
        try {
    
    
            user.setId(idWorker.nextId() + "");
            user.setEnable(1);
            user.setAddtime(DateTimeTransferUtil.getNowTimeStamp());
            userDao.userRegister(user);
            userDao.allocateUserRole(user.getId());
        } catch (Exception e) {
    
    
            throw new DuplicateKeyException("用户名重复");
        }
    }

After the user is registered, the allocateUserRole()method of adding user roles is executed , so that the initial authorization of the registration is completed. Let us first execute the program and use PostMan to test [First delete the data with the same name in the database! ]: It
Insert picture description here
prompts us to register successfully, go to the database to check the tb_user table and tb_role table, you can see that the user has successfully registered and assigned the default MEMBER role, but is this really completed? We go back to the userRegister method in UserService and add the following code

//请先将try catch的有关代码注释
userDao.userRegister(user);
//在两行代码中插入下面这一行代码
int i = 1 / 0;
userDao.allocateUserRole(user.getId());

Obviously there will be an error now, let us run the program and revisit the registration interface
Insert picture description here

At this time, we opened the database and found that the user named test1 in the database has been successfully registered, but because of an error we could not assign a role to it!
Insert picture description here
Insert picture description here

Enable transaction management

This situation is obviously something we don’t want to happen. We hope it will either succeed together or fail together, instead of half-success and half-failure as it is now. At this time, we need to add @Transactional(rollbackForClassName = "Exception.class")this annotation from the Spring framework to the UserService class. Enable the transaction function and roll back when a specified type of error occurs (here we specify any exception to roll back). Restart the project, delete the test1 that was just registered, and register with the same data. At this time, we will find that although an error occurred, neither of our steps took effect, and the realization of either success or failure together was completed. [Remember to uncomment the commented TryCatch code after the test is complete]
At this point, we have completed the user's initial permission setting, and then we will improve the user login module

Improve user login module

We mentioned earlier that after the user successfully logs in, a Token should be generated using the user's id and permission name to return to the front end. Then we will complete this function. First, we must go to UserDao and modify the userLogin method as follows:

    /**
     * 通过登录的账号查询是否存在该用户
     *
     * @param loginName 登录名,可能是username也可能是email
     * @return 返回用户密码
     */
    @Select("SELECT id,password FROM tb_user WHERE username = #{loginName}")
    User userLogin(@Param("loginName") String loginName);

Then modify UserService as follows:

    public String userLogin(UserLoginDTO ulDTO) {
    
    
        User trueUser = userDao.userLogin(ulDTO.getUsername());
        if (trueUser.getPassword().equals(ulDTO.getPassword())){
    
    
            return jwtUtil.createJWT(trueUser.getId(),userDao.getUserRole(trueUser.getId()));
        }
        throw new InternalAuthenticationServiceException("用户名或密码有误");
    }

We returned the token created with the user id and user role as required, then we carry this token in the return body of the unified request of the Controller.

    @PostMapping("/login")
    public CommonResult<String> login(@RequestBody UserLoginDTO ulDTO) {
    
    
        String token = userService.userLogin(ulDTO);
        return new CommonResult<>(20000, "OK", toekn);
    }

So our task of returning the token to the front end has also been completed. Our work to improve the user login module has also come to an end. Let us look back at the entire registration and login function. It is now relatively complete, but there are still some problems. Let us further improve the registration process.

Protect users' private information!

With the great development of the Internet, there will be news about user password leaks every once in a while. Whether it is external attacks or internal staff leaks, we should be vigilant about this, whether it is the process of writing business logic. Or we must be vigilant about the storage process of user information. At present, user passwords in the project are stored in plain text. We should use some encryption method to encrypt user passwords, so that even if the user passwords stored in the database are unfortunate Leaks cannot be cracked either.
So how should we encrypt? We can use the BCryptPasswordEncoder class that comes with the SpringSecurity framework to encrypt private information!

The author will not repeat the algorithm involved in encryption here. If you are more interested, you can visit the official documentation of SpringSecurity for learning and research.

The specific usage method is as follows, first register this class in the main startup class:

	@Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
    
    
        return new BCryptPasswordEncoder();
    }

Then make the following modifications in UserService:
First, inject this encryption class through the construction method

	private final UserDao userDao;
    private final IdWorker idWorker;
    private final JwtUtil jwtUtil;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    
    public UserService(UserDao userDao, IdWorker idWorker, JwtUtil jwtUtil, BCryptPasswordEncoder bCryptPasswordEncoder) {
    
    
        this.userDao = userDao;
        this.idWorker = idWorker;
        this.jwtUtil = jwtUtil;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

Then encrypt the user's password when the user registers

public void userRegister(UserRegisterDTO urDTO) {
    
    
        User user = new User();
        BeanUtils.copyProperties(urDTO,user);
        try {
    
    
            user.setId(idWorker.nextId() + "");
            user.setEnable(1);
            user.setAddtime(DateTimeTransferUtil.getNowTimeStamp());
            //进行加密
            user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
            userDao.userRegister(user);
            userDao.allocateUserRole(user.getId());
        } catch (Exception e) {
    
    
            throw new DuplicateKeyException("用户名重复");
        }
    }

When the user logs in, we have to judge whether the user's password is the same as the password stored in the database. Since the password stored during registration has been encrypted, how do we judge it? In fact, the author of the framework has already thought of this. He provided us with a matches()method to judge. If the same is true, if the difference is false, then we will modify the user login module:

    public String userLogin(UserLoginDTO ulDTO) {
    
    
        User trueUser = userDao.userLogin(ulDTO.getUsername());
        //前面放的是用户输入的密码,后面是加密过的密码
        if (bCryptPasswordEncoder.matches(ulDTO.getPassword(), trueUser.getPassword())) {
    
    
            return jwtUtil.createJWT(trueUser.getId(), userDao.getUserRole(trueUser.getId()));
        }
        throw new InternalAuthenticationServiceException("用户名或密码有误");
    }

So here we have completed the encryption of user privacy information, start the project, let us test the two functions through postman~
Insert picture description here
First, test the registration interface (remember to delete the previous test account before testing!), found The registration is successful, come to the database, we can see that the user's password has been encrypted! Insert picture description here
Next, let us test the login interface: we
Insert picture description here
also found that the login was successful, and the generated token was successfully obtained! With the token front end, we can directly get the user's id and permissions every time we request, which provides strong support for our subsequent business code writing.

So far, the content of this blog has basically come to an end. After this blog, our login and registration interface has been relatively perfect, but is there really no problem at all? Readers are asked to think for themselves, and the author will reveal the answer in the next blog.
The content of this blog has ended here. If you have any questions about the content of the blog, you can contact the author by private message. If this article is useful to you, I hope you can like it, thank you~

Guess you like

Origin blog.csdn.net/Alfalfa99/article/details/108756863