springboot项目:前后端搭建

目录

一、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项目:前后端搭建_第1张图片

二、构建springboot项目

创建SpringBoot项目并配置POM

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

springboot项目:前后端搭建_第2张图片

spring-boot-starter-freemarker
spring-boot-starter-web
mysql-connector-java 5.1.44
lombok

mybatis-plus-boot-starter 3.4.0
mybatis-plus-generator 3.4.0

HikariCP

commons-codec
commons-lang3 3.6

spring-boot-starter-validation

spring-boot-starter-data-redis

参考

pom.xml



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.9.RELEASE
         
    
    com.cdl
    spbootpro
    0.0.1-SNAPSHOT
    spbootpro
    Demo project for Spring Boot

    
        1.8
    
    
        
        
            org.springframework.boot
            spring-boot-starter-freemarker
        

        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            mysql
            mysql-connector-java
            runtime
            5.1.44
        

        
        
            org.projectlombok
            lombok
            true
        

        
        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        

        
            junit
            junit
            test
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.0
        
        
        
            com.baomidou
            mybatis-plus-generator
            3.4.0
        

        
        
            com.zaxxer
            HikariCP
        

        
        
            commons-codec
            commons-codec
        

        
            org.apache.commons
            commons-lang3
            3.6
        

        
        
            org.springframework.boot
            spring-boot-starter-validation
        

        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        

        
        
            org.apache.commons
            commons-pool2
        

        
        
        
            com.alipay.sdk
            alipay-easysdk
            2.0.1
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


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项目:前后端搭建_第3张图片

 导入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 {

    /**
     * 

* 读取控制台内容 *

*/ 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 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 superControllerClassPackage??>
import ${superControllerClassPackage};


/**
 * 

* ${table.comment!} 前端控制器 *

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

entity2.java.ftl

package ${package.Entity};

<#list table.importPackages as pkg>
import ${pkg};

<#if swagger2>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
    <#if chainModel>
import lombok.experimental.Accessors;
    


/**
 * 

* ${table.comment!} *

* * @author ${author} * @since ${date} */ <#if entityLombokModel> @Data <#if superEntityClass??> @EqualsAndHashCode(callSuper = true) <#else> @EqualsAndHashCode(callSuper = false) <#if chainModel> @Accessors(chain = true) <#if table.convert> @TableName("${table.name}") <#if swagger2> @ApiModel(value="${entity}对象", description="${table.comment!}") <#if superEntityClass??> public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}> { <#elseif activeRecord> public class ${entity} extends Model<${entity}> { <#else> public class ${entity} implements Serializable { <#if entitySerialVersionUID> private static final long serialVersionUID = 1L; <#-- ---------- BEGIN 字段循环遍历 ----------> <#list table.fields as field> <#if field.keyFlag> <#assign keyPropertyName="${field.propertyName}"/> <#if field.comment!?length gt 0> <#if swagger2> @ApiModelProperty(value = "${field.comment}") <#else> /** * ${field.comment} */ <#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}") <#-- 普通字段 --> <#elseif field.fill??> <#-- ----- 存在字段填充设置 -----> <#if field.convert> @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill}) <#else> @TableField(fill = FieldFill.${field.fill}) <#elseif field.convert> @TableField("${field.annotationColumnName}") <#-- 乐观锁注解 --> <#if (versionFieldName!"") == field.name> @Version <#-- 逻辑删除注解 --> <#if (logicDeleteFieldName!"") == field.name> @TableLogic private ${field.propertyType} ${field.propertyName}; <#------------ END 字段循环遍历 ----------> <#if !entityLombokModel> <#list table.fields as field> <#if field.propertyType == "boolean"> <#assign getprefix="is"/> <#else> <#assign getprefix="get"/> 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}) { this.${field.propertyName} = ${field.propertyName}; <#if chainModel> return this; } <#if entityColumnConstant> <#list table.fields as field> public static final String ${field.name?upper_case} = "${field.name}"; <#if activeRecord> @Override protected Serializable pkVal() { <#if keyPropertyName??> return this.${keyPropertyName}; <#else> return null; } <#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} + "}"; } }

mapper2.java.ftl

package ${package.Mapper};

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

/**
 * 

* ${table.comment!} Mapper 接口 *

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

mapper2.xml.ftl





<#if enableCache>
    
    


<#if baseResultMap>
    
    
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位-->
        


<#list table.commonFields as field><#--生成公共字段 -->
        

<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 -->
        


    


<#if baseColumnList>
    
    
<#list table.commonFields as field>
        ${field.columnName},

        ${table.fieldNames}
    



service2.java.ftl

package ${package.Service};

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

/**
 * 

* ${table.comment!} 服务类 *

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

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;

/**
 * 

* ${table.comment!} 服务实现类 *

* * @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} { }

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

springboot项目:前后端搭建_第4张图片

 生成成功

springboot项目:前后端搭建_第5张图片

 编辑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项目:前后端搭建_第6张图片

 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 goods01 = goodsService.list(new QueryWrapper().eq("goods_type", "01").last("limit 6"));
        List goods07 = goodsService.list(new QueryWrapper().eq("goods_type", "07").last("limit 12"));
//        将数据转换成容易在页面上进行展现的数据格式
        DataUtils dataUtils = new DataUtils<>();
        Map> gt01 = dataUtils.transfor(3, goods01);
        Map> gt07 = dataUtils.transfor(4, goods07);
        model.addAttribute("gt01",gt01);
        model.addAttribute("gt07",gt07);
        return "index.html";
    }

}

页面数据展示

index.html



	
		<#include "common/head.html">
		
		
	
	
		
		<#include "common/top.html">

		
		

<#if gt01?? && gt01?size gt 0> <#list gt01?keys as key>
<#include "common/footer.html"/>

运行结果:

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

将页面的展示格式改变

index.html



	
		<#include "common/head.html">
		
		
	
	
		
		<#include "common/top.html">

		
		

<#if gt01?? && gt01?size gt 0> <#list gt01?keys as key>
< class="wrapper">

<#if gt07?? && gt07?size gt 0> <#list gt07?keys as key>
<#include "common/footer.html"/>

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

四、用户明文登录 

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

公共跳转控制器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项目:前后端搭建_第7张图片

 在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;

/**
 * 

* 用户信息表 前端控制器 *

* * @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;

/**
 * 

* 用户信息表 服务类 *

* * @author cdl * @since 2022-11-05 */ public interface IUserService extends IService { 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;

/**
 * 

* 用户信息表 服务实现类 *

* * @author cdl * @since 2022-11-05 */ @Service public class UserServiceImpl extends ServiceImpl 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() .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项目:前后端搭建_第8张图片

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

springboot项目:前后端搭建_第9张图片

 测试一下

什么也不填

springboot项目:前后端搭建_第10张图片

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

springboot项目:前后端搭建_第11张图片

会进入主页面

 输入错的密码 

springboot项目:前后端搭建_第12张图片

springboot项目:前后端搭建_第13张图片

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

盐加密

前端加密:防止客户端浏览器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;

/**
 * 

* 用户信息表 服务实现类 *

* * @author cdl * @since 2022-11-05 */ @Service public class UserServiceImpl extends ServiceImpl 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() .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



	
		<#include "common/head.html">
		
		
		
		
		

	
	
		
		
		
	

加密成功

springboot项目:前后端搭建_第14张图片

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

登录令牌管理

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

 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;

/**
 * 

* 用户信息表 服务实现类 *

* * @author cdl * @since 2022-11-05 */ @Service public class UserServiceImpl extends ServiceImpl 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() .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 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项目:前后端搭建_第15张图片

 springboot项目:前后端搭建_第16张图片

 用的工具类

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 

package com.cdl.spbootpro.utils;

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

public class DataUtils {
    /**
     * 转换方法,基于商品的排版情况(一行三列、一行四列等等)进行数据分行处理
     * @param cols  一行显示几列
     * @param goods 需要筛选的数据集
     * @return
     */
    public Map> transfor(int cols, List goods){
        Map> data=new HashMap<>();
        List 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

package com.cdl.spbootpro.utils;

import lombok.Data;

import java.io.Serializable;

/**
 * 响应封装类
 */
@Data
public class JsonResponseBody 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 map;
	
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public Map getMap() {
		return map;
	}
	public void setMap(Map 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 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();
}
}

你可能感兴趣的:(spring,boot,spring,boot,java,spring)