Make writing a habit together! This is the second day of my participation in the “Gold Digging Day New Plan · April More text challenge”. Click here for more details.

User registration

1 User – Creates a data table

1. Run the use command to select the store database.

USE store;
Copy the code

2. Create a T_user user table in the Store database.

CREATE TABLE t_user (
	uid INT AUTO_INCREMENT COMMENT 'user id',
	username VARCHAR(20) NOT NULL UNIQUE COMMENT 'Username'.password CHAR(32) NOT NULL COMMENT 'password'.salt CHAR(36) COMMENT 'salt value',
	phone VARCHAR(20) COMMENT 'Phone number',
	email VARCHAR(30) COMMENT 'Email',
	gender INT COMMENT 'Gender: 0-female, 1-male',
	avatar VARCHAR(50) COMMENT 'avatar',
	is_delete INT COMMENT 'Deleted or not: 0- Not deleted, 1- Deleted',
	created_user VARCHAR(20) COMMENT 'Log - Creator',
	created_time DATETIME COMMENT 'Log - Creation Time',
	modified_user VARCHAR(20) COMMENT 'Log - Last modified Executor',
	modified_time DATETIME COMMENT 'Log - Last modified time',
	PRIMARY KEY (uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code

2 User – Creates an entity class

1. Many of the entity classes in the project will have four logging attributes, so before creating the entity classes, create a base class for these entity classes and declare the four logging attributes in the base class. Create a BaseEntity class under the com.cy.store.entity package as the base class for the entity class.

package com.cy.store.entity;
import java.io.Serializable;
import java.util.Date;

/** The base class of the entity class */
public class BaseEntity implements Serializable {
    private String createdUser;
    private Date createdTime;
    private String modifiedUser;
    private Date modifiedTime;

    // Generate: Getter and Setter, toString()
}

Copy the code

Because this base class is intended to be inherited by other entity classes, it should be declared abstract.

2. Create an entity class for com.cy.store.entity.User User data, inherit it from BaseEntity class, and declare the corresponding attributes in the data table in the class.

package com.cy.store.entity;
import java.io.Serializable;
import java.util.Objects;

/** User data entity class */
public class User extends BaseEntity implements Serializable {
    private Integer uid;
    private String username;
    private String password;
    private String salt;
    private String phone;
    private String email;
    private Integer gender;
    private String avatar;
    private Integer isDelete;

	// Generate: Getter and Setter, Generate hashCode() and equals(), toString()
}
Copy the code

User – registration – persistence layer

3.1 Preparations

1. Under the SRC/test/Java com. Cy. Store. StoreApplicationTests test class to write and perform the “access to the database connection” unit testing, to check up on the configuration of the database connection is correct.

@Autowired
private DataSource dataSource;

@Test
public void getConnection(a) throws Exception {
	System.out.println(dataSource.getConnection());
}
Copy the code

2. Perform under SRC/test/Java com. Cy. ToreApplicationTests contextLoads in the test class () test method, to check whether the test environment is normal.

3.2 Planning SQL Statements to Be Executed

1. The essence of user registration is to insert data into the user table. The SQL statement to be executed is roughly as follows:

INSERT INTOT_user (list of fields other than UID)VALUES(List of matched values)Copy the code

2. Since the user name field in the data table is designed as UNIQUE, you should also check whether the user name is already registered before performing the insert. Therefore, you need to have “query user data by user name” function. SQL statements that need to be executed are as follows:

SELECT * FROM t_user WHERE username=?
Copy the code

3.3 Interfaces and Abstract methods

1. Create com. Cy. Store. Mapper. UserMapper interface and an abstract method is added at the interface.

package com.cy.mapper;
import com.cy.store.entity.User;

/** Persistence layer interface to handle user data operations */
public interface UserMapper {
    /** * Insert user data *@paramUser User data *@returnNumber of affected rows */
    Integer insert(User user);

    /** * Query user data by user name *@paramUsername username *@returnMatching user data, or null */ if there is no matching user data
    User findByUsername(String username);
}
Copy the code

2. Since this is the first time in the project that a persistence layer interface is created, the @mapperscan (“com.cy.store.mapper”) annotation should also be added before StoreApplication starts the class to configure the location of the interface file.

After MyBatis integration with Spring, it is necessary to realize the mapping relationship between entities and data tables. The mapping between entities and data tables can be implemented by adding @mapper annotations to the Mapper interface. However, it is recommended to add @mapperscan annotations to the SpringBoot boot class directly in the future. This will be more convenient, so that there is no need to add @mapper annotations to every Mapper.

@SpringBootApplication
@MapperScan("com.cy.store.mapper")
public class StoreApplication {

	public static void main(String[] args) { SpringApplication.run(StoreApplication.class, args); }}Copy the code

3.4 Configuring SQL Mapping

1. Create a mapper folder in SRC /main/resources and create a usermapper. XML mapping file in this folder to configure the mapping of the two abstract methods.


      
<! DOCTYPEmapper
        PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper">
    <resultMap id="UserEntityMap" type="com.cy.store.entity.User">
        <id column="uid" property="uid"/>
        <result column="is_delete" property="isDelete"/>
        <result column="created_user" property="createdUser"/>
        <result column="created_time" property="createdTime"/>
        <result column="modified_user" property="modifiedUser"/>
        <result column="modified_time" property="modifiedTime"/>
    </resultMap>

    <! -- Insert User data: Integer insert(User User) -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="uid">
        INSERT INTO
            t_user (username, password, salt, phone, email, gender, avatar, is_delete, created_user, created_time, modified_user, modified_time)
        VALUES
        (#{username}, #{password}, #{salt}, #{phone}, #{email}, #{gender}, #{avatar}, #{isDelete}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
    </insert>

    <! User findByUsername(String username) -->
    <select id="findByUsername" resultMap="UserEntityMap">
        SELECT
            *
        FROM
            t_user
        WHERE
            username = #{username}
    </select>
</mapper>
Copy the code

2. Since this is the first time SQL mapping is used in the project, you need to add the configuration of the Mybatis. Mapper-locations property in application.properties to specify the location of the XML file.

mybatis.mapper-locations=classpath:mapper/*.xml
Copy the code

3. After the completion of the timely execution of the unit test, check whether the functions developed above can run correctly. In SRC/test/Java create com. Cy. Store. Mapper. UserMapperTests unit test class, before the test class statement add @ RunWith (SpringRunner. Class) and @ SpringBootTest annotations, And declare persistence layer objects in the test class and inject values through autowiring.

The @runwith (springrunner.class) annotation is a test launcher that loads SpringBoot test annotations.

package com.cy.store.mapper;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

// @runwith (springrunner.class) annotation is a test launcher that can load Springboot test annotations
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTests {
    @Autowired
    private UserMapper userMapper;

}
Copy the code

4. If the error message “Could not autowire. No beans of ‘userMapper’ type found” is displayed in step 4, the userMapper cannot be automatically assembled. The solution is to set the Severity under the Autowiring for bean class option to Warning.

5. Then write two test methods to unit test the two functions completed above.

The unit Test method must be public, the return type of the method must be void, the method must not have a parameter list, and the method must be decorated by the @test annotation.

@Test
public void insert(a) {
    User user = new User();
    user.setUsername("user01");
    user.setPassword("123456");
    Integer rows = userMapper.insert(user);
    System.out.println("rows=" + rows);
}

@Test
public void findByUsername(a) {
    String username = "user01";
    User result = userMapper.findByUsername(username);
    System.out.println(result);
}
Copy the code

If there is an org. Apache. Ibatis. Binding. BindingException: Invalid bound statement (not found) abnormal possible reasons:

1. The mapper Folder type created under the resources file is not correctly selected (Eclipse selects Folder, IDEA selects Directory).

2. The namespace attribute of the mapper tag of the mapping file is not correctly mapped to the DAO layer interface, or the attribute mybatis. Mapper-locations in application.properties is not correctly configured with the XML mapping file.

4 User-registration-service layer

4.1 Service Positioning

1. Service: A complete set of data processing processes, usually represented as a function that users think, but corresponding to multiple data operations during development. In a project, the business controls the processing flow and related logic of each “function” (such as registration, login, etc.).

2. Process: What to do first, and then what to do. For example, during registration, check whether the user name is occupied and then decide whether to complete registration.

3. Logic: What you can and can’t do. For example, if the user name is occupied during registration, the user cannot be registered. Otherwise, registration is allowed.

4. The main function of a service is to ensure data security and data integrity and validity.

4.2 Abnormal Planning

1. About exceptions

1. Please list at least ten abnormalities that you recognize:

Throwable
	Error
		OutOfMemoryError(OOM)
	Exception
		SQLException
		IOException
			FileNotFoundException
		RuntimeException
			NullPointerException
			ArithmeticException
			ClassCastException
			IndexOutOfBoundsException
				ArrayIndexOutOfBoundsException
				StringIndexOutOfBoundsException
Copy the code

2. Abnormal handling methods and principles:

Exception handling methods are as follows: Catch processing (try… catch… Finally), declare throw (throw/throws). If the current method is suitable for processing, the processing is captured; If the current method is not suitable for processing, a throw is declared.

2. Plan exceptions

1. To facilitate the unified management of custom exception, should first create com. Cy. Store. Service. Ex. ServiceException the base class for custom exception is unusual, inherited from RuntimeException class, and from the parent class generation subclass five construction method.

package com.cy.store.service.ex;

/** The base class for business exceptions */
public class ServiceException extends RuntimeException {
	// Override Methods...  
}
Copy the code

2. When a user registration, may lead to normal registration for user name being used, the need to throw the exception user name was taken, so you can design a user name repeat. Com cy. Store. Service. Ex. UsernameDuplicateException exception class, Five constructors that inherit from the ServiceException class and generate subclasses from their parent class.

package com.cy.store.service.ex;

/** Duplicate user name exception */
public class UsernameDuplicateException extends ServiceException {
    // Override Methods...
}
Copy the code

3. During user registration, an INSERT operation will be performed on the database, which may fail. Create cn. Tedu. Store. Service. Ex. InsertException ` exception classes, inherit from ServiceException classes, and five from the parent class to generate a subclass constructor.

package com.cy.store.service.ex;

/** Insert data exception */
public class InsertException extends ServiceException {
    // Override Methods...
}
Copy the code

4. All custom exceptions should be descendants of RuntimeException. The current inheritance structure for exceptions in the project is shown below.

RuntimeException
	-- ServiceException
		-- UsernameDuplicateException
		-- InsertException
Copy the code

4.3 Interfaces and Abstract Methods

1. To create com. Cy. Store. Service. IUserService business layer interface, and an abstract method is added at the interface.

package com.cy.store.service;
import com.cy.store.entity.User;

/** Business layer interface for processing user data */
public interface IUserService {
    /** * User registration *@paramUser User data */
    void reg(User user);
}
Copy the code

2. Create business layer interfaces for decoupling. Design principles for abstract methods at the business layer.

1. Design return value types only on the premise that the operation succeeds, regardless of the case of operation failure; 2. The method name can be customized and is usually related to the function operated by the user. 3. The parameter list of the method is determined according to the specific business function performed, and the data needed is designed. In general, the parameters need to be sufficient to invoke the persistence layer corresponding function; At the same time, the client can pass the parameters to the controller; 4. An exception is thrown in the method to indicate that the operation failed.Copy the code

4.4 Implement abstract methods

1. Create com. Cy. Store. Service. Impl. UserServiceImpl business layer implementation class, and implement IUserService interface. Add the @Service annotation before the class and add the persistence layer UserMapper object to the class.

package com.cy.store.service.impl;
import com.cy.store.entity.User;
import com.cy.store.mapper.UserMapper;
import com.cy.store.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/** Business layer implementation class for processing user data */
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public void reg(User user) {
        // TODO}}Copy the code

2. The UserServiceImpl class needs to override the abstract methods in the IUserService interface.

@Override
public void reg(User user) {
	// Get the registered user name based on the parameter user object
	// Call the User findByUsername(String username) method on the persistence layer to query User data based on the Username
	// Check whether the query result is not null
	/ / is: the user name has been occupied, it throws UsernameDuplicateException anomalies
	
	// Create the current time object
	// Complete data: encrypted password
	// Complete data: salt value
	// Complete data: isDelete(0)
	// Complete data: 4 log attributes

	// Indicates that the user name is not occupied, so registration is allowed
	// Call the persistence layer Integer INSERT (User User) method to register and get the return value (number of affected rows)
	// Determine if the number of affected rows is not 1
	// Yes: An InsertException is thrown if some kind of error occurs while inserting data
	
}
Copy the code

3. The specific implementation process of reg() method.

@Service
public class UserServiceImpl implements IUserService {
	@Autowired
	private UserMapper userMapper;
	
	@Override
	public void reg(User user) {
		// Get the registered user name based on the parameter user object
		String username = user.getUsername();
		// Call the User findByUsername(String username) method on the persistence layer to query User data based on the Username
		User result = userMapper.findByUsername(username);
		// Check whether the query result is not null
		if(result ! =null) {
			/ / is: the user name has been occupied, it throws UsernameDuplicateException anomalies
			throw new UsernameDuplicateException("User name trying to register [" + username + "] Already occupied");
		}
		
		// Create the current time object
		Date now = new Date();
		// Complete data: encrypted password
		String salt = UUID.randomUUID().toString().toUpperCase();
		String md5Password = getMd5Password(user.getPassword(), salt);
		user.setPassword(md5Password);
		// Complete data: salt value
		user.setSalt(salt);
		// Complete data: isDelete(0)
		user.setIsDelete(0);
		// Complete data: 4 log attributes
		user.setCreatedUser(username);
		user.setCreatedTime(now);
		user.setModifiedUser(username);
		user.setModifiedTime(now);
		
		// Indicates that the user name is not occupied, so registration is allowed
		// Call the persistence layer Integer INSERT (User User) method to register and get the return value (number of affected rows)
		Integer rows = userMapper.insert(user);
		// Determine if the number of affected rows is not 1
		if(rows ! =1) {
			// Yes: An InsertException is thrown if some kind of error occurs while inserting data
			throw new InsertException("Unknown error occurred adding user data, please contact system administrator."); }}/** * Perform password encryption *@paramPassword Original password *@paramSalt salt value *@returnEncrypted ciphertext */
	private String getMd5Password(String password, String salt) {
		/* * Encryption rules: * 1, ignore the strength of the original password * 2, use UUID as the salt value, concatenate the left and right sides of the original password * 3, cyclic encryption 3 times */
		for (int i = 0; i < 3; i++) {
			password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
		}
		returnpassword; }}Copy the code

4. After the completion of the SRC/test/Java create com. Cy. Store. Service. UserServiceTests test class, write and execute user registration business layer of unit tests.

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTests {
	@Autowired
	private IUserService iUserService;
	
	@Test
	public void reg(a) {
		try {
			User user = new User();
			user.setUsername("lower");
			user.setPassword("123456");
			user.setGender(1);
			user.setPhone("17858802974");
			user.setEmail("[email protected]");
			user.setAvatar("xxxx");
			iUserService.reg(user);
			System.out.println("Registration successful!");
		} catch (ServiceException e) {
			System.out.println("Registration failed!"+ e.getClass().getSimpleName()); System.out.println(e.getMessage()); }}}Copy the code

4.5 Password Encryption

Password encryption can effectively prevent account security problems caused by data leakage. In general, programmers do not need to worry about the algorithms used in the encryption process, because there are so many mature encryption algorithms that can be used directly. However, all encryption algorithms are not suitable for encrypting passwords, because encryption algorithms can work backwards. That is, if you can get all the parameters in the encryption process, you can get the original text according to the ciphertext.

When encrypting passwords, the message digest algorithm is used. The characteristics of message digest algorithm are:

1. When the original text is the same, the summary data obtained by the same summarization algorithm must be the same; 2. Use the same summarization algorithm to calculate, no matter what the length of the original text is, the length of the summary data is fixed; 3. If the abstract data are the same, then the original text is almost the same, but it may be very unlikely to be different.Copy the code

Different text, in a certain probability can get the same summary data, this phenomenon is called collision.

Take the MD5 algorithm as an example. The result is a 128-bit binary number. In the field of password application, the minimum and maximum value of password length are usually limited, but the type of password is limited, and the probability of collision can be considered as non-existence.

Common Digest algorithms include Secure Hash Argorithm (SHA) family and Message Digest (MD) family.

The cracking of MD5 algorithm mainly comes from two aspects. One is the cracking of Professor Wang Xiaoyun. In academic terms, the cracking is actually the collision of the message summarization algorithm, that is, to find two different original texts but corresponding to the same abstract faster, rather than the imaginary “original text obtained by reverse operation based on ciphertext”. The other is the so-called “online cracking”, is to use the database to record a large number of original text and the corresponding relationship between the abstract, when trying to “cracking” is essentially query this database, according to the abstract query the original text.

To ensure password security, the following encryption rules must be met:

1. Users are required to use a more secure original password. 2. Add salt; 3. Multiple encryption; 4. Combine all of the above applications.Copy the code

5 User – Registration – Controller

5.1 Creating a Response Result Class

Create com. Cy. Store. Util. JsonResult type response results.

package com.cy.store.util;
import java.io.Serializable;

/** * Response result class *@param<E> The type of response data */
public class JsonResult<E> implements Serializable {
    /** Status code */
    private Integer state;
    /** Status description */
    private String message;
    / * * * / data
    private E data;

    public JsonResult(a) {
        super(a); }public JsonResult(Integer state) {
        super(a);this.state = state;
    }

    /** call */ when an exception occurs
    public JsonResult(Throwable e) {
        super(a);// Get exception information in the exception object
        this.message = e.getMessage();
    }

    public JsonResult(Integer state, E data) {
        super(a);this.state = state;
        this.data = data;
    }

    // Generate: Getter and Setter
}
Copy the code

5.2 Design Request

Design the request submitted by the user and design the response:

Request path: /users/reg Request parameter: User User Request type: POST Response result: JsonResult<Void>Copy the code

5.3 Processing Requests

1. Create com. Cy. Store. Controller. The UserController controller class, before the declaration of a class to add @ RestController and @ RequestMapping (” users “) annotation, Add the IUserService business object to the class and decorate it with the @AutoWired annotation.

package com.cy.store.controller;
import com.cy.store.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/** The controller class that handles user-related requests */
@RestController
@RequestMapping("users")
public class UserController {
    @Autowired
    private IUserService userService;
}
Copy the code

2. Then add a user registration method to the class to handle the request.

@RequestMapping("reg")
public JsonResult<Void> reg(User user) {
    // Create the return value
    JsonResult<Void> result = new JsonResult<Void>();
    try {
        // Call the business object to perform the registration
        userService.reg(user);
        // The response succeeded
        result.setState(200);
    } catch (UsernameDuplicateException e) {
        // The user name is occupied
        result.setState(4000);
        result.setMessage("User name already in use");
    } catch (InsertException e) {
        // Insert data is abnormal
        result.setState(5000);
        result.setMessage("Registration failed. Please contact your system administrator.");
    }
    return result;
}
Copy the code

3. Start after the completion of the project, open the browser to http://localhost:8080/users/reg? Username = controller&Password =123456 Requests a test.

{
    state: 200,
    message: null,
    data: null
}
Copy the code

5.4 Controller Layer Adjustment

1. Then create a base class that provides the controller com. Cy. Store. Controller. BaseController, said in a defined response status code of success and unified handling exceptions.

The @ExceptionHandler annotation is used to uniformly handle exceptions thrown by the method. When we use this annotation, we need to define an ExceptionHandler and annotate this method with the @exceptionhandler annotation. This method will handle exceptions thrown by other methods in the class (which were annotated by the @requestmapping annotation). The @ExceptionHandler annotation allows you to add an argument to the class of an exception class, indicating that the method specifically handles that exception.

package com.cy.store.controller;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.ServiceException;
import com.cy.store.service.ex.UsernameDuplicateException;
import com.cy.store.util.JsonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;

/** The base class of the controller class */
public class BaseController {
    /** Indicates the status code of successful operation */
    public static final int OK = 200;

    / * *@ExceptionHandlerUsed to uniformly handle exceptions thrown by methods */
    @ExceptionHandler(ServiceException.class)
    public JsonResult<Void> handleException(Throwable e) {
        JsonResult<Void> result = new JsonResult<Void>(e);
        if (e instanceof UsernameDuplicateException) {
            result.setState(4000);
        } else if (e instanceof InsertException) {
            result.setState(5000);
        }
        returnresult; }}Copy the code

2. Finally simplify the user registration reg() method in the UserController controller class.

/** The controller class that handles user-related requests */
@RestController
@RequestMapping("users")
public class UserController extends BaseController {
    @Autowired
    private IUserService userService;

    @RequestMapping("reg")
    public JsonResult<Void> reg(User user) {
        // Call the business object to perform the registration
        userService.reg(user);
        / / return
        return newJsonResult<Void>(OK); }}Copy the code

3. Start after the completion of the project, open the browser to http://localhost:8080/users/reg? Username = controller&Password =123456 Requests a test.

6 User-Registration-Front page

1. Will the computer store front pages folder under the static resources: resources page bootstrap3, js, CSS, images, web, index. The HTML related resources is copied to the project SRC/main/resources/static folder.

2. At the end of the body tag in the register. HTML page, add the script tag for writing JavaScript programs. The access name of the project needs to be added to the requested URL.

The serialize() method creates a URL-encoded text string by serializing the form values.

<script type="text/javascript">
    $("#btn-reg").click(function() {
        console.log($("#form-reg").serialize());
        $.ajax({
            url: "/users/reg".type: "POST".data: $("#form-reg").serialize(),
            dataType: "json".success: function(json) {
                if (json.state == 200) {
                    alert("Registration successful!");
                    // location.href = "login.html";
                } else {
                    alert("Registration failed!"+ json.message); }}}); }); </script>Copy the code

3. Start after the completion of the project, open the browser to http://localhost:8080/web/register.html page and to register.

Note: Since there is no authentication data, you can successfully register without a user name or password.

The user login

1 User-logon-persistence layer

1.1 Planning SQL Statements to Be Executed

The SQL statement required for user login is to query user data based on the user name and check whether the password is correct. The SQL statement looks like this:

SELECT * FROM t_user WHERE username=?
Copy the code

Note: The background development corresponding to the above SQL statement has been completed, no need to develop again.

1.2 Interfaces and Abstract methods

Note: No need to develop again.

1.3 Configuring SQL Mapping

Note: No need to develop again.

2 User-logon-service layer

2.1 Abnormal Planning

1. If the user name does not exist the login fails, throwing com. Cy. Store. Service. Ex. UserNotFoundException abnormalities, and five from the parent class to generate a subclass constructor.

package com.cy.store.service.ex;

/** User data does not exist */
public class UserNotFoundException extends ServiceException {
    // Override Methods...
}
Copy the code

2. If the value of the isDelete field is 1, the current user data is marked as deleted. Perform the login failure operation and throw UserNotFoundException.

3. If the wrong password to log in failure, at the same time throw com. Cy. Store. Service. Ex. PasswordNotMatchException anomalies.

package com.cy.store.service.ex;

/** Failed to verify the password */
public class PasswordNotMatchException extends ServiceException {
    // Override Methods...
}
Copy the code

4. Create more UserNotFoundException and PasswordNotMatchException exception classes, more than exception class should inherit from ServiceException class.

2.2 Interfaces and Abstract methods

Add an abstract method to the login function in the IUserService interface.

/** * User login *@paramUsername username *@param"Password," password *@returnSuccessful user data */
User login(String username, String password);
Copy the code

After a successful login, you need to obtain the user ID for subsequent identification. In addition, you need to obtain the user name and profile picture of the user for display on the software interface. The data type that can encapsulate the ID, user name, and profile picture of the user is used as the return value type of the login method.

2.3 Implement abstract methods

1. Add the login(String Username, String Password) method to the UserServiceImpl class and analyze the service logic.

@Override
public User login(String username, String password) {
	// Call userMapper's findByUsername() method to query user data according to the parameter username
	
	// Check whether the query result is null
	// Yes: A UserNotFoundException is thrown
	
	// Check whether isDelete in the query result is 1
	// Yes: A UserNotFoundException is thrown
	
	// Get the salt value from the query result
	// Call the getMd5Password() method to combine the password and salt parameters for encryption
	// Check whether the password in the query result is inconsistent with the encrypted password
	/ / is: throw PasswordNotMatchException anomalies
	
	// Create a new User object
	// Encapsulate uid, username, and Avatar in the query result into a new user object
	// Returns the new user object
	
	return null;
}
Copy the code

2. Login (String username, String password) method in the code concrete implementation.

@Override
public User login(String username, String password) {
    // Call userMapper's findByUsername() method to query user data according to the parameter username
    User result = userMapper.findByUsername(username);
    // Check whether the query result is null
    if (result == null) {
        // Yes: A UserNotFoundException is thrown
        throw new UserNotFoundException("User data does not exist error");
    }

    // Check whether isDelete in the query result is 1
    if (result.getIsDelete() == 1) {
        // Yes: A UserNotFoundException is thrown
        throw new UserNotFoundException("User data does not exist error");
    }

    // Get the salt value from the query result
    String salt = result.getSalt();
    // Call the getMd5Password() method to combine the password and salt parameters for encryption
    String md5Password = getMd5Password(password, salt);
    // Check whether the password in the query result is inconsistent with the encrypted password
    if(! result.getPassword().equals(md5Password)) {/ / is: throw PasswordNotMatchException anomalies
        throw new PasswordNotMatchException("Error in password validation failure");
    }

    // Create a new User object
    User user = new User();
    // Encapsulate uid, username, and Avatar in the query result into a new user object
    user.setUid(result.getUid());
    user.setUsername(result.getUsername());
    user.setAvatar(result.getAvatar());
    // Returns the new user object
    return user;
}
Copy the code

3. Write and complete the unit tests in UserServiceTests.

@Test
public void login(a) {
    try {
        String username = "lower";
        String password = "123456";
        User user = iUserService.login(username, password);
        System.out.println("Login successful!" + user);
    } catch (ServiceException e) {
        System.out.println("Login failed!" + e.getClass().getSimpleName());
        System.out.println(e.getMessage());
    }
Copy the code

Note: Do not try to log in with incorrect data, such as data from new users tested earlier by the persistence layer, and remove this data from the table.

3 User – Login – Controller

3.1 Troubleshooting exceptions

When handling the user login function, raise the UserNotFoundException and PasswordNotMatchException anomalies in the business layer, and the two exceptions are not been processed. You should add these two branches to the exception handling method of the BaseController class.

@ExceptionHandler(ServiceException.class)
public JsonResult<Void> handleException(Throwable e) {
	JsonResult<Void> result = new JsonResult<Void>(e);
	if (e instanceof UsernameDuplicateException) {
		result.setState(4000);
	} else if (e instanceof UserNotFoundException) {
		result.setState(4001);
	} else if (e instanceof PasswordNotMatchException) {
		result.setState(4002);
	} else if (e instanceof InsertException) {
		result.setState(5000);
	}
	return result;
}
Copy the code

3.2 Design Request

Design the request submitted by the user and design the response:

Request path: /users/login Request parameters: String username, String password Request type: POST Response result: JsonResult<User>Copy the code

3.3 Processing Requests

1. Add the login(String Username, String Password) method to handle login requests in the UserController class.

@RequestMapping("login")
public JsonResult<User> login(String username, String password) {
	// Call the business object's method to perform the login and get the return value
	
	// Encapsulate the above return value and status code OK in the response result and return
	
	return null;
}
Copy the code

2. The login(String username, String Password) method code to process the login request.

@RequestMapping("login")
public JsonResult<User> login(String username, String password) {
	// Call the business object's method to perform the login and get the return value
	User data = userService.login(username, password);
	// Encapsulate the above return value and status code OK in the response result and return
	return new JsonResult<User>(OK, data);
}
Copy the code

4. Start after the completion of the project, visit http://localhost:8080/users/login? Username =Tom&password=1234 Requests to log in.

4 User-Login-Front page

1. At the end of the body tag in the login. HTML page, add the script tag for writing JavaScript programs.

<script type="text/javascript">
    $("#btn-login").click(function() {
    $.ajax({
        url: "/users/login".type: "POST".data: $("#form-login").serialize(),
        dataType: "json".success: function(json) {
            if (json.state == 200) {
                alert("Login successful!");
                location.href = "index.html";
            } else {
                alert("Login failed!"+ json.message); }}}); }); </script>Copy the code

2. Start after the completion of the project, open the browser to http://localhost:8080/web/login.html page and log in.

The interceptor

Intercepting requests in Spring MVC is done through the HandlerInterceptor, which intercepts the address of the request. Define an interceptor in Spring MVC that implements the HandlerInterceptor interface.

1 HandlerInterceptor

1.1 preHandle () method

This method will be called before the request is processed. The SpringMVC Interceptor is a chained invocation, and multiple interceptors can exist in an application or a request at the same time. Each Interceptor call is executed in the order in which it is declared, and the first one is the Interceptor’s preHandle() method, so there can be some pre-initialization or a pre-processing of the current request. You can also make some judgments in this method to determine whether the request should proceed. The return value of this method is a Boolean value. When false is returned, the request is complete. The Interceptor and Controller will not be executed again. When the value true is returned, the next Interceptor’s preHandle method is called, and if it is the last Interceptor, the requested Controller method is called.

1.2 postHandle () method

This method will be executed after the current request has been processed, after the Controller method is called, but it will be called before the DispatcherServlet views return render, So we can operate on the ModelAndView object after the Controller handles it in this method. The postHandle method is called in the opposite direction from the preHandle method, which means that the postHandle method declared by the Interceptor first will be executed later. If the current Interceptor’s preHandle() method returns false, this method will not be called.

1.3 afterCompletion () method

This method will be executed after the entire current request is complete, after the DispatcherServlet has rendered the corresponding view. The main purpose of this method is for resource cleanup. If the current Interceptor’s preHandle() method returns false, this method will not be called.

2 WebMvcConfigurer

In a SpringBoot project, if you want to customize some interceptors, ViewResolver, and MessageConverter, how do you do it? In SpringBoot 1.5, methods in the WebMvcConfigurerAdapter class were overridden to add custom interceptors, view parsers, message converters, and so on. After SpringBoot 2.0, this class is marked @deprecated. So we have to implement the WebMvcConfigurer interface.

The addInterceptors(InterceptorRegistry Registry) method, one of the core methods in the WebMvcConfigurer interface, adds an interceptor. It is mainly used for user login status interception and log interception.

  • AddInterceptor: You need an interceptor instance that implements the HandlerInterceptor interface

  • AddPathPatterns: Used to set filtering path rules for interceptors; AddPathPatterns (“/**”) intercepts all requests

  • ExcludePathPatterns: Used to set up filtering rules that do not need to be blocked

public interface WebMvcConfigurer {
    // ...
    default void addInterceptors(InterceptorRegistry registry) {}}Copy the code

3 project adds interceptor functionality

1. Analysis: Many operations in the project require login before they can be performed. It is unrealistic to write code to check whether the Session has login information before each request is processed. So use interceptors to solve this problem.

2. Create the interceptor class com. Cy. Store. The interceptor. LoginInterceptor, and realize the org. Springframework. Web. Servlet. HandlerInterceptor interface.

package com.cy.store.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** Defines the processor interceptor */
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (request.getSession().getAttribute("uid") = =null) {
            response.sendRedirect("/web/login.html");
            return false;
        }
        return true; }}Copy the code

3. Create a LoginInterceptorConfigurer interceptor configuration class and implement org. Springframework. Web. Servlet. Config. The annotation. WebMvcConfigurer interface, The configuration class needs to be decorated with the @configruation annotation.

package com.cy.store.config;
import com.cy.store.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;

/** Register processor interceptor */
@Configuration
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
    /** Interceptor configuration */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Create an interceptor object
        HandlerInterceptor interceptor = new LoginInterceptor();

        / / white list
        List<String> patterns = new ArrayList<String>();
        patterns.add("/bootstrap3/**");
        patterns.add("/css/**");
        patterns.add("/images/**");
        patterns.add("/js/**");
        patterns.add("/web/register.html");
        patterns.add("/web/login.html");
        patterns.add("/web/index.html");
        patterns.add("/web/product.html");
        patterns.add("/users/reg");
        patterns.add("/users/login");
        patterns.add("/districts/**");
        patterns.add("/products/**");

        // Add interceptors through the registration tool
        registry.addInterceptor(interceptor).addPathPatterns("/ * *").excludePathPatterns(patterns); }}Copy the code

The session

1. Rebuild the login() method and store the UID and USERNAME into the HttpSession object after successful login.

@RequestMapping("login")
public JsonResult<User> login(String username, String password, HttpSession session) {
    // Call the business object's method to perform the login and get the return value
    User data = userService.login(username, password);

    // After successful login, store uid and USERNAME to HttpSession
    session.setAttribute("uid", data.getUid());
    session.setAttribute("username", data.getUsername());
    Println (" uid=" + getUidFromSession(Session)); // system.out. println(" uid=" + getUidFromSession(Session));
    Println (" Username =" + getUsernameFromSession(Session)); // system.out.println (" username=" + getUsernameFromSession(Session));

    // Encapsulate the above return value and status code OK in the response result and return
    return new JsonResult<User>(OK, data);
}
Copy the code

2. Add methods to the BaseController class to obtain uid and username from the HttpSession object so that you can quickly obtain the values of these two attributes.

/** * get uid * from HttpSession object@paramSession HttpSession object *@returnId of the currently logged in user */
protected final Integer getUidFromSession(HttpSession session) {
	return Integer.valueOf(session.getAttribute("uid").toString());
}

/** * Get the user name from the HttpSession object *@paramSession HttpSession object *@returnThe current login user name */
protected final String getUsernameFromSession(HttpSession session) {
	return session.getAttribute("username").toString();
}
Copy the code