At present, most projects are separated from the front and back ends. In the background interface service development process, we often need to build a basic service first, such as login and registration function, automatic token security verification for all interfaces, so as to prevent security problems. And subsequent colleagues can focus only on the development of the business code, not the implementation of the infrastructure services.

This time I am going to build a simple background service, with Spring Boot + mysql + JJWT + Mybatis.

1. Set up the Spring Boot project

First, we use the initialization project function of IDEA to create a Spring Boot project, as shown in the figure:

Or create it online and click to enter

pom.xml

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6. RELEASE</version>
    </parent>
    <groupId>com.zz</groupId>
    <artifactId>rest-api</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>rest-api</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.1.6. RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
            <scope>compile</scope>
        </dependency>

        <! -- Use MySQL Connector-J -->

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>


        <! -- image to base64 -->

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

        <! -- JJWT support -->

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

        <! --commons-codec -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.terran4j</groupId>
            <artifactId>terran4j-commons-api2doc</artifactId>
            <version>1.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.5.10</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.15</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.15</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Copy the code

Maven changed to domestic Ali Cloud mirror, which is faster

settings.xml:

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="Http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
    
    <mirrors>
        <mirror>
            <id>alimaven</id>
            <name>aliyun maven</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>
</settings>


Copy the code

Mybatis

application.yml

# mysql
spring:
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB
  profiles:
    active: dev
  Static resource configuration
  mvc:
    static-path-pattern: / * *
  resources:
    static-locations: file:/Users/wz/projects/blog/uploadFile/,classpath:/static/,classpath:/resources/,classpath:/file/,classpath:/templates/

mybatis-plus:
  mapper-locations: classpath:/mapper/*.xml
  type-aliases-package: com.zz.entity
# custom
my:
  tokenURL: "55555"
  authURL: "88888"

Copy the code

application-dev.yml

# mysql
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: JDBC: mysql: / / 127.0.0.1:3306 / test? useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: * * * * * *
server:
  port: 8080

Copy the code

The approximate directory structure is as follows

Construction details will not be repeated;

2. Read from the definition configuration file

com.zz.config.MyConfiguration

package com.zz.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "my")
public class MyConfiguration {

    private String tokenURL;

    private String authURL;

    public String getAuthURL(a) {
        return this.authURL;
    }

    public void setAuthURL(String authURL) {
        this.authURL = authURL;
    }

    public String getTokenURL(a) {
        return this.tokenURL;
    }

    public void setTokenURL(String tokenURL) {
        this.tokenURL = tokenURL; }}Copy the code

3. Web service configuration

com.zz.config.MyConfiguration

package com.zz.config;

import com.zz.common.interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Intercept all requests and determine if the login needs to be skipped by checking for @passtoken annotations
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/ * *");
    }
    
    @Bean
    public AuthenticationInterceptor authenticationInterceptor(a) {
        return newAuthenticationInterceptor(); }}Copy the code

4, custom return unified entity class Response

com.zz.model.Response

package com.zz.model;


public class Response {

    private int code;
    private String msg;
    private Object data;
    
    public Object getData(a) {
        return data;
    }
    
    public void setData(Object data) {
        this.data = data;
    }
    
    public int getCode(a) {
        return code;
    }
    
    public void setCode(int code) {
        this.code = code;
    }
    
    public String getMsg(a) {
        return msg;
    }
    
    public void setMsg(String msg) {
        this.msg = msg; }}Copy the code

Utils Public method class

com.zz.utils.HttpUtils

Get Request, Response, and session

package com.zz.utils;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/** * get Request and Response */
public class HttpUtils {
    
    / / get request
    public static HttpServletRequest getRequest(a) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) return null;
        return requestAttributes.getRequest();
    }
    
    / / get the response
    public static HttpServletResponse getResponse(a) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) return null;
        return requestAttributes.getResponse();
    }
    
    / / get the session
    public static HttpSession getSession(a){
        HttpServletRequest request = getRequest();
        if(request == null) return null;
        returnrequest.getSession(); }}Copy the code

com.zz.utils.JWTUtils

JWT generates tokens and verifies tokens

package com.zz.utils;

import com.zz.entity.User;
import io.jsonwebtoken.*;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class JWTUtils {
    
    // The secret key used to generate the signature
    private static final String SECRETKEY = "KJHUhjjJYgYUllVbXhKDHXhkSyHjlNiVkYzWTBac1Yxkjhuad";
    
    // expirationDate Expirations of JWT, in seconds
    private static long expirationDate = 2 * 60 * 60;
    
    
    /** * Generates an encrypted key ** from a string@return SecretKey
     */
    private static SecretKey generalKey(String stringKey) {
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
    }
    
    /** * create JWT **@paramUser User information *@return jwt token
     */
    public static String createToken(User user) {
        
        // The signature algorithm used to specify the signature, namely the header part, which is already wrapped by JWT
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        
        // The JWT generation time
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        
        // Create a private declaration of the payload (for specific business purposes).
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getUserId());
        claims.put("userName", user.getUserName());
        claims.put("password", user.getPassword());
        
        // This method is locally encapsulated and can be read from the local configuration file. Remember that this secret key cannot be exposed. It is your server's private key and should not be disclosed in any scenario. Once the client is aware of this Secret, it means that the client can issue the JWT itself
        SecretKey key = generalKey(SECRETKEY + user.getPassword());
        
        // Generate the issuer
        // It is a string in the form of JSON or a string. Non-sensitive user information, such as user ID or user account, is added to store. After the token is parsed, it is compared with the token to prevent improper use
        HashMap<String, Object> storeInfo = new HashMap<String, Object>();
        storeInfo.put("userId", user.getUserId());
        storeInfo.put("userName", user.getUserName());
        String subject = storeInfo.toString();
        
        // Add standard and private declarations for payload
        // Create a new JwtBuilder and set the body of JWT
        JwtBuilder builder = Jwts.builder()
                // If there is a private declaration, be sure to set the private declaration that you created first. This is the claim assigned to the Builder, which overwrites the standard declaration once it is written after the standard declaration assignment
                .setClaims(claims)
                // Unique random UUID
                // Set JWT ID: the unique identifier of the JWT. This value can be set to a unique value based on service requirements. It is mainly used as a one-time token to avoid replay attacks
                .setId(UUID.randomUUID().toString())
                // Issue time of JWT
                .setIssuedAt(now)
                // represents the body of the JWT, that is, its owner. This is a JSONformat string. What userID, rolDID, etc. can be stored as a unique identifier for what user
                .setSubject(subject)
                // Set the signature algorithm and key used for the signature
                .signWith(signatureAlgorithm, key);
        
        if (expirationDate >= 0) {
            long expMillis = nowMillis + expirationDate * 1000;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }
    
    /** * Decrypts the token to obtain the declared entity **@paramToken Encrypted token *@return claims
     */
    public static Claims parseToken(String token, User user) {
        // The signature key must be exactly the same as the generated signature key
        SecretKey key = generalKey(SECRETKEY + user.getPassword());
        
        // Get the private declaration
        Claims claims = Jwts.parser()
                // Set the signature key
                .setSigningKey(key)
                // Set the token to be resolved
                .parseClaimsJws(token).getBody();
        
        return claims;
    }
    
    
    /** * verify token **@paramToken Encrypted token *@paramUser User information *@return true|false
     */
    public static Boolean verify(String token, User user) {
        
        // Get the private declared entity
        Claims claims = parseToken(token, user);
        
        return claims.get("password").equals(user.getPassword()); }}Copy the code

Query entity class query

All service queries have a unified, individual entity class

Such as:

com.zz.query.UserQuery

User query entity

package com.zz.query;

public class UserQuery {
    
    private String userName;
    
    private String password;
    
    private long userId;
    
    private boolean showPassword;
    
    public boolean isShowPassword(a) {
        return showPassword;
    }
    
    public void setShowPassword(boolean showPassword) {
        this.showPassword = showPassword;
    }
    
    public long getUserId(a) {
        return userId;
    }
    
    public void setUserId(long userId) {
        this.userId = userId;
    }
    
    public String getUserName(a) {
        return userName;
    }
    
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    public String getPassword(a) {
        return password;
    }
    
    public void setPassword(String password) {
        this.password = password; }}Copy the code

7. Return entity class after query

All service queries are returned with a unified, individual entity class

Such as:

com.zz.entity.User

User data return entity

package com.zz.entity;

public class User {
    
    private long userId;
    
    private String userName;
    
    private String token;
    
    private String password;
    
    public String getPassword(a) {
        return password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    public String getToken(a) {
        return token;
    }
    
    public void setToken(String token) {
        this.token = token;
    }
    
    public long getUserId(a) {
        return userId;
    }
    
    public void setUserId(long userId) {
        this.userId = userId;
    }
    
    public String getUserName(a) {
        return userName;
    }
    
    public void setUserName(String userName) {
        this.userName = userName; }}Copy the code

8. The interface realizes the three-tier architecture

We have a three-tier architecture: Controller – > Service – > mapper;

If we were to write a User interface, we would first declare a UserController routing control layer, then call UserService to implement the class method, and then call the Mapper persistence layer to CRUD.

9. Start setting up the registered user function

The basic construction shall be suspended first and the substantial business shall be deduced.

Not to mention mysql connections;

Let us begin the journey of realization;

com.zz.newController.UserController

User registration

package com.zz.newController;

import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.zz.common.annotation.PassToken;
import com.zz.common.base.BaseApplicationController;
import com.zz.entity.User;
import com.zz.model.Response;
import com.zz.query.UserQuery;
import com.zz.service.UserService;
import com.zz.utils.JWTUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;


/**
 * 登录
 * author: wz
 */
@RestController
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    /* * @param userName * @param password * @return response */
    @PostMapping("/add")
    @PassToken
    public Response addUser(@RequestParam String userName, @RequestParam String password, Response response) {
        UserQuery query = new UserQuery();
        User userData = null;
        
        query.setUserName(userName);
        query.setPassword(password);
        
        int result;
        String message = "";
        
        // Check whether the user already exists
        UserQuery findUserQuery = new UserQuery();
        findUserQuery.setUserName(userName);
        User existUser = this.userService.findUserByName(findUserQuery);
        if (existUser == null) {
            
            // Insert the user
            try {
                result = this.userService.addUser(query);
                message = "success";
            } catch (Exception e) {
                result = 0;
                message = "error";
                e.printStackTrace();
            }
            
            // The user information is displayed after the user is successfully inserted
            if (result == 1) {
                userData = this.userService.findUserByName(findUserQuery);
                
                / / token is generated
                String token = null;
                
                // Current user
                User currentUser = new User();
                if(userData ! =null) {
                    currentUser.setUserId(userData.getUserId());
                    currentUser.setUserName(userData.getUserName());
                    currentUser.setPassword(password);
                    token = JWTUtils.createToken(currentUser);
                }
                
                if(token ! =null) {
                    userData.setToken(token);
                    
                    // Obtain token user information
                    // Claims userDataFromToken = JWTUtils.parseToken(token, currentUser);}}}else {
            message = "User already exists";
        }
        
        response.setData(userData);
        response.setMsg(message);
        returnresponse; }}Copy the code

com.zz.service.UserService

Interface User Interface

package com.zz.service;

import com.zz.entity.User;
import com.zz.query.UserQuery;

import java.util.List;
import java.util.Map;

public interface UserService {
    
    // Add a user
    int addUser(UserQuery query);
    
    
}

Copy the code

com.zz.service.impl.UserServiceImpl

User interface implementation class

package com.zz.service.impl;

import com.zz.entity.User;
import com.zz.mapper.UserMapper;
import com.zz.query.UserQuery;
import com.zz.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Override
    public int addUser(UserQuery query){
        return this.userMapper.insert(query); }}Copy the code

com.zz.mapper.UserMapper

mapper

package com.zz.mapper;

import com.zz.entity.User;
import com.zz.query.UserQuery;

import java.util.List;

public interface UserMapper {
    
    int insert(UserQuery query);
    
}

Copy the code

resources/mapper/UserMapper.xml

The names must match

<?xml version="1.0" encoding="UTF-8" ? >

      
<mapper namespace="com.zz.mapper.UserMapper">

    <resultMap id="BaseResult" type="com.zz.entity.User">
        <id column="user_id" property="userId"></id>
        <id column="user_name" property="userName"></id>
    </resultMap>

    <sql id="base">
        user_id,
        user_name
        <if test="showPassword">
            , password
        </if>
    </sql>

    <sql id="base_condition">
        <where>
            <if test="userName! =null and userName! = "">
                user_name=#{userName}
            </if>
            <if test="password! =null and password! = "">
                and password=#{password}
            </if>
        </where>

    </sql>

    <insert id="insert">
        INSERT INTO user(
        user_name,
        password
        ) VALUES (
        #{userName},
        #{password}
        )
    </insert>


</mapper>

Copy the code

At this point, the entire interface writing process is complete, and that’s all it takes to write an interface under the current architecture.

10. Set up a Web instance — register a user

Since we have configured the static resource path in the configuration file, we can write an unseparated instance of we B in resources to access it.

resources/static/regist.html

The sign-up page


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
    <title>Registered users</title>
    <! -- Introducing styles -->
    <link rel="stylesheet" href="css/regist.css"/>
    <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.1.3/weui.min.css">
    
</head>
<body>
<div class="container">
    <div class="page form_page js_show">
        <div class="weui-form">
            <div class="weui-form__text-area">
                <h2 class="weui-form__title">Register a new user</h2>
            </div>
            <div class="weui-form__control-area">
                <div class="weui-cells__group weui-cells__group_form">
                    <div class="weui-cells weui-cells_form">
                        <div class="weui-cell">
                            <div class="weui-cell__hd"><label class="weui-label">The user name</label></div>
                            <div class="weui-cell__bd">
                                <input id="Js_input - user" class="weui-input" placeholder="Please enter a user name to set.">
                            </div>
                        </div>
                        <div class="weui-cell">
                            <div class="weui-cell__hd"><label class="weui-label">password</label></div>
                            <div class="weui-cell__bd">
                                <input id="Js_input - PWD" type="password" class="weui-input" placeholder="Please enter the password to be set.">
                            </div>
                        </div>
                        <div class="weui-cell">
                            <div class="weui-cell__hd"><label class="weui-label">Confirm password</label></div>
                            <div class="weui-cell__bd">
                                <input id="Js_input - pwd2" type="password" class="weui-input" placeholder="Please enter your password again." type="number" pattern="[0-9] *">
                            </div>
                        </div>
                    </div>
                </div>
            </div>
<! -- <div class="weui-form__tips-area">-->
<! -- <p class="weui-form__tips">-->
<! -- Form page prompt, center aligned -->
<! -- </p>-->
<! -- </div>-->
            <div class="weui-form__opr-area">
                <a class="weui-btn weui-btn_primary" href="javascript:" id="submit">determine</a>
            </div>

            <div class="weui-form__extra-area">
                <div class="weui-footer">
<! -- <p class="weui-footer__links">-->
<! -- <a href="javascript:void(0);" Class ="weui-footer__link"> </a>-->
<! -- </p>-->
                    <p class="weui-footer__text">Copyright © 2019 alex wong</p>
                </div>
            </div>
        </div>
        <div id="js_toast" style="display: none;">
            <div class="weui-mask_transparent"></div>
            <div class="weui-toast">
                <i class="weui-icon-success-no-circle weui-icon_toast"></i>
                <p class="weui-toast__content">Has been completed</p>
            </div>
        </div>
    </div>
</div>
</body>
<script src="js/md5.js"></script>
<script src="js/utils.js"></script>
<script src="js/dataService.js"></script>
<script type="text/javascript" src="https://res.wx.qq.com/open/libs/weuijs/1.2.1/weui.min.js"></script>
<script src="js/regist.js"></script>
</html>
Copy the code

static/js/dataService.js

const APIURL = '/';

window.dataService = {

	//GET
	get: (url, params = {}) = > {

		const searchArr = [];

		Object.keys(params).forEach(n= > {
			searchArr.push(`${n}=${params[n]}`);
		});

		const searchStr = searchArr.length ? '? ' + searchArr.join('&') : ' ';
		const token = utils.getCookie('token');

		return fetch(APIURL + url + searchStr, {
			method: 'GET'.headers: {
				token
			}
		}).then(res= > res.json());
	},

	//POST
	post: (url, params = {}) = > {

		const formData = new FormData();

		Object.keys(params).forEach(n= > {
			formData.append(n, params[n]);
		});

		const token = utils.getCookie('token');

		return fetch(APIURL + url, {
			method: 'POST'.headers: {
				token
			},
			body: formData
		}).then(res= > res.json());
	},

	/ / register
	addUser(params) {
		return this.post('user/add', params);
	},

	/ / login
	login(params) {
		return this.post('user/login', params);
	},

	// User information
	getUserInfo(params) {
		return this.get('user/info', params); }};Copy the code

static/js/utils.js

window.utils = {

	// md5
	generateMd5(userName, password) {
		const salt = "1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik9ol0p@! .";
		const asciStr = userName + salt + password;
		const asciArr = asciStr.split(' ');
		const asciResult = [];
		asciArr.forEach(n= > {
			asciResult.push(n.charCodeAt());
		});
		const ascireusltStr = asciResult.join(salt);
		return hex_md5(ascireusltStr);
	},

	// setCookie
	setCookie(name, value) {
		var time = 2 * 60 * 60 * 1000;
		var exp = new Date(a); exp.setTime(exp.getTime() + time);document.cookie = name + "=" + escape(value) + "; expires=" + exp.toGMTString();
	},

	// getCookie
	getCookie(name) {
		var arr, reg = new RegExp("(^| )" + name + "= (/ ^; (*). | $)");
		if (arr = document.cookie.match(reg))
			return unescape(arr[2]);
		else
			return null; }};Copy the code

static/js/regist.js

// Get relevant user information
const userNameInput = document.getElementById("Js_input - user");
const passwordInput = document.getElementById("Js_input - PWD");
const passwordConfirmInput = document.getElementById("Js_input - pwd2");
const submitBtn = document.getElementById("submit");

// submit
submitBtn.onclick = (a)= > {

	const userName = userNameInput.value;
	const password = passwordInput.value;
	const confirmPassword = passwordConfirmInput.value;

	// verify
	if(! userName) { weui.topTips('User name cannot be empty');
		return;
	} else if(! password) { weui.topTips('User password cannot be empty');
		return;
	} else if(confirmPassword ! == password) { weui.topTips('Inconsistent passwords, please try again');
		return;
	}

	// Encrypt the password
	const newPassword = utils.generateMd5(userName, password);

	/ / register
	dataService.addUser({
		userName,
		password: newPassword,
	}).then(res= > {
		const {code, data, msg} = res;
		if(! data) { weui.topTips(msg); }else {
			weui.topTips('Registration is successful, welcome${data.userName}`);
			window.location.href = location.origin + '/login.html'; }})};Copy the code

The effect is as follows:

Add some basic checks

The user password is encrypted and transmitted, and the new user is verified to be registered

Mysql > select * from user

11. Back-end – User login function

As described in Step 9 above, please add the following additions directly to the above service without showing all the code.

com.zz.newController.UserController

The first step is to determine whether the user exists, and if so, return the basic information and the user credential token

/** * login **@paramUserName userName *@param"Password," password *@return{} * /
    @PostMapping("/login")
    @PassToken
    public Response login(@RequestParam String userName, @RequestParam String password, Response response) {
        
        UserQuery query = new UserQuery();
        query.setUserName(userName);
        query.setPassword(password);
        
        // Verify user and password
        try {
            // Check whether the user already exists
            User existUser = this.userService.findUserByName(query);
            
            / / token is generated
            String token = null;
            
            // Current user
            User currentUser = new User();
            if(existUser ! =null) {
                currentUser.setUserId(existUser.getUserId());
                currentUser.setUserName(existUser.getUserName());
                currentUser.setPassword(password);
               // Generate user credentials
                token = JWTUtils.createToken(currentUser);
                if(token ! =null) {
                    existUser.setToken(token);
                }
                response.setMsg("success");
                response.setData(existUser);
            } else {
                // Login failed
                response.setMsg("Login failed. Please check username and password.");
                response.setData(null); }}catch (Exception e) {
            response.setMsg("login failed");
            response.setData(null);
            e.printStackTrace();
        }
        return response;
    }
Copy the code

com.zz.service.UserService

package com.zz.service;

import com.zz.entity.User;
import com.zz.query.UserQuery;

import java.util.List;
import java.util.Map;

public interface UserService {
    
    // Add a user
    int addUser(UserQuery query);
    
    // Find a single user
    User findUserById(UserQuery query);
    
    User findUserByName(UserQuery query);
    
}

Copy the code

com.zz.service.impl.UserServiceImpl

package com.zz.service.impl;

import com.zz.entity.User;
import com.zz.mapper.UserMapper;
import com.zz.query.UserQuery;
import com.zz.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Override
    public int addUser(UserQuery query){
        return this.userMapper.insert(query);
    }
    
    @Override
    public User findUserById(UserQuery query) {
        return this.userMapper.findUserById(query);
    }
    
    @Override
    public User findUserByName(UserQuery query) {
        return this.userMapper.findUserByName(query);
    }
    
    @Override
    public List<User> findAllUser(UserQuery query) {
        return this.userMapper.findAllUser(query); }}Copy the code

com.zz.mapper.UserMapper

package com.zz.mapper;

import com.zz.entity.User;
import com.zz.query.UserQuery;

import java.util.List;

public interface UserMapper {
    
    int insert(UserQuery query);
    
    User findUserById(UserQuery query);
    
    User findUserByName(UserQuery query);
    
    List<User> findAllUser(UserQuery query);
    
}

Copy the code

mapper/UserMapper.xml


       Version = "1.0" encoding="UTF-8" ? >

       mapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.zz.mapper.UserMapper"> <resultMap id="BaseResult" type="com.zz.entity.User"> <id column="user_id" property="userId"></id> <id column="user_name" property="userName"></id> </resultMap> <sql id="base"> user_id, user_name <if test="showPassword"> , password </if> </sql> <sql id="base_condition"> <where> <if test="userName! =null and userName! =''"> user_name=#{userName} </if> <if test="password! =null and password! =''"> and password=#{password} </if> </where> </sql> <! <select id="findAllUser" resultMap="BaseResult"> select <include refid="base"/> from user </select> ResultMap ="BaseResult"> select <include refid="base"/> from user where user_id =  #{userId} </select> <select id="findUserByName" resultMap="BaseResult"> select <include refid="base"/> from user <include refid="base_condition"/> </select> <insert id="insert"> INSERT INTO user( user_name, password ) VALUES ( #{userName}, #{password} ) </insert> </mapper>Copy the code

12. Set up web instance — login user

static/login.html


      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
    <title>login</title>
    <! -- Introducing styles -->
    <link rel="stylesheet" href="css/regist.css"/>
    <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.1.3/weui.min.css">
    
</head>
<body>
<div class="container">
    <div class="page form_page js_show">
        <div class="weui-form">
            <div class="weui-form__text-area">
                <h2 class="weui-form__title">The login</h2>
            </div>
            <div class="weui-form__control-area">
                <div class="weui-cells__group weui-cells__group_form">
                    <div class="weui-cells weui-cells_form">
                        <div class="weui-cell">
                            <div class="weui-cell__hd"><label class="weui-label">The user name</label></div>
                            <div class="weui-cell__bd">
                                <input id="Js_input - user" class="weui-input" placeholder="Please enter user name">
                            </div>
                        </div>
                        <div class="weui-cell">
                            <div class="weui-cell__hd"><label class="weui-label">password</label></div>
                            <div class="weui-cell__bd">
                                <input id="Js_input - PWD" type="password" class="weui-input" placeholder="Please enter your password">
                            </div>
                        </div>
                        
                    </div>
                </div>
            </div>
<! -- <div class="weui-form__tips-area">-->
<! -- <p class="weui-form__tips">-->
<! -- Form page prompt, center aligned -->
<! -- </p>-->
<! -- </div>-->
            <div class="weui-form__opr-area">
                <a class="weui-btn weui-btn_primary" href="javascript:" id="submit">determine</a>
            </div>

            <div class="weui-form__extra-area">
                <div class="weui-footer">
<! -- <p class="weui-footer__links">-->
<! -- <a href="javascript:void(0);" Class ="weui-footer__link"> </a>-->
<! -- </p>-->
                    <p class="weui-footer__text">Copyright © 2019 alex wong</p>
                </div>
            </div>
        </div>
        <div id="js_toast" style="display: none;">
            <div class="weui-mask_transparent"></div>
            <div class="weui-toast">
                <i class="weui-icon-success-no-circle weui-icon_toast"></i>
                <p class="weui-toast__content">Has been completed</p>
            </div>
        </div>
    </div>
</div>
</body>
<script src="js/md5.js"></script>
<script src="js/utils.js"></script>
<script src="js/dataService.js"></script>
<script type="text/javascript" src="https://res.wx.qq.com/open/libs/weuijs/1.2.1/weui.min.js"></script>
<script src="js/login.js"></script>
</html>
Copy the code

static/js/login.js

// Get relevant user information
const userNameInput = document.getElementById("Js_input - user");
const passwordInput = document.getElementById("Js_input - PWD");
const submitBtn = document.getElementById("submit");

// submit
submitBtn.onclick = (a)= > {

	const userName = userNameInput.value;
	const password = passwordInput.value;

	// verify
	if(! userName) { weui.topTips('User name cannot be empty');
		return;
	} else if(! password) { weui.topTips('User password cannot be empty');
		return;
	}

	// Encrypt the password
	const newPassword = utils.generateMd5(userName, password);

	/ / register
	dataService.login({
		userName,
		password: newPassword,
	}).then(res= > {
		const {code, data, msg} = res;
		if(! data) { weui.topTips(msg); }else {
			weui.topTips('Login successful, welcome${data.userName}`);
			utils.setCookie('token', data.token);
			location.href = location.origin + '/home.html'; }})};Copy the code

The login interface returns the user certificate token, which is used to verify the user interface to enhance security.

Add custom annotations and interceptors

In normal business development, it is important not to expose the interface service to anyone who can access it, otherwise others can view or modify your data at will, which is a serious matter. In addition to limiting the range of fixed client IP in terms of network segment IP, the interface itself also needs to add security authentication. In this case, we need to use the previously generated user certificate token.

The question is if we customize the control, which interfaces need to be authenticated and which interfaces do not need to be authenticated? Some people might say, just verify it all, why bother? However, in real business, some interfaces cannot be forced to verify, such as the interface shared by some users to wechat. Authentication cannot be added, otherwise the shared page cannot be displayed normally.

So we can custom annotation @passtoken, add this annotation interface, can not carry out token verification.

com.zz.common.annotation.PassToken

PassToken annotations

package com.zz.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// Whether to skip token authentication
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required(a) default true;
}

Copy the code

Adding interceptors

com.zz.common.interceptor.AuthenticationInterceptor

When a request is sent, a token is added to the request header, and the token is retrieved from the header during validation

If there is no token, a tokenless prompt is displayed.

If so, JWT is used to verify that the token exists and that the user password is correct.

package com.zz.common.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.mongodb.util.JSON;
import com.zz.common.annotation.PassToken;
import com.zz.common.base.BaseApplicationController;
import com.zz.entity.User;
import com.zz.model.Response;
import com.zz.query.UserQuery;
import com.zz.service.UserService;
import com.zz.utils.JWTUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/ / the interceptor
public class AuthenticationInterceptor implements HandlerInterceptor {
    
    @Autowired
    private UserService userService;
    
    /** * response Returns a message **@param code
     * @param message
     * @return
     * @throws JSONException
     */
    public JSONObject getJsonObject(int code, String message) throws JSONException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("msg", message);
        jsonObject.put("code", code);
        return jsonObject;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        
        Fetch the token from the HTTP request header
        String token = BaseApplicationController.getToken();
        // If not mapped to the method directly through
        if(! (objectinstanceof HandlerMethod)) {
            return true;
        }
        
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        // Check if there are PassToken annotations, skip authentication if there are
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true; }}// Authentication is performed by default
        httpServletResponse.setContentType("application/json; charset=UTF-8");
        if (token == null || token.equals("null")) {
            JSONObject jsonObject = getJsonObject(403."No token, please log in again");
            httpServletResponse.getWriter().write(jsonObject.toString());
            return false;
            // throw new RuntimeException(" No token, please login again ");
        }
        
        // Get the user ID in the token
        long userId;
        try {
            userId = BaseApplicationController.getCurrentUserId();
        } catch (JWTDecodeException j) {
            JSONObject jsonObject = getJsonObject(500."Access exception, token incorrect, please log in again");
            httpServletResponse.getWriter().write(jsonObject.toString());
            return false;
            // throw new RuntimeException(" Access exception!" );
        }
        
        // Verify that the user exists
        UserQuery query = new UserQuery();
        query.setUserId(userId);
        query.setShowPassword(Boolean.TRUE);
        User user = userService.findUserById(query);
        
        if (user == null) {
            JSONObject jsonObject = getJsonObject(500."User does not exist, please log in again.");
            httpServletResponse.getWriter().write(jsonObject.toString());
            return false;
            // throw new RuntimeException(" User does not exist, please log in again ");
        }
        
        // Verify the token is valid
        Boolean verify = JWTUtils.verify(token, user);
        if(! verify) { JSONObject jsonObject = getJsonObject(500."Illegal access, please log in again.");
            httpServletResponse.getWriter().write(jsonObject.toString());
            return false;
            // throw new RuntimeException(" Illegal access! ") );
        }
        
        return true; }}Copy the code

Here’s an example:

com.zz.newController.UserController

package com.zz.newController;

import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.zz.common.annotation.PassToken;
import com.zz.common.base.BaseApplicationController;
import com.zz.entity.User;
import com.zz.model.Response;
import com.zz.query.UserQuery;
import com.zz.service.UserService;
import com.zz.utils.JWTUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;


/** * autho: Alex Wong */
@RestController
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    /* * @param userName * @param password * @return response */
    @PostMapping("/add")
    @PassToken
    public Response addUser(@RequestParam String userName, @RequestParam String password, Response response) {
        UserQuery query = new UserQuery();
        User userData = null;
        
        query.setUserName(userName);
        query.setPassword(password);
        
        int result;
        String message = "";
        
        // Check whether the user already exists
        UserQuery findUserQuery = new UserQuery();
        findUserQuery.setUserName(userName);
        User existUser = this.userService.findUserByName(findUserQuery);
        if (existUser == null) {
            
            // Insert the user
            try {
                result = this.userService.addUser(query);
                message = "success";
            } catch (Exception e) {
                result = 0;
                message = "error";
                e.printStackTrace();
            }
            
            // The user information is displayed after the user is successfully inserted
            if (result == 1) {
                userData = this.userService.findUserByName(findUserQuery);
                
                / / token is generated
                String token = null;
                
                // Current user
                User currentUser = new User();
                if(userData ! =null) {
                    currentUser.setUserId(userData.getUserId());
                    currentUser.setUserName(userData.getUserName());
                    currentUser.setPassword(password);
                    token = JWTUtils.createToken(currentUser);
                }
                
                if(token ! =null) {
                    userData.setToken(token);
                    
                    // Obtain token user information
                    // Claims userDataFromToken = JWTUtils.parseToken(token, currentUser);}}}else {
            message = "User already exists";
        }
        
        response.setData(userData);
        response.setMsg(message);
        return response;
    }
    
    /** * login **@paramUserName userName *@param"Password," password *@return{} * /
    @PostMapping("/login")
    @PassToken
    public Response login(@RequestParam String userName, @RequestParam String password, Response response) {
        
        UserQuery query = new UserQuery();
        query.setUserName(userName);
        query.setPassword(password);
        
        // Verify user and password
        try {
            // Check whether the user already exists
            User existUser = this.userService.findUserByName(query);
            
            / / token is generated
            String token = null;
            
            // Current user
            User currentUser = new User();
            if(existUser ! =null) {
                currentUser.setUserId(existUser.getUserId());
                currentUser.setUserName(existUser.getUserName());
                currentUser.setPassword(password);
                token = JWTUtils.createToken(currentUser);
                if(token ! =null) {
                    existUser.setToken(token);
                }
                response.setMsg("success");
                response.setData(existUser);
            } else {
                // Login failed
                response.setMsg("Login failed. Please check username and password.");
                response.setData(null); }}catch (Exception e) {
            response.setMsg("login failed");
            response.setData(null);
            e.printStackTrace();
        }
        return response;
    }
    
    /** * obtain personal information **@return{} * /
    @GetMapping("/info")
    public Response getUserInfo(Response response) {
        / / access token
        String token = BaseApplicationController.getToken();
        User userData2 = BaseApplicationController.getCurrentUser();
        Map<String, Object> headerData = BaseApplicationController.getHeader();
        if(token ! =null && !token.equals("null")) {
            User userData = new User();
            DecodedJWT claims = JWT.decode(token);
            userData.setUserName(claims.getClaim("userName").asString());
            userData.setUserId(claims.getClaim("userId").asLong());
            response.setData(userData);
            response.setMsg("success");
        } else {
            response.setMsg("Token does not exist");
        }
        returnresponse; }}Copy the code

We have written a new interface to get user information, as above, the rest of the code will not be described;

The user information is successfully obtained

Delete the token

Token corrected the mistake on purpose

At this point, the validation process is perfectly successful;

14,

The above process is only a simple service construction, the real service still needs more configuration, such as XSS configuration to prevent XSS attacks, multi-source data, etc. Well, that’s the end of this article. Please forgive me if I’m not clear about anything.