目录

一、springboot项目简介

二、构建springboot项目

创建SpringBoot项目并配置POM

pom.xml

application.yml

启动类的配置

SpbootproApplication 

测试一下是否能访问前端

IndexController 

三、首页功能

 导入mybatisplus的代码生成器

首页方法改造

 IndexController 

页面数据展示

index.html

将页面的展示格式改变

index.html

四、用户明文登录 

PageController

UserDto 

IUserService 

 UserServiceImpl 

五、前端及数据库密码加密

login.js

 UserServiceImpl 

login.html

六、服务端客户端登录密码管理

UserServiceImpl 

IRedisService

 IRedisServiceImpl 

 用的工具类

CookieUtils 

 DataUtils 

 JsonResponseBody

 JsonResponseStatus

 MD5Utils 

 MybatisPlusConfig 

 PageBean 

 ValidatorUtils 

一、springboot项目简介

项目前期准备

使用技术

前端:Freemarker、jQuery
后端:SpringBoot、MyBatisPlus、Lombok
中间件:Redis

数据表介绍

用户表:t_user

商品表:t_goods

订单表:t_order

订单项表:t_order_item

数据源表:t_dict_type

数据项表:t_dict_dat

 后期开发项目肯定是还要修改的 现在浅看一下

springboot后端架构图 springboot前后端_spring boot

二、构建springboot项目

创建SpringBoot项目并配置POM

构建项目时并没有选择任何组件(避免于所准备的依赖因为版本的问题导致出现问题)

springboot后端架构图 springboot前后端_spring boot_02

spring-boot-starter-freemarker
spring-boot-starter-web
mysql-connector-java 5.1.44
lombok
<!-- mybatis plus依赖 -->
mybatis-plus-boot-starter 3.4.0
mybatis-plus-generator 3.4.0
<!-- hariki连接池 -->
HikariCP
<!-- MD5依赖 -->
commons-codec
commons-lang3 3.6
<!-- valid验证依赖 -->
spring-boot-starter-validation
<!-- redis -->
spring-boot-starter-data-redis

参考

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.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cdl</groupId>
    <artifactId>spbootpro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spbootpro</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--freemarker-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

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

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.44</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- mybatis plus依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- mybatis-plus-generator依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
        </dependency>

        <!--hariki-->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </dependency>

        <!-- MD5依赖 -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.6</version>
        </dependency>

        <!-- valid验证依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

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

        <!--commons-pool2 对象池依赖 2.0版本的lettuce需要-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <!--spring-session将session借助于第三方存储(redis/mongodb等等),默认redis-->
        <!--<dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>-->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-easysdk</artifactId>
            <version>2.0.1</version>
        </dependency>
    </dependencies>

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

</project>

application.yml参考如下

application.yml

server:
    port: 8081
    servlet:
        context-path: /
spring:
    datasource:
        url: jdbc:mysql://localhost:3306/spbootpro?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=UTF8
        driver-class-name: com.mysql.jdbc.Driver
        password: 123456
        username: root
        hikari:
            # 最小空闲连接数量
            minimum-idle: 5
            # 空闲连接存活最大时间,默认600000(10分钟)
            idle-timeout: 180000
            # 连接池最大连接数,默认是10
            maximum-pool-size: 10
            # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
            auto-commit: true
            # 连接池名称
            pool-name: MyHikariCP
            # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
            max-lifetime: 1800000
            # 数据库连接超时时间,默认30秒,即30000
            connection-timeout: 30000
    freemarker:
        #设置编码格式
        charset: UTF-8
        #后缀
        suffix:
        #文档类型
        content-type: text/html
        #模板前端
        template-loader-path: classpath:/templates/
        #启用模板
        enabled: true
    mvc:
        static-path-pattern: /static/**
    redis:
        #服务端IP
        host: 192.168.26.128
        #端口
        port: 6379
        #密码
        password: 123456
        #选择数据库
        database: 0
        #超时时间
        timeout: 10000ms
        #Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问
        #Lettuce线程安全,Jedis线程非安全
        lettuce:
            pool:
                #最大连接数,默认8
                max-active: 8
                #最大连接阻塞等待时间,默认-1
                max-wait: 10000ms
                #最大空闲连接,默认8
                max-idle: 200
                #最小空闲连接,默认0
                min-idle: 5
#mybatis-plus配置
mybatis-plus:
    #所对应的 XML 文件位置
    mapper-locations: classpath*:/mapper/*Mapper.xml
    #别名包扫描路径
    type-aliases-package: com.cdl.spbootpro.model
    configuration:
        #驼峰命名规则
        map-underscore-to-camel-case: true
#日志配置
logging:
    level:
        com.cdl.spbootpro.mapper: debug

redis使用的连接的IP是虚拟机所分配的

启动类的配置

SpbootproApplication 

package com.cdl.spbootpro;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@MapperScan({"com.cdl.spbootpro.mapper"})//扫描接口
@EnableTransactionManagement//开启事务
@SpringBootApplication
public class SpbootproApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpbootproApplication.class, args);
    }

}

将准备的页面资料以及图片等复制放入resources下

测试一下是否能访问前端

新建一个controller的包

IndexController 

package com.cdl.spbootpro.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-05 17:31
 */
@Controller
public class IndexController {

    @RequestMapping("/")
    public String index(){
//        模板前端
//        /templates+index.html+""
        //前缀+逻辑视图名+后缀
        return "index.html";
    }

}

运行启动类 浏览器输入地址 可以访问到我们准备的资源页面中

springboot后端架构图 springboot前后端_redis_03

但是此时的数据全都是静态的资源 页面中写的一样的

springboot后端架构图 springboot前后端_spring_04

三、首页功能

目标:将数据库中的数据展示在首页的页面上

数据库数据将这些数据展示到对应的商品上

springboot后端架构图 springboot前后端_spring_05

 导入mybatisplus的代码生成器

CodeGenerator 

package com.cdl.spbootpro.generator;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class CodeGenerator {

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir") + "/spbootpro";
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("cdl");
        gc.setOpen(false);
        gc.setBaseColumnList(true);
        gc.setBaseResultMap(true);
        // gc.setSwagger2(true); 实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/spbootpro?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //pc.setModuleName(scanner("模块名"));
        pc.setParent("com.cdl.spbootpro");
        //设置包名
        pc.setEntity("model");
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mybatis-generator/mapper2.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判断自定义文件夹是否需要创建
                checkDir("调用默认方法创建的目录,自定义目录用");
                if (fileType == FileType.MAPPER) {
                    // 已经生成 mapper 文件判断存在,不想重新生成返回 false
                    return !new File(filePath).exists();
                }
                // 允许生成模板文件
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        templateConfig.setMapper("templates/mybatis-generator/mapper2.java");
        templateConfig.setEntity("templates/mybatis-generator/entity2.java");
        templateConfig.setService("templates/mybatis-generator/service2.java");
        templateConfig.setServiceImpl("templates/mybatis-generator/serviceImpl2.java");
        templateConfig.setController("templates/mybatis-generator/controller2.java");
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setEntitySerialVersionUID(false);
        // 公共父类
        //strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
        // 写于父类中的公共字段
        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("t_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

将自定义的代码生成模板放到templates目录下,覆盖Mybatis-plus默认的代码生成模板

名为mybatis-generator的文件夹,将该文件夹放入resources下的templates下

controller2.java.ftl

package ${package.Controller};


import org.springframework.web.bind.annotation.RequestMapping;

<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>

/**
 * <p>
 * ${table.comment!} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>

}
</#if>

entity2.java.ftl

package ${package.Entity};

<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger2>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

/**
 * <p>
 * ${table.comment!}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokModel>
@Data
    <#if superEntityClass??>
@EqualsAndHashCode(callSuper = true)
    <#else>
@EqualsAndHashCode(callSuper = false)
    </#if>
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if table.convert>
@TableName("${table.name}")
</#if>
<#if swagger2>
@ApiModel(value="${entity}对象", description="${table.comment!}")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#else>
public class ${entity} implements Serializable {
</#if>

<#if entitySerialVersionUID>
    private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyName="${field.propertyName}"/>
    </#if>

    <#if field.comment!?length gt 0>
        <#if swagger2>
    @ApiModelProperty(value = "${field.comment}")
        <#else>
    /**
     * ${field.comment}
     */
        </#if>
    </#if>
    <#if field.keyFlag>
        <#-- 主键 -->
        <#if field.keyIdentityFlag>
    @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
        <#elseif idType??>
    @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
        <#elseif field.convert>
    @TableId("${field.annotationColumnName}")
        </#if>
        <#-- 普通字段 -->
    <#elseif field.fill??>
    <#-- -----   存在字段填充设置   ----->
        <#if field.convert>
    @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
        <#else>
    @TableField(fill = FieldFill.${field.fill})
        </#if>
    <#elseif field.convert>
    @TableField("${field.annotationColumnName}")
    </#if>
    <#-- 乐观锁注解 -->
    <#if (versionFieldName!"") == field.name>
    @Version
    </#if>
    <#-- 逻辑删除注解 -->
    <#if (logicDeleteFieldName!"") == field.name>
    @TableLogic
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->

<#if !entityLombokModel>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>
    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

    <#if chainModel>
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    <#else>
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    </#if>
        this.${field.propertyName} = ${field.propertyName};
        <#if chainModel>
        return this;
        </#if>
    }
    </#list>
</#if>

<#if entityColumnConstant>
    <#list table.fields as field>
    public static final String ${field.name?upper_case} = "${field.name}";

    </#list>
</#if>
<#if activeRecord>
    @Override
    protected Serializable pkVal() {
    <#if keyPropertyName??>
        return this.${keyPropertyName};
    <#else>
        return null;
    </#if>
    }

</#if>
<#if !entityLombokModel>
    @Override
    public String toString() {
        return "${entity}{" +
    <#list table.fields as field>
        <#if field_index==0>
            "${field.propertyName}=" + ${field.propertyName} +
        <#else>
            ", ${field.propertyName}=" + ${field.propertyName} +
        </#if>
    </#list>
        "}";
    }
</#if>
}

mapper2.java.ftl

package ${package.Mapper};

import ${package.Entity}.${entity};
import ${superMapperClassPackage};

/**
 * <p>
 * ${table.comment!} Mapper 接口
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if kotlin>
interface ${table.mapperName} : ${superMapperClass}<${entity}>
<#else>
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {

}
</#if>

mapper2.xml.ftl

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${package.Mapper}.${table.mapperName}">

<#if enableCache>
    <!-- 开启二级缓存 -->
    <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>

</#if>
<#if baseResultMap>
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位-->
        <id column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
<#list table.commonFields as field><#--生成公共字段 -->
        <result column="${field.name}" property="${field.propertyName}" />
</#list>
<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 -->
        <result column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
    </resultMap>

</#if>
<#if baseColumnList>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
<#list table.commonFields as field>
        ${field.columnName},
</#list>
        ${table.fieldNames}
    </sql>

</#if>
</mapper>

service2.java.ftl

package ${package.Service};

import ${package.Entity}.${entity};
import ${superServiceClassPackage};

/**
 * <p>
 * ${table.comment!} 服务类
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if kotlin>
interface ${table.serviceName} : ${superServiceClass}<${entity}>
<#else>
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {

}
</#if>

serviceImpl2.java.ftl

package ${package.ServiceImpl};

import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;

/**
 * <p>
 * ${table.comment!} 服务实现类
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Service
<#if kotlin>
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {

}
<#else>
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} {

}
</#if>

运行代码生成类 输入要生成的表名

springboot后端架构图 springboot前后端_spring_06

 生成成功

springboot后端架构图 springboot前后端_spring_07

 编辑IndexController并定义商品查询请求处理方法

主要查询出 装饰摆件 和 墙式壁挂两个类别的上坪在首页进行展示

SELECT * FROM t_goods;
SELECT * FROM t_goods where goods_type = '01';
SELECT * FROM t_goods where goods_type = '07';

SELECT * FROM t_dict_type;
SELECT * FROM t_dict_data;

首页数据绑定语法

1) list集合判空
<#if goods07?? && goods07?size gt 0>

2) 遍历map集合,获取所有的keys
<#list goods07?keys as key>

3) 根据key获取对应value值goods01[key]
<#list goods07[key] as g>

首页方法改造

注意:

springboot后端架构图 springboot前后端_spring boot_08

 IndexController 

package com.cdl.spbootpro.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cdl.spbootpro.model.Goods;
import com.cdl.spbootpro.service.IGoodsService;
import com.cdl.spbootpro.utils.DataUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-05 17:31
 */
@Controller
public class IndexController {
    @Autowired
    private IGoodsService goodsService;

    @RequestMapping("/")
    public String index(Model model){
//        模板前端
//        /templates+index.html+""
        //前缀+逻辑视图名+后缀
        //        主要查询出 装饰摆件 和 墙式壁挂两个类别的上坪在首页进行展示
        List<Goods> goods01 = goodsService.list(new QueryWrapper<Goods>().eq("goods_type", "01").last("limit 6"));
        List<Goods> goods07 = goodsService.list(new QueryWrapper<Goods>().eq("goods_type", "07").last("limit 12"));
//        将数据转换成容易在页面上进行展现的数据格式
        DataUtils<Goods> dataUtils = new DataUtils<>();
        Map<String, List<Goods>> gt01 = dataUtils.transfor(3, goods01);
        Map<String, List<Goods>> gt07 = dataUtils.transfor(4, goods07);
        model.addAttribute("gt01",gt01);
        model.addAttribute("gt07",gt07);
        return "index.html";
    }

}

页面数据展示

index.html

<!DOCTYPE html>
<html>
	<head lang="en">
		<#include "common/head.html">
		<link rel="stylesheet" type="text/css" href="css/public.css"/>
		<link rel="stylesheet" type="text/css" href="css/index.css" />
	</head>
	<body>
		<!------------------------------head------------------------------>
		<#include "common/top.html">

		<!-------------------------banner--------------------------->
		<div class="block_home_slider">
			<div id="home_slider" class="flexslider">
				<ul class="slides">
					<li>
						<div class="slide">
							<img src="img/banner2.jpg"/>
						</div>
					</li>
					<li>
						<div class="slide">
							<img src="img/banner1.jpg"/>
						</div>
					</li>
				</ul>
			</div>
		</div>
		
		<!------------------------------thImg------------------------------>
		<div class="thImg">
			<div class="clearfix">
				<a href="${ctx}/page/vase_proList.html"><img src="img/i1.jpg"/></a>
				<a href="${ctx}/page/proList.html"><img src="img/i2.jpg"/></a>
				<a href="#2"><img src="img/i3.jpg"/></a>
			</div>
		</div>
		
		<!------------------------------news------------------------------>
		<div class="news">
			<div class="wrapper">
				<h2><img src="img/ih1.jpg"/></h2>
				<div class="top clearfix">
					<a href="${ctx}/page/proDetail.html"><img src="img/n1.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n2.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n3.jpg"/><p></p></a>
				</div>
				<div class="bott clearfix">
					<a href="${ctx}/page/proDetail.html"><img src="img/n4.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n5.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n6.jpg"/><p></p></a>
				</div>
				<h2><img src="img/ih2.jpg"/></h2>
				<#if gt01?? && gt01?size gt 0>
				<#list gt01?keys as key>
				<div class="flower clearfix tran">
					<!--遍历中的所有的key,是为了拿该key中的对象-->
					<#list gt01[key] as g>
					<a href="proDetail.html" class="clearfix">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="${g.goodsImg}"/>
								<span class="abr"></span>
							</dt>
							<dd>${g.goodsName}</dd>
							<dd><span>¥ ${g.goodsPrice}</span></dd>
						</dl>
					</a>
				</#list>
			</div>
		</#list>
	</#if>

				<!--<div class="flower clearfix tran">
					<a href="proDetail.html" class="clearfix">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/flo1.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【花艺】七头美丽玫瑰仿真花束</dd>
							<dd><span>¥ 79.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/flo2.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【花艺】七头美丽玫瑰仿真花束</dd>
							<dd><span>¥ 79.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/flo3.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【花艺】七头美丽玫瑰仿真花束</dd>
							<dd><span>¥ 79.00</span></dd>
						</dl>
					</a>
				</div>-->
			</div>
		</div>
		
		<!------------------------------ad------------------------------>
		<a href="#" class="ad"><img src="img/ib1.jpg"/></a>
		
		<!------------------------------people------------------------------>
		<div class="people">
			<div class="wrapper">
				<h2><img src="img/ih3.jpg"/></h2>
				<div class="pList clearfix tran">
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s7.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】不锈钢壁饰墙饰软装</dd>
							<dd><span>¥688.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s10.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】小城动物木板画壁挂北欧</dd>
							<dd><span>¥188.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s4.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】玄关假山水壁饰背景墙饰挂件创意</dd>
							<dd><span>¥599.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s9.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】金属树枝壁饰铜鸟装饰品</dd>
							<dd><span>¥928.00</span></dd>
						</dl>
					</a>
				</div>
				<div class="pList clearfix tran">
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s6.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】金属壁饰创意背景墙面挂件创意</dd>
							<dd><span>¥228.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s8.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】小城动物木板画壁挂北欧</dd>
							<dd><span>¥199.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s12.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】欧式复古挂钟创意餐厅钟表家居挂件</dd>
							<dd><span>¥666.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s1.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】客厅地中海欧式现代相片墙创意</dd>
							<dd><span>¥59.80</span></dd>
						</dl>
					</a>
				</div>
				<div class="pList clearfix tran">
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s5.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】铁艺荷叶壁挂软装背景墙上装饰品</dd>
							<dd><span>¥800.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s3.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】欧式照片墙 创意组合结婚礼物</dd>
							<dd><span>¥189.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s2.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】欧式钟表相框墙挂墙创意组合</dd>
							<dd><span>¥148.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/s11.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【最家】小城动物木板画壁挂北欧</dd>
							<dd><span>¥188.00</span></dd>
						</dl>
					</a>
				</div>
			</div>
		</div>

		<#include "common/footer.html"/>

		<script src="js/public.js" type="text/javascript" charset="utf-8"></script>
		<script src="js/nav.js" type="text/javascript" charset="utf-8"></script>
		<script src="js/jquery.flexslider-min.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			$(function() {
				$('#home_slider').flexslider({
					animation: 'slide',
					controlNav: true,
					directionNav: true,
					animationLoop: true,
					slideshow: true,
					slideshowSpeed:2000,
					useCSS: false
				});

			});
		</script>
	</body>
</html>

运行结果:

springboot后端架构图 springboot前后端_springboot后端架构图_09

springboot后端架构图 springboot前后端_springboot后端架构图_10

 可以发现所有的数据已经不一样了

将页面的展示格式改变

index.html

<!DOCTYPE html>
<html>
	<head lang="en">
		<#include "common/head.html">
		<link rel="stylesheet" type="text/css" href="css/public.css"/>
		<link rel="stylesheet" type="text/css" href="css/index.css" />
	</head>
	<body>
		<!------------------------------head------------------------------>
		<#include "common/top.html">

		<!-------------------------banner--------------------------->
		<div class="block_home_slider">
			<div id="home_slider" class="flexslider">
				<ul class="slides">
					<li>
						<div class="slide">
							<img src="img/banner2.jpg"/>
						</div>
					</li>
					<li>
						<div class="slide">
							<img src="img/banner1.jpg"/>
						</div>
					</li>
				</ul>
			</div>
		</div>
		
		<!------------------------------thImg------------------------------>
		<div class="thImg">
			<div class="clearfix">
				<a href="${ctx}/page/vase_proList.html"><img src="img/i1.jpg"/></a>
				<a href="${ctx}/page/proList.html"><img src="img/i2.jpg"/></a>
				<a href="#2"><img src="img/i3.jpg"/></a>
			</div>
		</div>
		
		<!------------------------------news------------------------------>
		<div class="news">
			<div class="wrapper">
				<h2><img src="img/ih1.jpg"/></h2>
				<div class="top clearfix">
					<a href="${ctx}/page/proDetail.html"><img src="img/n1.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n2.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n3.jpg"/><p></p></a>
				</div>
				<div class="bott clearfix">
					<a href="${ctx}/page/proDetail.html"><img src="img/n4.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n5.jpg"/><p></p></a>
					<a href="${ctx}/page/proDetail.html"><img src="img/n6.jpg"/><p></p></a>
				</div>
				<h2><img src="img/ih2.jpg"/></h2>
				<#if gt01?? && gt01?size gt 0>
				<#list gt01?keys as key>
				<div class="flower clearfix tran">
					<!--遍历中的所有的key,是为了拿该key中的对象-->
					<#list gt01[key] as g>
					<a href="proDetail.html" class="clearfix">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="${g.goodsImg}"/>
								<span class="abr"></span>
							</dt>
							<dd>${g.goodsName}</dd>
							<dd><span>¥ ${g.goodsPrice}</span></dd>
						</dl>
					</a>
				</#list>
			</div>
		</#list>
	</#if>

				<!--<div class="flower clearfix tran">
					<a href="proDetail.html" class="clearfix">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/flo1.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【花艺】七头美丽玫瑰仿真花束</dd>
							<dd><span>¥ 79.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/flo2.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【花艺】七头美丽玫瑰仿真花束</dd>
							<dd><span>¥ 79.00</span></dd>
						</dl>
					</a>
					<a href="proDetail.html">
						<dl>
							<dt>
								<span class="abl"></span>
								<img src="img/flo3.jpg"/>
								<span class="abr"></span>
							</dt>
							<dd>【花艺】七头美丽玫瑰仿真花束</dd>
							<dd><span>¥ 79.00</span></dd>
						</dl>
					</a>
				</div>-->
			</div>
		</div>
		
		<!------------------------------ad------------------------------>
		<a href="#" class="ad"><img src="img/ib1.jpg"/></a>
		
		<!------------------------------people------------------------------>
		<div class="people">
			< class="wrapper">
				<h2><img src="img/ih3.jpg"/></h2>
				<#if gt07?? && gt07?size gt 0>
				<#list gt07?keys as key>
			<div class="pList clearfix tran">
				<#list gt07[key] as g>
				<a href="proDetail.html">
					<dl>
						<dt>
							<span class="abl"></span>
							<img src="${g.goodsImg}"/>
							<span class="abr"></span>
						</dt>
						<dd>${g.goodsName}</dd>
						<dd><span>¥ ${g.goodsPrice}</span></dd>
					</dl>
				</a>
		</#list>
		</div>
</#list>
</#if>

			</div>
		</div>

		<#include "common/footer.html"/>

		<script src="js/public.js" type="text/javascript" charset="utf-8"></script>
		<script src="js/nav.js" type="text/javascript" charset="utf-8"></script>
		<script src="js/jquery.flexslider-min.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			$(function() {
				$('#home_slider').flexslider({
					animation: 'slide',
					controlNav: true,
					directionNav: true,
					animationLoop: true,
					slideshow: true,
					slideshowSpeed:2000,
					useCSS: false
				});

			});
		</script>
	</body>
</html>

springboot后端架构图 springboot前后端_springboot后端架构图_11

此时的页面还是不能够跳转的

四、用户明文登录 

要实现登录功能,要确保页面间能够跳转

公共跳转控制器PageController

PageController

package com.cdl.spbootpro.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-05 21:45
 */
@Controller
public class PageController {

    /**
     * 直接跳转页面(没有层级文件夹的情况)
     * 列如:
     *  http://localhost:8081/page/paint.html
     *  http://localhost:8081/page/perfume.html
     *
     * @return
     */
    @RequestMapping("/page/{page}")
    public String page(@PathVariable(value = "page") String page) {
        return page;
    }

    /**
     * 直接跳转页面(存在层级文件夹的情况)
     *
     * @return
     */
    @RequestMapping("/page/{dir}/{page}")
    public String dir(@PathVariable(value = "dir") String dir, @PathVariable(value = "page") String page) {
        return dir + "/" + page;
    }


}

跳转成功

springboot后端架构图 springboot前后端_spring boot_12

 在js下建一个包(login)再建一个文件(login.js)用于写登录的内容

$(function () {
   // alert(1);
    //    给登录按钮添加事件
    $("#login").click(function () {
        let mobile = $("#mobile").val();
        let password = $("#password").val();

        //2.向后台发起登录ajax请求
        $.post('/user/userLogin',{
            mobile:mobile,
            password:password
        },function(rs){
            if(rs.code!=200){
                alert(rs.msg);
            }else
            //alert(rs.msg);
                location.href='/';
        },'json');
    });
});

定义UserDto.java接受前台传递的参数

UserDto 

package com.cdl.spbootpro.model.dto;

import com.cdl.spbootpro.validator.IsMobile;
import lombok.Data;

import javax.validation.constraints.NotBlank;

@Data
public class UserDto {
    @NotBlank(message = "手机号码不能为空!")
    @IsMobile
    private String mobile;
    @NotBlank(message = "密码不能为空!")
    private String password;
}

处理浏览器端的请求 UserController

package com.cdl.spbootpro.controller;


import com.cdl.spbootpro.model.dto.UserDto;
import com.cdl.spbootpro.service.IUserService;
import com.cdl.spbootpro.utils.JsonResponseBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

/**
 * <p>
 * 用户信息表 前端控制器
 * </p>
 *
 * @author cdl
 * @since 2022-11-05
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    @RequestMapping("/userLogin")
    public JsonResponseBody userLogin(@Valid UserDto userDto,
                                      HttpServletRequest req,
                                      HttpServletResponse resp){
        return userService.userLogin(userDto,req,resp);
    }

}

对应的业务代码

IUserService 

package com.cdl.spbootpro.service;

import com.cdl.spbootpro.model.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cdl.spbootpro.model.dto.UserDto;
import com.cdl.spbootpro.utils.JsonResponseBody;

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

/**
 * <p>
 * 用户信息表 服务类
 * </p>
 *
 * @author cdl
 * @since 2022-11-05
 */
public interface IUserService extends IService<User> {

    JsonResponseBody userLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp);
}

 UserServiceImpl 

package com.cdl.spbootpro.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cdl.spbootpro.exception.BusinessException;
import com.cdl.spbootpro.model.User;
import com.cdl.spbootpro.mapper.UserMapper;
import com.cdl.spbootpro.model.dto.UserDto;
import com.cdl.spbootpro.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cdl.spbootpro.utils.JsonResponseBody;
import com.cdl.spbootpro.utils.JsonResponseStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author cdl
 * @since 2022-11-05
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;


    @Override
    public JsonResponseBody userLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
//要做的事情:
// 判断mobile和password是否为空 已由JSP303完成(在controller中的UserDto添加@Valid便可解决)
//判断mobile格式是否正确  (自定义验证注解@IsMobile)
//根据用户手机号码查询用户
 User user = userMapper.selectOne(new QueryWrapper<User>()
                .eq("id", userDto.getMobile()));
// 判断所查询的用户是否存在(校验账号)
        if(null==user)
            throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
//校验密码
        if(!user.getPassword().equals(userDto.getPassword()))
            throw new BusinessException(JsonResponseStatus.USER_PASSWORD_ERROR);
        return new JsonResponseBody<>();
    }
}

注意:

springboot后端架构图 springboot前后端_java_13

 此时的数据库(没有使用盐加密)

springboot后端架构图 springboot前后端_spring boot_14

 测试一下

什么也不填

springboot后端架构图 springboot前后端_java_15

输入正确的手机号码和密码(123456)

springboot后端架构图 springboot前后端_springboot后端架构图_16

会进入主页面

 输入错的密码 

springboot后端架构图 springboot前后端_spring_17

五、前端及数据库密码加密

盐加密

前端加密:防止客户端浏览器F12导致密码泄露

后端加密:防止数据库数据泄露导致密码泄露

login.js变更如下

login.js

$(function () {
   // alert(1);
    //    给登录按钮添加事件
    $("#login").click(function () {
        let mobile = $("#mobile").val();
        let password = $("#password").val();

        //1.密码加密
        //1) 定义固定盐
        let salt='f1g2h3j4';
        //2) 固定盐混淆
        let temp=salt.charAt(1)+""+salt.charAt(5)+password+salt.charAt(0)+""+salt.charAt(3);
        //3) 使用MD5完成前端第一次加密
        let pwd=md5(temp);

        console.log("mobile=%s,password=%s",mobile,pwd);

        //2.向后台发起登录ajax请求
        $.post('/user/userLogin',{
            mobile:mobile,
            password:pwd
        },function(rs){
            if(rs.code!=200){
                alert(rs.msg);
            }else
            //alert(rs.msg);
                location.href='/';
        },'json');
    });
});

 UserServiceImpl 

package com.cdl.spbootpro.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cdl.spbootpro.exception.BusinessException;
import com.cdl.spbootpro.model.User;
import com.cdl.spbootpro.mapper.UserMapper;
import com.cdl.spbootpro.model.dto.UserDto;
import com.cdl.spbootpro.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cdl.spbootpro.utils.JsonResponseBody;
import com.cdl.spbootpro.utils.JsonResponseStatus;
import com.cdl.spbootpro.utils.MD5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author cdl
 * @since 2022-11-05
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;


    @Override
    public JsonResponseBody userLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
//要做的事情:
// 判断mobile和password是否为空 已由JSP303完成(在controller中的UserDto添加@Valid便可解决)
//判断mobile格式是否正确  (自定义验证注解@IsMobile)
//根据用户手机号码查询用户
 User user = userMapper.selectOne(new QueryWrapper<User>()
                .eq("id", userDto.getMobile()));
// 判断所查询的用户是否存在(校验账号)
        if(null==user)
            throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
//前台传递到后台的密码 要经过工具类MD5加密一次才有可能跟数据库密码匹配上
        String pwd = MD5Utils.formPassToDbPass(userDto.getPassword(), user.getSalt());
        //校验密码
        if(!user.getPassword().equals(pwd))
            throw new BusinessException(JsonResponseStatus.USER_PASSWORD_ERROR);
        return new JsonResponseBody<>();
    }
}

在login.html中引入md5.js

login.html

<!DOCTYPE html>
<html>
	<head>
		<#include "common/head.html">
		<link rel="stylesheet" type="text/css" href="css/public.css"/>
		<link rel="stylesheet" type="text/css" href="css/login.css"/>
		<script type="text/javascript" src="js/jquery-1.12.4.min.js"></script>
		<script type="text/javascript" src="js/md5.js"></script>
		<script type="text/javascript" src="js/login/login.js"></script>

	</head>
	<body>
		<!-------------------login-------------------------->
		<div class="login">
			<form action="#" method="post">
				<h1><a href="${ctx}/"><img src="img/temp/logo.png"></a></h1>
				<p></p>
				<div class="msg-warn hide"><b></b>公共场所不建议自动登录,以防账号丢失</div>
				<p><input style="font-size:14px;" type="text" id="mobile" value="" placeholder="邮箱/手机号"></p>
				<p><input style="font-size:14px;" type="password" id="password" value="" placeholder="密码"></p>
				<p><input type="button" id="login" value="登  录"></p>
				<p class="txt"><a class="" href="${ctx}/page/reg.html">免费注册</a><a href="${ctx}/page/forget.html">忘记密码?</a></p>
			</form>
		</div>
		
	</body>
</html>

加密成功

springboot后端架构图 springboot前后端_springboot后端架构图_18

六、服务端客户端登录密码管理

登录令牌管理

将登录的用户数据分别保留在客户端以及服务端

 UserServiceImpl.java变更如下

UserServiceImpl 

package com.cdl.spbootpro.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cdl.spbootpro.exception.BusinessException;
import com.cdl.spbootpro.model.User;
import com.cdl.spbootpro.mapper.UserMapper;
import com.cdl.spbootpro.model.dto.UserDto;
import com.cdl.spbootpro.service.IRedisService;
import com.cdl.spbootpro.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cdl.spbootpro.utils.CookieUtils;
import com.cdl.spbootpro.utils.JsonResponseBody;
import com.cdl.spbootpro.utils.JsonResponseStatus;
import com.cdl.spbootpro.utils.MD5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author cdl
 * @since 2022-11-05
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private IRedisService redisService;


    @Override
    public JsonResponseBody userLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
//要做的事情:
// 判断mobile和password是否为空 已由JSP303完成(在controller中的UserDto添加@Valid便可解决)
//判断mobile格式是否正确  (自定义验证注解@IsMobile)
//根据用户手机号码查询用户
 User user = userMapper.selectOne(new QueryWrapper<User>()
                .eq("id", userDto.getMobile()));
// 判断所查询的用户是否存在(校验账号)
        if(null==user)
            throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
//前台传递到后台的密码 要经过工具类MD5加密一次才有可能跟数据库密码匹配上
        String pwd = MD5Utils.formPassToDbPass(userDto.getPassword(), user.getSalt());
        //校验密码
        if(!user.getPassword().equals(pwd))
            throw new BusinessException(JsonResponseStatus.USER_PASSWORD_ERROR);
        //将登陆用户对象与token令牌进行绑定保存到cookie和redis
        //创建登陆令牌token
        String token= UUID.randomUUID().toString().replace("-","");
        //将token令牌保存到cookie中
        CookieUtils.setCookie(req,resp,"token",token,7200);
        //将登陆token令牌与用户对象user绑定到redis中
        redisService.setUserToRedis(token,user);
        //将用户登陆的昵称设置到cookie中
        CookieUtils.setCookie(req,resp,"nickname",user.getNickname());
        return new JsonResponseBody<>();
    }
}

IRedisService

package com.cdl.spbootpro.service;

import com.cdl.spbootpro.model.User;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-06 0:43
 */
public interface IRedisService {
    //存贮数据
    void setUserToRedis(String token, User user);

    User getUserToRedis(String token);
}

 IRedisServiceImpl 

package com.cdl.spbootpro.service.impl;

import com.cdl.spbootpro.model.User;
import com.cdl.spbootpro.service.IRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author cdl
 * @site www.cdl.com
 * @create 2022-11-06 0:45
 */
@Service
public class IRedisServiceImpl implements IRedisService {
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public void setUserToRedis(String token, User user) {
        redisTemplate.opsForValue().set("user:"+token,user,7200L, TimeUnit.SECONDS);//进reids
    }

    @Override
    public User getUserToRedis(String token) {
        User user = (User) redisTemplate.opsForValue().get("user:" + token);
       return user;
    }
}

运行

springboot后端架构图 springboot前后端_redis_19

 

springboot后端架构图 springboot前后端_springboot后端架构图_20

 用的工具类

utils下

CookieUtils 

package com.cdl.spbootpro.utils;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

@Slf4j
public class CookieUtils {

    /**
     *
     * @Description: 得到Cookie的值, 不编码
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        return getCookieValue(request, cookieName, false);
    }

    /**
     *
     * @Description: 得到Cookie的值
     * @param request
     * @param cookieName
     * @param isDecoder
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {
                        retValue = cookieList[i].getValue();
                    }
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     *
     * @Description: 得到Cookie的值
     * @param request
     * @param cookieName
     * @param encodeString
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     *
     * @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);
    }

    /**
     *
     * @Description: 设置Cookie的值 在指定时间内生效,但不编码
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, int cookieMaxage) {
        setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
    }

    /**
     *
     * @Description: 设置Cookie的值 不设置生效时间,但编码
     * 在服务器被创建,返回给客户端,并且保存客户端
     * 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中
     * 如果没有设置,会默认把cookie保存在浏览器的内存中
     * 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param isEncode
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, boolean isEncode) {
        setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    }

    /**
     *
     * @Description: 设置Cookie的值 在指定时间内生效, 编码参数
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage
     * @param isEncode
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, int cookieMaxage, boolean isEncode) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
    }

    /**
     *
     * @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage
     * @param encodeString
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, int cookieMaxage, String encodeString) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    }

    /**
     *
     * @Description: 删除Cookie带cookie域名
     * @param request
     * @param response
     * @param cookieName
     */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
                                    String cookieName) {
        doSetCookie(request, response, cookieName, null, -1, false);
    }


    /**
     *
     * @Description: 设置Cookie的值,并使其在指定时间内生效
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage	cookie生效的最大秒数
     * @param isEncode
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                          String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 设置域名的cookie
                String domainName = getDomainName(request);
                log.info("========== domainName: {} ==========", domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @Description: 设置Cookie的值,并使其在指定时间内生效
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage	cookie生效的最大秒数
     * @param encodeString
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                          String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 设置域名的cookie
                String domainName = getDomainName(request);
                log.info("========== domainName: {} ==========", domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @Description: 得到cookie的域名
     * @return
     */
    private static final String getDomainName(HttpServletRequest request) {
        String domainName = null;

        String serverName = request.getRequestURL().toString();
        if (serverName == null || serverName.equals("")) {
            domainName = "";
        } else {
            serverName = serverName.toLowerCase();
            serverName = serverName.substring(7);
            final int end = serverName.indexOf("/");
            serverName = serverName.substring(0, end);
            if (serverName.indexOf(":") > 0) {
                String[] ary = serverName.split("\\:");
                serverName = ary[0];
            }

            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3 && !isIp(serverName)) {
                // www.xxx.com.cn
                domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
            } else if (len <= 3 && len > 1) {
                // xxx.com or xxx.cn
                domainName = "." + domains[len - 2] + "." + domains[len - 1];
            } else {
                domainName = serverName;
            }
        }
        return domainName;
    }

    public static String trimSpaces(String IP){//去掉IP字符串前后所有的空格
        while(IP.startsWith(" ")){
            IP= IP.substring(1,IP.length()).trim();
        }
        while(IP.endsWith(" ")){
            IP= IP.substring(0,IP.length()-1).trim();
        }
        return IP;
    }

    public static boolean isIp(String IP){//判断是否是一个IP
        boolean b = false;
        IP = trimSpaces(IP);
        if(IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){
            String s[] = IP.split("\\.");
            if(Integer.parseInt(s[0])<255)
                if(Integer.parseInt(s[1])<255)
                    if(Integer.parseInt(s[2])<255)
                        if(Integer.parseInt(s[3])<255)
                            b = true;
        }
        return b;
    }
}

 DataUtils<T> 

package com.cdl.spbootpro.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DataUtils<T> {
    /**
     * 转换方法,基于商品的排版情况(一行三列、一行四列等等)进行数据分行处理
     * @param cols  一行显示几列
     * @param goods 需要筛选的数据集
     * @return
     */
    public Map<String, List<T>> transfor(int cols, List<T> goods){
        Map<String,List<T>> data=new HashMap<>();
        List<T> rs=new ArrayList<>();
        int len=goods.size();
        for (int i = 0; i < len; i++) {
            rs.add(goods.get(i));
            if((i+1)%cols==0){
                data.put("goods"+(i+1),rs);
                if(i==len-1)
                    break;
                rs=new ArrayList<>();
                continue;
            }
            if(i==len-1){
                data.put("goods"+(i+1),rs);
            }
        }
        return data;
    }
}

 JsonResponseBody<T>

package com.cdl.spbootpro.utils;

import lombok.Data;

import java.io.Serializable;

/**
 * 响应封装类
 */
@Data
public class JsonResponseBody<T> implements Serializable {

    private String msg="OK";
    private T data;
    private Integer code;
    private Integer total;

    public JsonResponseBody(){
        this.data=null;
        this.code=JsonResponseStatus.SUCCESS.getCode();
    }

    public JsonResponseBody(T data){
        this.data=data;
        this.code=JsonResponseStatus.SUCCESS.getCode();
    }

    public JsonResponseBody(T data,Integer total){
        this.data=data;
        this.total=total;
        this.code=JsonResponseStatus.SUCCESS.getCode();
    }

    public JsonResponseBody(JsonResponseStatus jsonResponseStatus){
        this.msg=jsonResponseStatus.getMsg();
        this.code=jsonResponseStatus.getCode();
    }

    public JsonResponseBody(JsonResponseStatus jsonResponseStatus,T data){
        this.data=data;
        this.msg=jsonResponseStatus.getMsg();
        this.code=jsonResponseStatus.getCode();
    }
}

 JsonResponseStatus

package com.cdl.spbootpro.utils;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@AllArgsConstructor
public enum JsonResponseStatus {

    SUCCESS(200,"OK"),
    ERROR(500,"内部错误"),

    USER_LOGIN_ERROR(100101,"用户名或者密码错误"),
    USER_MOBILE_ERROR(100102,"手机号码格式错误"),
    USER_PASSWORD_ERROR(100103,"用户密码错误"),
    USER_USERNAME_ERROR(100104,"账号不存在"),

    BIND_ERROR(200101,"参数校验异常"),
    TOKEN_EEROR(200102,"token令牌失效或已过期")
    ;

    private final Integer code;
    private final String msg;
}

 MD5Utils 

package com.cdl.spbootpro.utils;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * MD5加密
 * 用户端:password=MD5(明文+固定Salt)
 * 服务端:password=MD5(用户输入+随机Salt)
 * 用户端MD5加密是为了防止用户密码在网络中明文传输,服务端MD5加密是为了提高密码安全性,双重保险。
 */
@Component
public class MD5Utils {

    //加密盐,与前端一致
    private static String salt="f1g2h3j4";

    /**
     * md5加密
     * @param src
     * @return
     */
    public static String md5(String src){
        return DigestUtils.md5Hex(src);
    }

    /**
     * 获取加密的盐
     * @return
     */
    public static String createSalt(){
        return UUID.randomUUID().toString().replace("-","");
    }

    /**
     * 将前端的明文密码通过MD5加密方式加密成后端服务所需密码
     * 注意:该步骤实际是在前端完成!!!
     * @param inputPass 明文密码
     * @return
     */
    public static String inputPassToFormpass(String inputPass){
        //混淆固定盐salt,安全性更可靠
        String str=salt.charAt(1)+""+salt.charAt(5)+inputPass+salt.charAt(0)+""+salt.charAt(3);
        return md5(str);
    }

    /**
     * 将后端密文密码+随机salt生成数据库的密码
     * @param formPass
     * @param salt
     * @return
     */
    public static String formPassToDbPass(String formPass,String salt){
        //混淆固定盐salt,安全性更可靠
        String str=salt.charAt(7)+""+salt.charAt(4)+formPass+salt.charAt(1)+""+salt.charAt(5);
        return md5(str);
    }

    /**
     * 将用户输入的密码转换成数据库的密码
     * @param inputPass 明文密码
     * @param salt      盐
     * @return
     */
    public static String inputPassToDbPass(String inputPass,String salt){
        String formPass = inputPassToFormpass(inputPass);
        String dbPass = formPassToDbPass(formPass, salt);
        return dbPass;
    }

    public static void main(String[] args) {
        //d7aaa28e3b8e6c88352bd5e7c23829f9
        //5512a78a188b318c074a15f9b056a712
        String formPass = inputPassToFormpass("123456");
        System.out.println("前端加密密码:"+formPass);
        String salt = createSalt();
        System.out.println("后端加密随机盐:"+salt);
        String dbPass = formPassToDbPass(formPass, salt);
        System.out.println("后端加密密码:"+dbPass);
        System.out.println("-------------------------------------------");
        String dbPass1 = inputPassToDbPass("123456", salt);
        System.out.println("最终加密密码:"+dbPass1);
    }
}

 MybatisPlusConfig 

package com.cdl.spbootpro.utils;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.cdl.shoppingpro.service.*.mapper*")
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

 PageBean 

package com.cdl.spbootpro.utils;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Map;

public class PageBean implements Serializable {

	//页码
	private int page=1;
	//每页显示条数
	private int rows=10;
	//总记录数
	private int total=0;
	//是否分页标记
	private boolean pagination=true;
	//上一次请求的路径
	private String url;
	//请求参数Map集合
	private Map<String,String[]> map;
	
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public Map<String, String[]> getMap() {
		return map;
	}
	public void setMap(Map<String, String[]> map) {
		this.map = map;
	}
	public int getPage() {
		return page;
	}
	public void setPage(int page) {
		this.page = page;
	}
	public int getRows() {
		return rows;
	}
	public void setRows(int rows) {
		this.rows = rows;
	}
	public int getTotal() {
		return total;
	}
	public void setTotal(int total) {
		this.total = total;
	}
	public boolean isPagination() {
		return pagination;
	}
	public void setPagination(boolean pagination) {
		this.pagination = pagination;
	}
	
	//重载setPage/setRows/setPagination方法
	public void setPage(String page) {
		if(null!=page&&!"".equals(page))
			this.page=Integer.parseInt(page);
	}
	public void setRows(String rows) {
		if(null!=rows&&!"".equals(rows))
			this.rows=Integer.parseInt(rows);
	}
	public void setPagination(String pagination) {
		if(null!=pagination&&!"".equals(pagination))
			this.pagination=Boolean.parseBoolean(pagination);
	}
	
	public void setRequest(HttpServletRequest req) {
		//获取前端提交的page/rows/pagination参数
		String page=req.getParameter("page");
		String rows=req.getParameter("rows");
		String pagination=req.getParameter("pagination");
		
		//设置属性
		this.setPage(page);
		this.setPagination(pagination);
		this.setRows(rows);
		
		//设置上一次请求的路径
		//第一次请求:
		//http://localhost:8080/项目名/请求路径.action?page=1
		//第二次请求:下一页  page=2
		//项目名+请求路径.action
		//req.getContextPath()+req.getServletPath()==req.getRequestURI()
		this.url=req.getRequestURI();
		
		//获取请求参数集合
		// checkbox name='hobby'
		// Map<String,String[]> hobby==key  value==new String[]{"篮球","足球",..}
		this.map=req.getParameterMap();
	}
	
	/**
	 * 获取分页的起始位置
	 * @return
	 */
	public int getStartIndex() {
		//第1页:(1-1)*10  ==0    limit 0,10
		//第2页:(2-1)*10  ==10   limit 10,10
		//..
		return (this.page-1)*this.rows;
	}
	
	//获取末页、上一页、下一页
	/**
	 * 获取末页
	 * @return
	 */
	public int getMaxPager() {
		int maxPager=this.total/this.rows;
		if(this.total%this.rows!=0)
			maxPager++;
		return maxPager;
	}
	
	/**
	 * 获取上一页
	 * @return
	 */
	public int getPreviousPager() {
		int previousPager=this.page-1;
		if(previousPager<=1)
			previousPager=1;
		return previousPager;
	}
	
	/**
	 * 获取下一页
	 * @return
	 */
	public int getNextPager() {
		int nextPager=this.page+1;
		if(nextPager>getMaxPager())
			nextPager=getMaxPager();
		return nextPager;
	}
	@Override
	public String toString() {
		return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pagination=" + pagination
				+ ", url=" + url + ", map=" + map + "]";
	}
	
}

 ValidatorUtils 

package com.cdl.spbootpro.utils;

import org.apache.commons.lang3.StringUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则校验工具类
 * @author 刘开宇
 */
public class ValidatorUtils {

    private static final Pattern mobile_pattern=Pattern.compile("[1]([0-9])[0-9]{9}$");

    public static boolean isMobile(String mobile){
        if(StringUtils.isEmpty(mobile))
            return false;
    Matcher matcher = mobile_pattern.matcher(mobile);
        return matcher.matches();
}
}