• SpringBoot电商项目前后端界面搭建


    目录

    一、SpringBoot项目简介

    1、技术点介绍

    2、数据表介绍

    二、构建SpringBoot项目

    1.创建SpringBoot项目并配置POM

    2.配置application.yml

    3.启动类配置

    4.首页访问 ,导入前端页面及页面对应的js/css/images文件

    三、实现首页功能

    0)导入帮助类

    config:

    exception:

    Generator:

    Utils:

    在generater类运行自动生成代码:

    1)创建公共跳转控制器PageController

    2)使用Mybatis-plus反向生成代码(Goods商品信息)

    3)创建IndexController并定义商品查询请求处理方法

    四、用户明文登录

    创建UserController类实现用户登录

            1.1)构建UserDto,定义mobile和password属性

            1.2)创建UserController类

            1.3)定义userLogin(UserDto userDto,HttpServletRequest req,HttpServletResponse resp)

            1.4)定义响应封装类JsonResponseBody和JsonResponseStatus

            1.5)在IUserService中定义userLogin(UserVo userVo),并返回JsonResponseBody

            1.6)全局异常处理

            1.7)自定义注解参数校验(JSR303)

    login.js:

    测试多种情况: 

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

    UserServiceImpl.java变更如下

    login.js变更如下:

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

    UserServiceImpl.java变更如下


    一、SpringBoot项目简介

    1、技术点介绍

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

    2、数据表介绍

    用户表:t_user

    商品表:t_goods

    订单表:t_order

    订单项表:t_order_item

    数据源表:t_dict_type

    数据项表:t_dict_data

    后续微服务秒杀项目所用:
    秒杀商品表:t_seckill_goods
    秒杀订单表:t_seckill_order

    二、构建SpringBoot项目

    起初搭建时,可以不勾选任何组件

    1.创建SpringBoot项目并配置POM

    pom依赖:

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0modelVersion>
    5. <parent>
    6. <groupId>org.springframework.bootgroupId>
    7. <artifactId>spring-boot-starter-parentartifactId>
    8. <version>2.3.9.RELEASEversion>
    9. <relativePath/>
    10. parent>
    11. <groupId>com.ycxgroupId>
    12. <artifactId>spbootproartifactId>
    13. <version>0.0.1-SNAPSHOTversion>
    14. <name>spbootproname>
    15. <description>Demo project for Spring Bootdescription>
    16. <properties>
    17. <java.version>1.8java.version>
    18. properties>
    19. <dependencies>
    20. <dependency>
    21. <groupId>org.springframework.bootgroupId>
    22. <artifactId>spring-boot-starter-freemarkerartifactId>
    23. dependency>
    24. <dependency>
    25. <groupId>org.springframework.bootgroupId>
    26. <artifactId>spring-boot-starter-webartifactId>
    27. dependency>
    28. <dependency>
    29. <groupId>mysqlgroupId>
    30. <artifactId>mysql-connector-javaartifactId>
    31. <scope>runtimescope>
    32. <version>5.1.44version>
    33. dependency>
    34. <dependency>
    35. <groupId>org.projectlombokgroupId>
    36. <artifactId>lombokartifactId>
    37. <optional>trueoptional>
    38. dependency>
    39. <dependency>
    40. <groupId>org.springframework.bootgroupId>
    41. <artifactId>spring-boot-starter-testartifactId>
    42. <scope>testscope>
    43. <exclusions>
    44. <exclusion>
    45. <groupId>org.junit.vintagegroupId>
    46. <artifactId>junit-vintage-engineartifactId>
    47. exclusion>
    48. exclusions>
    49. dependency>
    50. <dependency>
    51. <groupId>junitgroupId>
    52. <artifactId>junitartifactId>
    53. <scope>testscope>
    54. dependency>
    55. <dependency>
    56. <groupId>com.baomidougroupId>
    57. <artifactId>mybatis-plus-boot-starterartifactId>
    58. <version>3.4.0version>
    59. dependency>
    60. <dependency>
    61. <groupId>com.baomidougroupId>
    62. <artifactId>mybatis-plus-generatorartifactId>
    63. <version>3.4.0version>
    64. dependency>
    65. <dependency>
    66. <groupId>com.zaxxergroupId>
    67. <artifactId>HikariCPartifactId>
    68. dependency>
    69. <dependency>
    70. <groupId>commons-codecgroupId>
    71. <artifactId>commons-codecartifactId>
    72. dependency>
    73. <dependency>
    74. <groupId>org.apache.commonsgroupId>
    75. <artifactId>commons-lang3artifactId>
    76. <version>3.6version>
    77. dependency>
    78. <dependency>
    79. <groupId>org.springframework.bootgroupId>
    80. <artifactId>spring-boot-starter-validationartifactId>
    81. dependency>
    82. <dependency>
    83. <groupId>org.springframework.bootgroupId>
    84. <artifactId>spring-boot-starter-data-redisartifactId>
    85. dependency>
    86. <dependency>
    87. <groupId>org.apache.commonsgroupId>
    88. <artifactId>commons-pool2artifactId>
    89. dependency>
    90. <dependency>
    91. <groupId>com.alipay.sdkgroupId>
    92. <artifactId>alipay-easysdkartifactId>
    93. <version>2.0.1version>
    94. dependency>
    95. dependencies>
    96. <build>
    97. <plugins>
    98. <plugin>
    99. <groupId>org.springframework.bootgroupId>
    100. <artifactId>spring-boot-maven-pluginartifactId>
    101. plugin>
    102. plugins>
    103. build>
    104. project>

    2.配置application.yml

    1)添加数据库及连接池配置
    2)添加freemarker配置
    3)添加mybatis-plus配置
    4)添加logging日志配置

    yml文件:

    1. server:
    2. port: 8081
    3. servlet:
    4. context-path: /
    5. spring:
    6. datasource:
    7. url: jdbc:mysql://localhost:3306/spbootpro?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=UTF8
    8. driver-class-name: com.mysql.jdbc.Driver
    9. password: 1234
    10. username: root
    11. hikari:
    12. # 最小空闲连接数量
    13. minimum-idle: 5
    14. # 空闲连接存活最大时间,默认60000010分钟)
    15. idle-timeout: 180000
    16. # 连接池最大连接数,默认是10
    17. maximum-pool-size: 10
    18. # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
    19. auto-commit: true
    20. # 连接池名称
    21. pool-name: MyHikariCP
    22. # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认180000030分钟
    23. max-lifetime: 1800000
    24. # 数据库连接超时时间,默认30秒,即30000
    25. connection-timeout: 30000
    26. freemarker:
    27. #设置编码格式
    28. charset: UTF-8
    29. #后缀
    30. suffix:
    31. #文档类型
    32. content-type: text/html
    33. #模板前端
    34. template-loader-path: classpath:/templates/
    35. #启用模板
    36. enabled: true
    37. mvc:
    38. static-path-pattern: /static/**
    39. redis:
    40. #服务端IP
    41. host: 192.168.122.128
    42. #端口
    43. port: 6379
    44. #密码
    45. password: 123456
    46. #选择数据库
    47. database: 9
    48. #超时时间
    49. timeout: 10000ms
    50. #Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问
    51. #Lettuce线程安全,Jedis线程非安全
    52. lettuce:
    53. pool:
    54. #最大连接数,默认8
    55. max-active: 8
    56. #最大连接阻塞等待时间,默认-1
    57. max-wait: 10000ms
    58. #最大空闲连接,默认8
    59. max-idle: 200
    60. #最小空闲连接,默认0
    61. min-idle: 5
    62. #mybatis-plus配置
    63. mybatis-plus:
    64. #所对应的 XML 文件位置
    65. mapper-locations: classpath*:/mapper/*Mapper.xml
    66. #别名包扫描路径
    67. type-aliases-package: com.ycx.spbootpro.model
    68. configuration:
    69. #驼峰命名规则
    70. map-underscore-to-camel-case: true
    71. #日志配置
    72. logging:
    73. level:
    74. com.ycx.spbootpro.mapper: debug

    3.启动类配置

    1. package com.ycx.spbootpro;
    2. import org.mybatis.spring.annotation.MapperScan;
    3. import org.springframework.boot.SpringApplication;
    4. import org.springframework.boot.autoconfigure.SpringBootApplication;
    5. import org.springframework.transaction.annotation.EnableTransactionManagement;
    6. @MapperScan({"com.ycx.spbootpro.mapper"})
    7. @EnableTransactionManagement
    8. @SpringBootApplication
    9. public class SpbootproApplication {
    10. public static void main(String[] args) {
    11. SpringApplication.run(SpbootproApplication.class, args);
    12. }
    13. }

    4.首页访问 ,导入前端页面及页面对应的js/css/images文件

     IndexController :

    1. package com.ycx.spbootpro.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. /**
    5. * @author 杨总
    6. * @create 2022-11-04 21:02
    7. */
    8. @Controller
    9. public class IndexController {
    10. @RequestMapping("/")
    11. public String index(){
    12. // 前缀+逻辑视图名+后缀
    13. return "index.html";
    14. }
    15. }

    运行访问:

    三、实现首页功能

    0)导入帮助类

    config:

    RedisConfig :

    1. package com.ycx.spbootpro.config;
    2. import org.springframework.context.annotation.Bean;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.data.redis.connection.RedisConnectionFactory;
    5. import org.springframework.data.redis.core.RedisTemplate;
    6. import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    7. import org.springframework.data.redis.serializer.StringRedisSerializer;
    8. @Configuration
    9. public class RedisConfig {
    10. @Bean
    11. public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
    12. RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
    13. redisTemplate.setStringSerializer(new StringRedisSerializer());
    14. redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    15. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    16. redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
    17. redisTemplate.setConnectionFactory(connectionFactory);
    18. redisTemplate.afterPropertiesSet();
    19. return redisTemplate;
    20. }
    21. }

     exception:

     BusinessException :

    1. package com.ycx.spbootpro.exception;
    2. import com.ycx.spbootpro.utils.JsonResponseStatus;
    3. import lombok.AllArgsConstructor;
    4. import lombok.Data;
    5. import lombok.NoArgsConstructor;
    6. @Data
    7. @AllArgsConstructor
    8. @NoArgsConstructor
    9. public class BusinessException extends RuntimeException {
    10. private JsonResponseStatus jsonResponseStatus;
    11. }

    GlobalExceptionHandler :

    1. package com.ycx.spbootpro.exception;
    2. import com.ycx.spbootpro.utils.JsonResponseBody;
    3. import com.ycx.spbootpro.utils.JsonResponseStatus;
    4. import org.springframework.validation.BindException;
    5. import org.springframework.web.bind.annotation.ExceptionHandler;
    6. import org.springframework.web.bind.annotation.RestControllerAdvice;
    7. @RestControllerAdvice
    8. public class GlobalExceptionHandler {
    9. @ExceptionHandler
    10. public JsonResponseBody exceptionHandler(Exception e){
    11. JsonResponseBody jsonResponseBody=null;
    12. e.printStackTrace();
    13. if(e instanceof BusinessException){
    14. BusinessException ex= (BusinessException) e;
    15. jsonResponseBody=new JsonResponseBody<>(ex.getJsonResponseStatus());
    16. }else if(e instanceof BindException){
    17. BindException ex= (BindException) e;
    18. String msg = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
    19. jsonResponseBody=new JsonResponseBody<>(JsonResponseStatus.BIND_ERROR);
    20. jsonResponseBody.setMsg(msg);
    21. }else{
    22. System.out.println("aaaaaa");
    23. }
    24. return jsonResponseBody;
    25. }
    26. }

    Generator:

    CodeGenerator :

    1. package com.ycx.spbootpro.generator;
    2. import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
    3. import com.baomidou.mybatisplus.core.toolkit.StringPool;
    4. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
    5. import com.baomidou.mybatisplus.generator.AutoGenerator;
    6. import com.baomidou.mybatisplus.generator.InjectionConfig;
    7. import com.baomidou.mybatisplus.generator.config.*;
    8. import com.baomidou.mybatisplus.generator.config.po.TableInfo;
    9. import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    10. import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
    11. import java.util.ArrayList;
    12. import java.util.List;
    13. import java.util.Scanner;
    14. public class CodeGenerator {
    15. /**
    16. *

    17. * 读取控制台内容
    18. *

    19. */
    20. public static String scanner(String tip) {
    21. Scanner scanner = new Scanner(System.in);
    22. StringBuilder help = new StringBuilder();
    23. help.append("请输入" + tip + ":");
    24. System.out.println(help.toString());
    25. if (scanner.hasNext()) {
    26. String ipt = scanner.next();
    27. if (StringUtils.isNotBlank(ipt)) {
    28. return ipt;
    29. }
    30. }
    31. throw new MybatisPlusException("请输入正确的" + tip + "!");
    32. }
    33. public static void main(String[] args) {
    34. // 代码生成器
    35. AutoGenerator mpg = new AutoGenerator();
    36. // 全局配置
    37. GlobalConfig gc = new GlobalConfig();
    38. String projectPath = System.getProperty("user.dir") + "/spbootpro";
    39. gc.setOutputDir(projectPath + "/src/main/java");
    40. gc.setAuthor("yangzong");
    41. gc.setOpen(false);
    42. gc.setBaseColumnList(true);
    43. gc.setBaseResultMap(true);
    44. // gc.setSwagger2(true); 实体属性 Swagger2 注解
    45. mpg.setGlobalConfig(gc);
    46. // 数据源配置
    47. DataSourceConfig dsc = new DataSourceConfig();
    48. dsc.setUrl("jdbc:mysql://localhost:3306/spbootpro?useUnicode=true&useSSL=false&characterEncoding=utf8");
    49. // dsc.setSchemaName("public");
    50. dsc.setDriverName("com.mysql.jdbc.Driver");
    51. dsc.setUsername("root");
    52. dsc.setPassword("1234");
    53. mpg.setDataSource(dsc);
    54. // 包配置
    55. PackageConfig pc = new PackageConfig();
    56. //pc.setModuleName(scanner("模块名"));
    57. pc.setParent("com.ycx.spbootpro");
    58. //设置包名
    59. pc.setEntity("model");
    60. mpg.setPackageInfo(pc);
    61. // 自定义配置
    62. InjectionConfig cfg = new InjectionConfig() {
    63. @Override
    64. public void initMap() {
    65. // to do nothing
    66. }
    67. };
    68. // 如果模板引擎是 freemarker
    69. String templatePath = "/templates/mybatis-generator/mapper2.xml.ftl";
    70. // 如果模板引擎是 velocity
    71. // String templatePath = "/templates/mapper.xml.vm";
    72. // 自定义输出配置
    73. List<FileOutConfig> focList = new ArrayList<>();
    74. // 自定义配置会被优先输出
    75. focList.add(new FileOutConfig(templatePath) {
    76. @Override
    77. public String outputFile(TableInfo tableInfo) {
    78. // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
    79. return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
    80. + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
    81. }
    82. });
    83. /*
    84. cfg.setFileCreate(new IFileCreate() {
    85. @Override
    86. public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
    87. // 判断自定义文件夹是否需要创建
    88. checkDir("调用默认方法创建的目录,自定义目录用");
    89. if (fileType == FileType.MAPPER) {
    90. // 已经生成 mapper 文件判断存在,不想重新生成返回 false
    91. return !new File(filePath).exists();
    92. }
    93. // 允许生成模板文件
    94. return true;
    95. }
    96. });
    97. */
    98. cfg.setFileOutConfigList(focList);
    99. mpg.setCfg(cfg);
    100. // 配置模板
    101. TemplateConfig templateConfig = new TemplateConfig();
    102. // 配置自定义输出模板
    103. //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
    104. templateConfig.setMapper("templates/mybatis-generator/mapper2.java");
    105. templateConfig.setEntity("templates/mybatis-generator/entity2.java");
    106. templateConfig.setService("templates/mybatis-generator/service2.java");
    107. templateConfig.setServiceImpl("templates/mybatis-generator/serviceImpl2.java");
    108. templateConfig.setController("templates/mybatis-generator/controller2.java");
    109. templateConfig.setXml(null);
    110. mpg.setTemplate(templateConfig);
    111. // 策略配置
    112. StrategyConfig strategy = new StrategyConfig();
    113. strategy.setNaming(NamingStrategy.underline_to_camel);
    114. strategy.setColumnNaming(NamingStrategy.underline_to_camel);
    115. //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
    116. strategy.setEntityLombokModel(true);
    117. strategy.setRestControllerStyle(true);
    118. strategy.setEntitySerialVersionUID(false);
    119. // 公共父类
    120. //strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
    121. // 写于父类中的公共字段
    122. strategy.setSuperEntityColumns("id");
    123. strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
    124. strategy.setControllerMappingHyphenStyle(true);
    125. strategy.setTablePrefix("t_");
    126. mpg.setStrategy(strategy);
    127. mpg.setTemplateEngine(new FreemarkerTemplateEngine());
    128. mpg.execute();
    129. }
    130. }

    Utils:

    CookieUtils :

    1. package com.ycx.spbootpro.utils;
    2. import lombok.extern.slf4j.Slf4j;
    3. import javax.servlet.http.Cookie;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.io.UnsupportedEncodingException;
    7. import java.net.URLDecoder;
    8. import java.net.URLEncoder;
    9. @Slf4j
    10. public class CookieUtils {
    11. /**
    12. *
    13. * @Description: 得到Cookie的值, 不编码
    14. * @param request
    15. * @param cookieName
    16. * @return
    17. */
    18. public static String getCookieValue(HttpServletRequest request, String cookieName) {
    19. return getCookieValue(request, cookieName, false);
    20. }
    21. /**
    22. *
    23. * @Description: 得到Cookie的值
    24. * @param request
    25. * @param cookieName
    26. * @param isDecoder
    27. * @return
    28. */
    29. public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
    30. Cookie[] cookieList = request.getCookies();
    31. if (cookieList == null || cookieName == null) {
    32. return null;
    33. }
    34. String retValue = null;
    35. try {
    36. for (int i = 0; i < cookieList.length; i++) {
    37. if (cookieList[i].getName().equals(cookieName)) {
    38. if (isDecoder) {
    39. retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
    40. } else {
    41. retValue = cookieList[i].getValue();
    42. }
    43. break;
    44. }
    45. }
    46. } catch (UnsupportedEncodingException e) {
    47. e.printStackTrace();
    48. }
    49. return retValue;
    50. }
    51. /**
    52. *
    53. * @Description: 得到Cookie的值
    54. * @param request
    55. * @param cookieName
    56. * @param encodeString
    57. * @return
    58. */
    59. public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
    60. Cookie[] cookieList = request.getCookies();
    61. if (cookieList == null || cookieName == null) {
    62. return null;
    63. }
    64. String retValue = null;
    65. try {
    66. for (int i = 0; i < cookieList.length; i++) {
    67. if (cookieList[i].getName().equals(cookieName)) {
    68. retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
    69. break;
    70. }
    71. }
    72. } catch (UnsupportedEncodingException e) {
    73. e.printStackTrace();
    74. }
    75. return retValue;
    76. }
    77. /**
    78. *
    79. * @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
    80. * @param request
    81. * @param response
    82. * @param cookieName
    83. * @param cookieValue
    84. */
    85. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
    86. String cookieValue) {
    87. setCookie(request, response, cookieName, cookieValue, -1);
    88. }
    89. /**
    90. *
    91. * @Description: 设置Cookie的值 在指定时间内生效,但不编码
    92. * @param request
    93. * @param response
    94. * @param cookieName
    95. * @param cookieValue
    96. * @param cookieMaxage
    97. */
    98. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
    99. String cookieValue, int cookieMaxage) {
    100. setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
    101. }
    102. /**
    103. *
    104. * @Description: 设置Cookie的值 不设置生效时间,但编码
    105. * 在服务器被创建,返回给客户端,并且保存客户端
    106. * 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中
    107. * 如果没有设置,会默认把cookie保存在浏览器的内存中
    108. * 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息
    109. * @param request
    110. * @param response
    111. * @param cookieName
    112. * @param cookieValue
    113. * @param isEncode
    114. */
    115. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
    116. String cookieValue, boolean isEncode) {
    117. setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    118. }
    119. /**
    120. *
    121. * @Description: 设置Cookie的值 在指定时间内生效, 编码参数
    122. * @param request
    123. * @param response
    124. * @param cookieName
    125. * @param cookieValue
    126. * @param cookieMaxage
    127. * @param isEncode
    128. */
    129. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
    130. String cookieValue, int cookieMaxage, boolean isEncode) {
    131. doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
    132. }
    133. /**
    134. *
    135. * @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
    136. * @param request
    137. * @param response
    138. * @param cookieName
    139. * @param cookieValue
    140. * @param cookieMaxage
    141. * @param encodeString
    142. */
    143. public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
    144. String cookieValue, int cookieMaxage, String encodeString) {
    145. doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    146. }
    147. /**
    148. *
    149. * @Description: 删除Cookie带cookie域名
    150. * @param request
    151. * @param response
    152. * @param cookieName
    153. */
    154. public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
    155. String cookieName) {
    156. doSetCookie(request, response, cookieName, null, -1, false);
    157. }
    158. /**
    159. *
    160. * @Description: 设置Cookie的值,并使其在指定时间内生效
    161. * @param request
    162. * @param response
    163. * @param cookieName
    164. * @param cookieValue
    165. * @param cookieMaxage cookie生效的最大秒数
    166. * @param isEncode
    167. */
    168. private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
    169. String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
    170. try {
    171. if (cookieValue == null) {
    172. cookieValue = "";
    173. } else if (isEncode) {
    174. cookieValue = URLEncoder.encode(cookieValue, "utf-8");
    175. }
    176. Cookie cookie = new Cookie(cookieName, cookieValue);
    177. if (cookieMaxage > 0)
    178. cookie.setMaxAge(cookieMaxage);
    179. if (null != request) {// 设置域名的cookie
    180. String domainName = getDomainName(request);
    181. log.info("========== domainName: {} ==========", domainName);
    182. if (!"localhost".equals(domainName)) {
    183. cookie.setDomain(domainName);
    184. }
    185. }
    186. cookie.setPath("/");
    187. response.addCookie(cookie);
    188. } catch (Exception e) {
    189. e.printStackTrace();
    190. }
    191. }
    192. /**
    193. *
    194. * @Description: 设置Cookie的值,并使其在指定时间内生效
    195. * @param request
    196. * @param response
    197. * @param cookieName
    198. * @param cookieValue
    199. * @param cookieMaxage cookie生效的最大秒数
    200. * @param encodeString
    201. */
    202. private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
    203. String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
    204. try {
    205. if (cookieValue == null) {
    206. cookieValue = "";
    207. } else {
    208. cookieValue = URLEncoder.encode(cookieValue, encodeString);
    209. }
    210. Cookie cookie = new Cookie(cookieName, cookieValue);
    211. if (cookieMaxage > 0)
    212. cookie.setMaxAge(cookieMaxage);
    213. if (null != request) {// 设置域名的cookie
    214. String domainName = getDomainName(request);
    215. log.info("========== domainName: {} ==========", domainName);
    216. if (!"localhost".equals(domainName)) {
    217. cookie.setDomain(domainName);
    218. }
    219. }
    220. cookie.setPath("/");
    221. response.addCookie(cookie);
    222. } catch (Exception e) {
    223. e.printStackTrace();
    224. }
    225. }
    226. /**
    227. *
    228. * @Description: 得到cookie的域名
    229. * @return
    230. */
    231. private static final String getDomainName(HttpServletRequest request) {
    232. String domainName = null;
    233. String serverName = request.getRequestURL().toString();
    234. if (serverName == null || serverName.equals("")) {
    235. domainName = "";
    236. } else {
    237. serverName = serverName.toLowerCase();
    238. serverName = serverName.substring(7);
    239. final int end = serverName.indexOf("/");
    240. serverName = serverName.substring(0, end);
    241. if (serverName.indexOf(":") > 0) {
    242. String[] ary = serverName.split("\\:");
    243. serverName = ary[0];
    244. }
    245. final String[] domains = serverName.split("\\.");
    246. int len = domains.length;
    247. if (len > 3 && !isIp(serverName)) {
    248. // www.xxx.com.cn
    249. domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
    250. } else if (len <= 3 && len > 1) {
    251. // xxx.com or xxx.cn
    252. domainName = "." + domains[len - 2] + "." + domains[len - 1];
    253. } else {
    254. domainName = serverName;
    255. }
    256. }
    257. return domainName;
    258. }
    259. public static String trimSpaces(String IP){//去掉IP字符串前后所有的空格
    260. while(IP.startsWith(" ")){
    261. IP= IP.substring(1,IP.length()).trim();
    262. }
    263. while(IP.endsWith(" ")){
    264. IP= IP.substring(0,IP.length()-1).trim();
    265. }
    266. return IP;
    267. }
    268. public static boolean isIp(String IP){//判断是否是一个IP
    269. boolean b = false;
    270. IP = trimSpaces(IP);
    271. if(IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){
    272. String s[] = IP.split("\\.");
    273. if(Integer.parseInt(s[0])<255)
    274. if(Integer.parseInt(s[1])<255)
    275. if(Integer.parseInt(s[2])<255)
    276. if(Integer.parseInt(s[3])<255)
    277. b = true;
    278. }
    279. return b;
    280. }
    281. }

    DataUtils:

    1. package com.ycx.spbootpro.utils;
    2. import java.util.ArrayList;
    3. import java.util.HashMap;
    4. import java.util.List;
    5. import java.util.Map;
    6. public class DataUtils {
    7. /**
    8. * 转换方法,基于商品的排版情况(一行三列、一行四列等等)进行数据分行处理
    9. * @param cols 一行显示几列
    10. * @param goods 需要筛选的数据集
    11. * @return
    12. */
    13. public Map<String, List> transfor(int cols, List goods){
    14. Map<String,List> data=new HashMap<>();
    15. List rs=new ArrayList<>();
    16. int len=goods.size();
    17. for (int i = 0; i < len; i++) {
    18. rs.add(goods.get(i));
    19. if((i+1)%cols==0){
    20. data.put("goods"+(i+1),rs);
    21. if(i==len-1)
    22. break;
    23. rs=new ArrayList<>();
    24. continue;
    25. }
    26. if(i==len-1){
    27. data.put("goods"+(i+1),rs);
    28. }
    29. }
    30. return data;
    31. }
    32. }

    JsonResponseBody:

    1. package com.ycx.spbootpro.utils;
    2. import lombok.Data;
    3. import java.io.Serializable;
    4. /**
    5. * 响应封装类
    6. */
    7. @Data
    8. public class JsonResponseBody implements Serializable {
    9. private String msg="OK";
    10. private T data;
    11. private Integer code;
    12. private Integer total;
    13. public JsonResponseBody(){
    14. this.data=null;
    15. this.code=JsonResponseStatus.SUCCESS.getCode();
    16. }
    17. public JsonResponseBody(T data){
    18. this.data=data;
    19. this.code=JsonResponseStatus.SUCCESS.getCode();
    20. }
    21. public JsonResponseBody(T data,Integer total){
    22. this.data=data;
    23. this.total=total;
    24. this.code=JsonResponseStatus.SUCCESS.getCode();
    25. }
    26. public JsonResponseBody(JsonResponseStatus jsonResponseStatus){
    27. this.msg=jsonResponseStatus.getMsg();
    28. this.code=jsonResponseStatus.getCode();
    29. }
    30. public JsonResponseBody(JsonResponseStatus jsonResponseStatus,T data){
    31. this.data=data;
    32. this.msg=jsonResponseStatus.getMsg();
    33. this.code=jsonResponseStatus.getCode();
    34. }
    35. }

     JsonResponseStatus :

    1. package com.ycx.spbootpro.utils;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Getter;
    4. import lombok.ToString;
    5. @Getter
    6. @ToString
    7. @AllArgsConstructor
    8. public enum JsonResponseStatus {
    9. SUCCESS(200,"OK"),
    10. ERROR(500,"内部错误"),
    11. USER_LOGIN_ERROR(100101,"用户名或者密码错误"),
    12. USER_MOBILE_ERROR(100102,"手机号码格式错误"),
    13. USER_PASSWORD_ERROR(100103,"用户密码错误"),
    14. USER_USERNAME_ERROR(100104,"账号不存在"),
    15. BIND_ERROR(200101,"参数校验异常"),
    16. TOKEN_EEROR(200102,"token令牌失效或已过期")
    17. ;
    18. private final Integer code;
    19. private final String msg;
    20. }

    MD5Utils :

    1. package com.ycx.spbootpro.utils;
    2. import org.apache.commons.codec.digest.DigestUtils;
    3. import org.springframework.stereotype.Component;
    4. import java.util.UUID;
    5. /**
    6. * MD5加密
    7. * 用户端:password=MD5(明文+固定Salt)
    8. * 服务端:password=MD5(用户输入+随机Salt)
    9. * 用户端MD5加密是为了防止用户密码在网络中明文传输,服务端MD5加密是为了提高密码安全性,双重保险。
    10. */
    11. @Component
    12. public class MD5Utils {
    13. //加密盐,与前端一致
    14. private static String salt="f1g2h3j4";
    15. /**
    16. * md5加密
    17. * @param src
    18. * @return
    19. */
    20. public static String md5(String src){
    21. return DigestUtils.md5Hex(src);
    22. }
    23. /**
    24. * 获取加密的盐
    25. * @return
    26. */
    27. public static String createSalt(){
    28. return UUID.randomUUID().toString().replace("-","");
    29. }
    30. /**
    31. * 将前端的明文密码通过MD5加密方式加密成后端服务所需密码
    32. * 注意:该步骤实际是在前端完成!!!
    33. * @param inputPass 明文密码
    34. * @return
    35. */
    36. public static String inputPassToFormpass(String inputPass){
    37. //混淆固定盐salt,安全性更可靠
    38. String str=salt.charAt(1)+""+salt.charAt(5)+inputPass+salt.charAt(0)+""+salt.charAt(3);
    39. return md5(str);
    40. }
    41. /**
    42. * 将后端密文密码+随机salt生成数据库的密码
    43. * @param formPass
    44. * @param salt
    45. * @return
    46. */
    47. public static String formPassToDbPass(String formPass,String salt){
    48. //混淆固定盐salt,安全性更可靠
    49. String str=salt.charAt(7)+""+salt.charAt(9)+formPass+salt.charAt(1)+""+salt.charAt(5);
    50. return md5(str);
    51. }
    52. /**
    53. * 将用户输入的密码转换成数据库的密码
    54. * @param inputPass 明文密码
    55. * @param salt 盐
    56. * @return
    57. */
    58. public static String inputPassToDbPass(String inputPass,String salt){
    59. String formPass = inputPassToFormpass(inputPass);
    60. String dbPass = formPassToDbPass(formPass, salt);
    61. return dbPass;
    62. }
    63. public static void main(String[] args) {
    64. //d7aaa28e3b8e6c88352bd5e7c23829f9
    65. //5512a78a188b318c074a15f9b056a712
    66. String formPass = inputPassToFormpass("123456");
    67. System.out.println("前端加密密码:"+formPass);
    68. String salt = createSalt();
    69. System.out.println("后端加密随机盐:"+salt);
    70. String dbPass = formPassToDbPass(formPass, salt);
    71. System.out.println("后端加密密码:"+dbPass);
    72. System.out.println("-------------------------------------------");
    73. String dbPass1 = inputPassToDbPass("123456", salt);
    74. System.out.println("最终加密密码:"+dbPass1);
    75. }
    76. }

    MybatisPlusConfig :

    1. package com.ycx.spbootpro.utils;
    2. import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
    3. import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
    4. import org.mybatis.spring.annotation.MapperScan;
    5. import org.springframework.context.annotation.Bean;
    6. import org.springframework.context.annotation.Configuration;
    7. import org.springframework.transaction.annotation.EnableTransactionManagement;
    8. //Spring boot方式
    9. @EnableTransactionManagement
    10. @Configuration
    11. @MapperScan("com.zking.shoppingpro.service.*.mapper*")
    12. public class MybatisPlusConfig {
    13. @Bean
    14. public PaginationInterceptor paginationInterceptor() {
    15. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    16. // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
    17. // paginationInterceptor.setOverflow(false);
    18. // 设置最大单页限制数量,默认 500 条,-1 不受限制
    19. // paginationInterceptor.setLimit(500);
    20. // 开启 count 的 join 优化,只针对部分 left join
    21. paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
    22. return paginationInterceptor;
    23. }
    24. }

    PageBean : 

    1. package com.ycx.spbootpro.utils;
    2. import javax.servlet.http.HttpServletRequest;
    3. import java.io.Serializable;
    4. import java.util.Map;
    5. public class PageBean implements Serializable {
    6. //页码
    7. private int page=1;
    8. //每页显示条数
    9. private int rows=10;
    10. //总记录数
    11. private int total=0;
    12. //是否分页标记
    13. private boolean pagination=true;
    14. //上一次请求的路径
    15. private String url;
    16. //请求参数Map集合
    17. private Map<String,String[]> map;
    18. public String getUrl() {
    19. return url;
    20. }
    21. public void setUrl(String url) {
    22. this.url = url;
    23. }
    24. public Map<String, String[]> getMap() {
    25. return map;
    26. }
    27. public void setMap(Map<String, String[]> map) {
    28. this.map = map;
    29. }
    30. public int getPage() {
    31. return page;
    32. }
    33. public void setPage(int page) {
    34. this.page = page;
    35. }
    36. public int getRows() {
    37. return rows;
    38. }
    39. public void setRows(int rows) {
    40. this.rows = rows;
    41. }
    42. public int getTotal() {
    43. return total;
    44. }
    45. public void setTotal(int total) {
    46. this.total = total;
    47. }
    48. public boolean isPagination() {
    49. return pagination;
    50. }
    51. public void setPagination(boolean pagination) {
    52. this.pagination = pagination;
    53. }
    54. //重载setPage/setRows/setPagination方法
    55. public void setPage(String page) {
    56. if(null!=page&&!"".equals(page))
    57. this.page=Integer.parseInt(page);
    58. }
    59. public void setRows(String rows) {
    60. if(null!=rows&&!"".equals(rows))
    61. this.rows=Integer.parseInt(rows);
    62. }
    63. public void setPagination(String pagination) {
    64. if(null!=pagination&&!"".equals(pagination))
    65. this.pagination=Boolean.parseBoolean(pagination);
    66. }
    67. public void setRequest(HttpServletRequest req) {
    68. //获取前端提交的page/rows/pagination参数
    69. String page=req.getParameter("page");
    70. String rows=req.getParameter("rows");
    71. String pagination=req.getParameter("pagination");
    72. //设置属性
    73. this.setPage(page);
    74. this.setPagination(pagination);
    75. this.setRows(rows);
    76. //设置上一次请求的路径
    77. //第一次请求:
    78. //http://localhost:8080/项目名/请求路径.action?page=1
    79. //第二次请求:下一页 page=2
    80. //项目名+请求路径.action
    81. //req.getContextPath()+req.getServletPath()==req.getRequestURI()
    82. this.url=req.getRequestURI();
    83. //获取请求参数集合
    84. // checkbox name='hobby'
    85. // Map hobby==key value==new String[]{"篮球","足球",..}
    86. this.map=req.getParameterMap();
    87. }
    88. /**
    89. * 获取分页的起始位置
    90. * @return
    91. */
    92. public int getStartIndex() {
    93. //第1页:(1-1)*10 ==0 limit 0,10
    94. //第2页:(2-1)*10 ==10 limit 10,10
    95. //..
    96. return (this.page-1)*this.rows;
    97. }
    98. //获取末页、上一页、下一页
    99. /**
    100. * 获取末页
    101. * @return
    102. */
    103. public int getMaxPager() {
    104. int maxPager=this.total/this.rows;
    105. if(this.total%this.rows!=0)
    106. maxPager++;
    107. return maxPager;
    108. }
    109. /**
    110. * 获取上一页
    111. * @return
    112. */
    113. public int getPreviousPager() {
    114. int previousPager=this.page-1;
    115. if(previousPager<=1)
    116. previousPager=1;
    117. return previousPager;
    118. }
    119. /**
    120. * 获取下一页
    121. * @return
    122. */
    123. public int getNextPager() {
    124. int nextPager=this.page+1;
    125. if(nextPager>getMaxPager())
    126. nextPager=getMaxPager();
    127. return nextPager;
    128. }
    129. @Override
    130. public String toString() {
    131. return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pagination=" + pagination
    132. + ", url=" + url + ", map=" + map + "]";
    133. }
    134. }

    ValidatorUtils :

    1. package com.ycx.spbootpro.utils;
    2. import org.apache.commons.lang3.StringUtils;
    3. import java.util.regex.Matcher;
    4. import java.util.regex.Pattern;
    5. /**
    6. * 正则校验工具类
    7. * @author 刘开宇
    8. */
    9. public class ValidatorUtils {
    10. private static final Pattern mobile_pattern=Pattern.compile("[1]([0-9])[0-9]{9}$");
    11. public static boolean isMobile(String mobile){
    12. if(StringUtils.isEmpty(mobile))
    13. return false;
    14. Matcher matcher = mobile_pattern.matcher(mobile);
    15. return matcher.matches();
    16. }
    17. }

     在generater类运行自动生成代码:

    在控制台输入表名:

    生成成功:

    1)创建公共跳转控制器PageController

    1. package com.ycx.spbootpro.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.web.bind.annotation.PathVariable;
    4. import org.springframework.web.bind.annotation.RequestMapping;
    5. /**
    6. * @author 杨总
    7. * @create 2022-11-05 17:20
    8. */
    9. @Controller
    10. public class PageController {
    11. /**
    12. * 直接跳转页面(没有层级文件夹的情况)
    13. * 列如:
    14. * http://localhost:8081/page/paint.html
    15. * http://localhost:8081/page/perfume.html
    16. *
    17. * @return
    18. */
    19. @RequestMapping("/page/{page}")
    20. public String page(@PathVariable(value = "page") String page) {
    21. return page;
    22. }
    23. /**
    24. * 直接跳转页面(存在层级文件夹的情况)
    25. *
    26. * @return
    27. */
    28. @RequestMapping("/page/{dir}/{page}")
    29. public String dir(@PathVariable(value = "dir") String dir,
    30. @PathVariable(value = "page") String page) {
    31. return dir + "/" + page;
    32. }
    33. }

    2)使用Mybatis-plus反向生成代码(Goods商品信息)

    1. package com.ycx.spbootpro.generator;
    2. import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
    3. import com.baomidou.mybatisplus.core.toolkit.StringPool;
    4. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
    5. import com.baomidou.mybatisplus.generator.AutoGenerator;
    6. import com.baomidou.mybatisplus.generator.InjectionConfig;
    7. import com.baomidou.mybatisplus.generator.config.*;
    8. import com.baomidou.mybatisplus.generator.config.po.TableInfo;
    9. import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    10. import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
    11. import java.util.ArrayList;
    12. import java.util.List;
    13. import java.util.Scanner;
    14. public class CodeGenerator {
    15. /**
    16. *

    17. * 读取控制台内容
    18. *

    19. */
    20. public static String scanner(String tip) {
    21. Scanner scanner = new Scanner(System.in);
    22. StringBuilder help = new StringBuilder();
    23. help.append("请输入" + tip + ":");
    24. System.out.println(help.toString());
    25. if (scanner.hasNext()) {
    26. String ipt = scanner.next();
    27. if (StringUtils.isNotBlank(ipt)) {
    28. return ipt;
    29. }
    30. }
    31. throw new MybatisPlusException("请输入正确的" + tip + "!");
    32. }
    33. public static void main(String[] args) {
    34. // 代码生成器
    35. AutoGenerator mpg = new AutoGenerator();
    36. // 全局配置
    37. GlobalConfig gc = new GlobalConfig();
    38. String projectPath = System.getProperty("user.dir") + "/spbootpro";
    39. gc.setOutputDir(projectPath + "/src/main/java");
    40. gc.setAuthor("yangzong");
    41. gc.setOpen(false);
    42. gc.setBaseColumnList(true);
    43. gc.setBaseResultMap(true);
    44. // gc.setSwagger2(true); 实体属性 Swagger2 注解
    45. mpg.setGlobalConfig(gc);
    46. // 数据源配置
    47. DataSourceConfig dsc = new DataSourceConfig();
    48. dsc.setUrl("jdbc:mysql://localhost:3306/spbootpro?useUnicode=true&useSSL=false&characterEncoding=utf8");
    49. // dsc.setSchemaName("public");
    50. dsc.setDriverName("com.mysql.jdbc.Driver");
    51. dsc.setUsername("root");
    52. dsc.setPassword("1234");
    53. mpg.setDataSource(dsc);
    54. // 包配置
    55. PackageConfig pc = new PackageConfig();
    56. //pc.setModuleName(scanner("模块名"));
    57. pc.setParent("com.ycx.spbootpro");
    58. //设置包名
    59. pc.setEntity("model");
    60. mpg.setPackageInfo(pc);
    61. // 自定义配置
    62. InjectionConfig cfg = new InjectionConfig() {
    63. @Override
    64. public void initMap() {
    65. // to do nothing
    66. }
    67. };
    68. // 如果模板引擎是 freemarker
    69. String templatePath = "/templates/mybatis-generator/mapper2.xml.ftl";
    70. // 如果模板引擎是 velocity
    71. // String templatePath = "/templates/mapper.xml.vm";
    72. // 自定义输出配置
    73. List<FileOutConfig> focList = new ArrayList<>();
    74. // 自定义配置会被优先输出
    75. focList.add(new FileOutConfig(templatePath) {
    76. @Override
    77. public String outputFile(TableInfo tableInfo) {
    78. // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
    79. return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
    80. + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
    81. }
    82. });
    83. /*
    84. cfg.setFileCreate(new IFileCreate() {
    85. @Override
    86. public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
    87. // 判断自定义文件夹是否需要创建
    88. checkDir("调用默认方法创建的目录,自定义目录用");
    89. if (fileType == FileType.MAPPER) {
    90. // 已经生成 mapper 文件判断存在,不想重新生成返回 false
    91. return !new File(filePath).exists();
    92. }
    93. // 允许生成模板文件
    94. return true;
    95. }
    96. });
    97. */
    98. cfg.setFileOutConfigList(focList);
    99. mpg.setCfg(cfg);
    100. // 配置模板
    101. TemplateConfig templateConfig = new TemplateConfig();
    102. // 配置自定义输出模板
    103. //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
    104. templateConfig.setMapper("templates/mybatis-generator/mapper2.java");
    105. templateConfig.setEntity("templates/mybatis-generator/entity2.java");
    106. templateConfig.setService("templates/mybatis-generator/service2.java");
    107. templateConfig.setServiceImpl("templates/mybatis-generator/serviceImpl2.java");
    108. templateConfig.setController("templates/mybatis-generator/controller2.java");
    109. templateConfig.setXml(null);
    110. mpg.setTemplate(templateConfig);
    111. // 策略配置
    112. StrategyConfig strategy = new StrategyConfig();
    113. strategy.setNaming(NamingStrategy.underline_to_camel);
    114. strategy.setColumnNaming(NamingStrategy.underline_to_camel);
    115. //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
    116. strategy.setEntityLombokModel(true);
    117. strategy.setRestControllerStyle(true);
    118. strategy.setEntitySerialVersionUID(false);
    119. // 公共父类
    120. //strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
    121. // 写于父类中的公共字段
    122. strategy.setSuperEntityColumns("id");
    123. strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
    124. strategy.setControllerMappingHyphenStyle(true);
    125. strategy.setTablePrefix("t_");
    126. mpg.setStrategy(strategy);
    127. mpg.setTemplateEngine(new FreemarkerTemplateEngine());
    128. mpg.execute();
    129. }
    130. }

    3)创建IndexController并定义商品查询请求处理方法

    1. package com.ycx.spbootpro.controller;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.ycx.spbootpro.model.Goods;
    4. import com.ycx.spbootpro.service.IGoodsService;
    5. import com.ycx.spbootpro.utils.DataUtils;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.stereotype.Controller;
    8. import org.springframework.ui.Model;
    9. import org.springframework.web.bind.annotation.RequestMapping;
    10. import java.util.List;
    11. import java.util.Map;
    12. /**
    13. * @author 杨总
    14. * @create 2022-11-04 21:02
    15. */
    16. @Controller
    17. public class IndexController {
    18. @Autowired
    19. private IGoodsService goodsService;
    20. @RequestMapping("/")
    21. public String index(Model model){
    22. // 摆件花艺
    23. List<Goods> goods01 = goodsService.list(new QueryWrapper<Goods>()
    24. .eq("goods_type", "01")
    25. .last("limit 6"));
    26. // 壁挂北欧
    27. List<Goods> goods07 = goodsService.list(new QueryWrapper<Goods>()
    28. .eq("goods_type", "07")
    29. .last("limit 12"));
    30. // 为了方便首页数据显示,方便摆放
    31. DataUtils<Goods> dataUtils = new DataUtils<>();
    32. Map<String, List<Goods>> gt01 = dataUtils.transfor(3, goods01);
    33. Map<String, List<Goods>> gt07 = dataUtils.transfor(4, goods07);
    34. model.addAttribute("gt01",gt01);
    35. model.addAttribute("gt07",gt07);
    36. return "index.html";
    37. }
    38. }

    首页数据绑定语法:

    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>

     index.html:

    1. html>
    2. <html>
    3. <head lang="en">
    4. <#include "common/head.html" />
    5. <link rel="stylesheet" type="text/css" href="css/public.css"/>
    6. <link rel="stylesheet" type="text/css" href="css/index.css" />
    7. head>
    8. <div>
    9. <#include "common/top.html" />
    10. <div class="block_home_slider">
    11. <div id="home_slider" class="flexslider">
    12. <ul class="slides">
    13. <li>
    14. <div class="slide">
    15. <img src="img/banner2.jpg"/>
    16. div>
    17. li>
    18. <li>
    19. <div class="slide">
    20. <img src="img/banner1.jpg"/>
    21. div>
    22. li>
    23. ul>
    24. div>
    25. div>
    26. <div class="thImg">
    27. <div class="clearfix">
    28. <a href="${ctx}/page/vase_proList.html"><img src="img/i1.jpg"/>a>
    29. <a href="${ctx}/page/proList.html"><img src="img/i2.jpg"/>a>
    30. <a href="#2"><img src="img/i3.jpg"/>a>
    31. div>
    32. div>
    33. <div class="news">
    34. <div class="wrapper">
    35. <h2><img src="img/ih1.jpg"/>h2>
    36. <div class="top clearfix">
    37. <a href="${ctx}/page/proDetail.html"><img src="img/n1.jpg"/><p>p>a>
    38. <a href="${ctx}/page/proDetail.html"><img src="img/n2.jpg"/><p>p>a>
    39. <a href="${ctx}/page/proDetail.html"><img src="img/n3.jpg"/><p>p>a>
    40. div>
    41. <div class="bott clearfix">
    42. <a href="${ctx}/page/proDetail.html"><img src="img/n4.jpg"/><p>p>a>
    43. <a href="${ctx}/page/proDetail.html"><img src="img/n5.jpg"/><p>p>a>
    44. <a href="${ctx}/page/proDetail.html"><img src="img/n6.jpg"/><p>p>a>
    45. div>
    46. <h2><img src="img/ih2.jpg"/>h2>
    47. <#if gt01?? && gt01?size gt 0>
    48. <#list gt01?keys as key>
    49. <div class="flower clearfix tran">
    50. <#list gt01[key] as g>
    51. <a href="proDetail.html" class="clearfix">
    52. <dl>
    53. <dt>
    54. <span class="abl">span>
    55. <img src="${g.goodsImg}"/>
    56. <span class="abr">span>
    57. dt>
    58. <dd>${g.goodsName}dd>
    59. <dd><span>¥ ${g.goodsPrice}span>dd>
    60. dl>
    61. a>
    62. div>
    63. div>
    64. div>
    65. <a href="#" class="ad"><img src="img/ib1.jpg"/>a>
    66. <div class="people">
    67. <div class="wrapper">
    68. <h2><img src="img/ih3.jpg"/>h2>
    69. <#if gt07?? && gt07?size gt 0>
    70. <#list gt07?keys as key>
    71. <div class="pList clearfix tran">
    72. <#list gt07[key] as g>
    73. <a href="proDetail.html">
    74. <dl>
    75. <dt>
    76. <span class="abl">span>
    77. <img src="${g.goodsImg}"/>
    78. <span class="abr">span>
    79. dt>
    80. <dd>${g.goodsName}dd>
    81. <dd><span>¥ ${g.goodsPrice}span>dd>
    82. dl>
    83. a>
    84. div>
    85. >
    86. div>
    87. div>
    88. <#include "common/footer.html"/>
    89. <script src="js/public.js" type="text/javascript" charset="utf-8">script>
    90. <script src="js/nav.js" type="text/javascript" charset="utf-8">script>
    91. <script src="js/jquery.flexslider-min.js" type="text/javascript" charset="utf-8">script>
    92. <script type="text/javascript">
    93. $(function() {
    94. $('#home_slider').flexslider({
    95. animation: 'slide',
    96. controlNav: true,
    97. directionNav: true,
    98. animationLoop: true,
    99. slideshow: true,
    100. slideshowSpeed:2000,
    101. useCSS: false
    102. });
    103. });
    104. script>
    105. body>
    106. html>

    运行: 

     

    四、用户明文登录

    创建UserController类实现用户登录

            1.1)构建UserDto,定义mobile和password属性

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

    1. package com.ycx.spbootpro.model.dto;
    2. import com.ycx.spbootpro.validator.IsMobile;
    3. import lombok.Data;
    4. import javax.validation.constraints.NotBlank;
    5. @Data
    6. public class UserDto {
    7. @NotBlank(message = "手机号码不能为空!")
    8. @IsMobile
    9. private String mobile;
    10. @NotBlank(message = "密码不能为空!")
    11. private String password;
    12. }

            


            1.2)创建UserController类

    处理浏览器端的请求 UserController.java

    1. package com.ycx.spbootpro.controller;
    2. import com.ycx.spbootpro.model.dto.UserDto;
    3. import com.ycx.spbootpro.service.IUserService;
    4. import com.ycx.spbootpro.utils.JsonResponseBody;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.web.bind.annotation.RequestMapping;
    7. import org.springframework.web.bind.annotation.RestController;
    8. import javax.servlet.http.HttpServletRequest;
    9. import javax.servlet.http.HttpServletResponse;
    10. import javax.validation.Valid;
    11. /**
    12. *

    13. * 用户信息表 前端控制器
    14. *

    15. *
    16. * @author yangzong
    17. * @since 2022-11-05
    18. */
    19. @RestController
    20. @RequestMapping("/user")
    21. public class UserController {
    22. @Autowired
    23. private IUserService userService;
    24. @RequestMapping("/toLogin")
    25. public JsonResponseBody toLogin(@Valid UserDto userDto,
    26. HttpServletRequest req,
    27. HttpServletResponse resp){
    28. return userService.toLogin(userDto,req,resp);
    29. }
    30. }


            1.3)定义userLogin(UserDto userDto,HttpServletRequest req,HttpServletResponse resp)


            1.4)定义响应封装类JsonResponseBody和JsonResponseStatus

            1.5)在IUserService中定义userLogin(UserVo userVo),并返回JsonResponseBody

    1.5.1)判断mobile和password是否为空
    1.5.2)判断mobile格式是否正确
    1.5.3)根据用户手机号码查询用户是否存在
    1.5.4)校验账号
    1.5.5)校验密码

    1. package com.ycx.spbootpro.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.ycx.spbootpro.exception.BusinessException;
    4. import com.ycx.spbootpro.model.User;
    5. import com.ycx.spbootpro.mapper.UserMapper;
    6. import com.ycx.spbootpro.model.dto.UserDto;
    7. import com.ycx.spbootpro.service.IUserService;
    8. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    9. import com.ycx.spbootpro.utils.JsonResponseBody;
    10. import com.ycx.spbootpro.utils.JsonResponseStatus;
    11. import org.springframework.beans.factory.annotation.Autowired;
    12. import org.springframework.stereotype.Service;
    13. import javax.servlet.http.HttpServletRequest;
    14. import javax.servlet.http.HttpServletResponse;
    15. import javax.validation.Valid;
    16. /**
    17. *

    18. * 用户信息表 服务实现类
    19. *

    20. *
    21. * @author yangzong
    22. * @since 2022-11-05
    23. */
    24. @Service
    25. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    26. @Autowired
    27. private UserMapper userMapper;
    28. @Override
    29. public JsonResponseBody toLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
    30. // 1.5.1)判断mobile和password是否为空(已由JSP303完成)
    31. // 1.5.2)判断mobile格式是否正确(自定义验证注解)
    32. // 1.5.3)根据用户手机号码查询用户是否存在
    33. User user = userMapper.selectOne(new QueryWrapper<User>()
    34. .eq("id", userDto.getMobile()));
    35. //1.5.4)校验账号
    36. //判断用户对象是否存在
    37. if(null==user)
    38. throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
    39. // 1.5.5)校验密码
    40. if(!user.getPassword().equals(userDto.getPassword()))
    41. throw new BusinessException(JsonResponseStatus.USER_PASSWORD_ERROR);
    42. return new JsonResponseBody<>();
    43. }
    44. }

            1.6)全局异常处理

    1.6.1)创建BusinessException
    1.6.2)创建GlobalExceptionHandler
    1.6.3)修改userLogin中的异常处理方式

            1.7)自定义注解参数校验(JSR303)

    1.7.1)创建自定义注解IsMobile
    1.7.2)创建自定义校验规则类MobileValidator
    1.7.3)在UserVo类的mobile属性中使用IsMobile注解

            自定义JSR303注解,完成服务端登录账号的验证

    查看用户表数据

    login.js:

    1. $(function () {
    2. alert(2);
    3. //登录向后台发送Ajax请求
    4. $("#login").click(function () {
    5. let mobile = $("#mobile").val();
    6. let password = $("#password").val();
    7. $.post("/user/toLogin",{
    8. mobile:mobile,
    9. password:password
    10. },function (res) {
    11. alert(res.msg)
    12. },"json");
    13. })
    14. });

    测试多种情况: 

    1.手机号为空

    2.手机号为非法字符

    3.密码为空

    4.手机号不存在

    5.手机号密码正确

    6.密码错误

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

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

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

    运行:

    把 后端加密随机盐 和 加密密码  复制进表数据:

     

     再次运行,每次运行出来的密码都不一样:

    记得在login.html导入:

    UserServiceImpl.java变更如下

    1. package com.ycx.spbootpro.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.ycx.spbootpro.exception.BusinessException;
    4. import com.ycx.spbootpro.model.User;
    5. import com.ycx.spbootpro.mapper.UserMapper;
    6. import com.ycx.spbootpro.model.dto.UserDto;
    7. import com.ycx.spbootpro.service.IUserService;
    8. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    9. import com.ycx.spbootpro.utils.JsonResponseBody;
    10. import com.ycx.spbootpro.utils.JsonResponseStatus;
    11. import com.ycx.spbootpro.utils.MD5Utils;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.stereotype.Service;
    14. import javax.servlet.http.HttpServletRequest;
    15. import javax.servlet.http.HttpServletResponse;
    16. import javax.validation.Valid;
    17. /**
    18. *

    19. * 用户信息表 服务实现类
    20. *

    21. *
    22. * @author yangzong
    23. * @since 2022-11-05
    24. */
    25. @Service
    26. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    27. @Autowired
    28. private UserMapper userMapper;
    29. @Override
    30. public JsonResponseBody toLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
    31. // 1.5.1)判断mobile和password是否为空(已由JSP303完成)
    32. // 1.5.2)判断mobile格式是否正确(自定义验证注解)
    33. // 1.5.3)根据用户手机号码查询用户是否存在
    34. User user = userMapper.selectOne(new QueryWrapper<User>()
    35. .eq("id", userDto.getMobile()));
    36. //1.5.4)校验账号
    37. //判断用户对象是否存在
    38. if(null==user)
    39. throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
    40. // 前台传递到后台的密码要经过工具类md5加密一次才有可能跟数据库密码匹配上
    41. String pwd = MD5Utils.formPassToDbPass(userDto.getPassword(), user.getSalt());
    42. // 1.5.5)校验密码
    43. if(!pwd.equals(user.getPassword()))
    44. throw new BusinessException(JsonResponseStatus.USER_PASSWORD_ERROR);
    45. return new JsonResponseBody<>();
    46. }
    47. }

    login.js变更如下

    1. $(function () {
    2. alert(2);
    3. //登录向后台发送Ajax请求
    4. $("#login").click(function () {
    5. let mobile = $("#mobile").val();
    6. let password = $("#password").val();
    7. //1.密码加密
    8. //1) 定义固定盐
    9. let salt='f1g2h3j4';
    10. //2) 固定盐混淆
    11. let temp=salt.charAt(1)+""+salt.charAt(5)+password+salt.charAt(0)+""+salt.charAt(3);
    12. //3) 使用MD5完成前端第一次加密
    13. let pwd=md5(temp);
    14. $.post("/user/toLogin",{
    15. mobile:mobile,
    16. password:pwd
    17. },function (res) {
    18. alert(res.msg)
    19. },"json");
    20. })
    21. });

    再次运行:

     

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

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

    UserServiceImpl.java变更如下

    1. package com.ycx.spbootpro.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.ycx.spbootpro.exception.BusinessException;
    4. import com.ycx.spbootpro.model.User;
    5. import com.ycx.spbootpro.mapper.UserMapper;
    6. import com.ycx.spbootpro.model.dto.UserDto;
    7. import com.ycx.spbootpro.service.IRedisService;
    8. import com.ycx.spbootpro.service.IUserService;
    9. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    10. import com.ycx.spbootpro.utils.CookieUtils;
    11. import com.ycx.spbootpro.utils.JsonResponseBody;
    12. import com.ycx.spbootpro.utils.JsonResponseStatus;
    13. import com.ycx.spbootpro.utils.MD5Utils;
    14. import org.springframework.beans.factory.annotation.Autowired;
    15. import org.springframework.stereotype.Service;
    16. import javax.servlet.http.HttpServletRequest;
    17. import javax.servlet.http.HttpServletResponse;
    18. import javax.validation.Valid;
    19. import java.util.UUID;
    20. /**
    21. *

    22. * 用户信息表 服务实现类
    23. *

    24. *
    25. * @author yangzong
    26. * @since 2022-11-05
    27. */
    28. @Service
    29. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    30. @Autowired
    31. private UserMapper userMapper;
    32. @Autowired
    33. private IRedisService redisService;
    34. @Override
    35. public JsonResponseBody toLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
    36. // 1.5.1)判断mobile和password是否为空(已由JSP303完成)
    37. // 1.5.2)判断mobile格式是否正确(自定义验证注解)
    38. // 1.5.3)根据用户手机号码查询用户是否存在
    39. User user = userMapper.selectOne(new QueryWrapper<User>()
    40. .eq("id", userDto.getMobile()));
    41. //1.5.4)校验账号
    42. //判断用户对象是否存在
    43. if(null==user)
    44. throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
    45. // 前台传递到后台的密码要经过工具类md5加密一次才有可能跟数据库密码匹配上
    46. String pwd = MD5Utils.formPassToDbPass(userDto.getPassword(), user.getSalt());
    47. // 1.5.5)校验密码
    48. if(!pwd.equals(user.getPassword()))
    49. throw new BusinessException(JsonResponseStatus.USER_PASSWORD_ERROR);
    50. //6.将登陆用户对象与token令牌进行绑定保存到cookie和redis
    51. //创建登陆令牌token
    52. String token= UUID.randomUUID().toString().replace("-","");
    53. //将token令牌保存到cookie中
    54. CookieUtils.setCookie(req,resp,"token",token,7200);
    55. //将登陆token令牌与用户对象user绑定到redis中
    56. redisService.setUserToRedis(token,user);
    57. //将用户登陆的昵称设置到cookie中
    58. CookieUtils.setCookie(req,resp,"nickname",user.getNickname());
    59. return new JsonResponseBody<>();
    60. }
    61. }

    IRedisService.java:

    1. package com.ycx.spbootpro.service;
    2. import com.ycx.spbootpro.model.User;
    3. /**
    4. * @author 杨总
    5. * @create 2022-11-05 20:45
    6. */
    7. public interface IRedisService {
    8. void setUserToRedis(String token, User user) ;
    9. User getUserByToken(String token);
    10. }

    RedisServiceImpl.java:

    1. package com.ycx.spbootpro.service.impl;
    2. import com.ycx.spbootpro.model.User;
    3. import com.ycx.spbootpro.service.IRedisService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.data.redis.core.RedisTemplate;
    6. import org.springframework.stereotype.Service;
    7. import java.util.concurrent.TimeUnit;
    8. /**
    9. * @author 杨总
    10. * @create 2022-11-05 20:49
    11. */
    12. @Service
    13. public class RedisServiceImpl implements IRedisService {
    14. @Autowired
    15. private RedisTemplate<String,Object> redisTemplate;
    16. @Override
    17. public void setUserToRedis(String token, User user) {
    18. redisTemplate.opsForValue().set("user:"+token,user,7200L, TimeUnit.SECONDS);
    19. }
    20. @Override
    21. public User getUserByToken(String token) {
    22. return (User) redisTemplate.opsForValue().get("user:"+token);
    23. }
    24. }

    测试如下:

     

     

    今日内容就到这里啦~再会!

  • 相关阅读:
    Jenkins共享库使用
    Docker 入门,学明白这一篇就够啦
    Centos7 部署Jenkins
    数据库和缓存如何保证一致性?
    [附源码]Python计算机毕业设计Django校园招聘微信小程序
    TDD、BDD、ATDD都是什么、有什么区别?(上)
    【力扣每日一题】2023.9.5 从两个数字数组里生成最小数字
    【无标题】
    兔起鹘落全端涵盖,Go lang1.18入门精炼教程,由白丁入鸿儒,全平台(Sublime 4)Go lang开发环境搭建EP00
    后厂村路灯在线签名网站,在线签名工具,IPA在线签名
  • 原文地址:https://blog.csdn.net/weixin_65808248/article/details/127694190