• SpringBoot--网上商城项目(前端搭建、首页、用户登录、盐加密、登录令牌管理)


                                                                            文章目录

    一、项目技术点

    数据表

    二、构建SpringBoot项目

    1、创建SpringBoot项目并配置pom

    配置pom  

    2、application.yml的配置

    3、首页访问

     三、首页功能

    工具类!!!

     首页数据绑定语法

     四、用户明文登录

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

    盐加密

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


    一、项目技术点

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

    数据表

    用户表:t_user

    订单项表:t_order_item                           

    数据项表:t_dict_data

    商品表:t_goods                       

    订单表:t_order

    数据源表:t_dict_type

    二、构建SpringBoot项目

    1、创建SpringBoot项目并配置pom

     

     

    配置pom  

    1. <parent>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-parentartifactId>
    4. <version>2.3.9.RELEASEversion>
    5. <relativePath/>
    6. parent>

     

     导入pom依赖时,报红杠杠,是网络问题,删除重新导入或者换一个网络重新导入

    1. <properties>
    2. <java.version>1.8java.version>
    3. properties>
    4. <dependencies>
    5. <dependency>
    6. <groupId>org.springframework.bootgroupId>
    7. <artifactId>spring-boot-starter-freemarkerartifactId>
    8. dependency>
    9. <dependency>
    10. <groupId>org.springframework.bootgroupId>
    11. <artifactId>spring-boot-starter-webartifactId>
    12. dependency>
    13. <dependency>
    14. <groupId>mysqlgroupId>
    15. <artifactId>mysql-connector-javaartifactId>
    16. <scope>runtimescope>
    17. <version>5.1.44version>
    18. dependency>
    19. <dependency>
    20. <groupId>org.projectlombokgroupId>
    21. <artifactId>lombokartifactId>
    22. <optional>trueoptional>
    23. dependency>
    24. <dependency>
    25. <groupId>org.springframework.bootgroupId>
    26. <artifactId>spring-boot-starter-testartifactId>
    27. <scope>testscope>
    28. <exclusions>
    29. <exclusion>
    30. <groupId>org.junit.vintagegroupId>
    31. <artifactId>junit-vintage-engineartifactId>
    32. exclusion>
    33. exclusions>
    34. dependency>
    35. <dependency>
    36. <groupId>junitgroupId>
    37. <artifactId>junitartifactId>
    38. <scope>testscope>
    39. dependency>
    40. <dependency>
    41. <groupId>com.baomidougroupId>
    42. <artifactId>mybatis-plus-boot-starterartifactId>
    43. <version>3.4.0version>
    44. dependency>
    45. <dependency>
    46. <groupId>com.baomidougroupId>
    47. <artifactId>mybatis-plus-generatorartifactId>
    48. <version>3.4.0version>
    49. dependency>
    50. <dependency>
    51. <groupId>com.zaxxergroupId>
    52. <artifactId>HikariCPartifactId>
    53. dependency>
    54. <dependency>
    55. <groupId>commons-codecgroupId>
    56. <artifactId>commons-codecartifactId>
    57. dependency>
    58. <dependency>
    59. <groupId>org.apache.commonsgroupId>
    60. <artifactId>commons-lang3artifactId>
    61. <version>3.6version>
    62. dependency>
    63. <dependency>
    64. <groupId>org.springframework.bootgroupId>
    65. <artifactId>spring-boot-starter-validationartifactId>
    66. dependency>
    67. <dependency>
    68. <groupId>org.springframework.bootgroupId>
    69. <artifactId>spring-boot-starter-data-redisartifactId>
    70. dependency>
    71. <dependency>
    72. <groupId>org.apache.commonsgroupId>
    73. <artifactId>commons-pool2artifactId>
    74. dependency>
    75. <dependency>
    76. <groupId>com.alipay.sdkgroupId>
    77. <artifactId>alipay-easysdkartifactId>
    78. <version>2.0.1version>
    79. dependency>
    80. dependencies>
    81. <build>
    82. <plugins>
    83. <plugin>
    84. <groupId>org.springframework.bootgroupId>
    85. <artifactId>spring-boot-maven-pluginartifactId>
    86. plugin>
    87. plugins>
    88. build>

    2、application.yml的配置

    没有下载这个插件的话,需要下载,下载后,重新启动idea哦,这应该是常识了叭~~~~~~~~~~~

      转成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: 123456
    10. username: root
    11. hikari:
    12. # 最小空闲连接数量
    13. minimum-idle: 5
    14. # 空闲连接存活最大时间,默认600000(10分钟)
    15. idle-timeout: 180000
    16. # 连接池最大连接数,默认是10
    17. maximum-pool-size: 10
    18. # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
    19. auto-commit: true
    20. # 连接池名称
    21. pool-name: MyHikariCP
    22. # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
    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.141.128
    42. #端口
    43. port: 6379
    44. #密码
    45. password: 123456
    46. #选择数据库
    47. database: 0
    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.xiaokun.spbootpro.model
    68. configuration:
    69. #驼峰命名规则
    70. map-underscore-to-camel-case: true
    71. #日志配置
    72. logging:
    73. level:
    74. com.xiaokun.spbootpro.mapper: debug

    配置中该修该的修改一下,比如服务端IP,数据库名称什么的。 

     这个后缀不用修改也不用添加,空的方便后续开发

      编写controller层,访问首页界面

    3、首页访问

    样式,依赖都已经调整好了,导入前端页面及页面对应的js/css/images文件

    前端静态页面,导入你手上现有的就ok

     在浏览器中输入访问地址 http://localhost:8081/

    访问成功,就说明你的前端搭建好了

     三、首页功能

    将这边的静态数据变为动态数据,什么是动态数据呢?就是数据库中的数据👀 

    如果它的数据量大于6条时,只取前6条数据 

     

     导入工具类与代码生成器

    导入之前需要下载一个插件--Lombok ⬇⬇⬇

    1. package com.xiaokun.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 redisTemplate(RedisConnectionFactory connectionFactory){
    12. RedisTemplate 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. }

    1. package com.xiaokun.spbootpro.exception;
    2. import com.xiaokun.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. }
    1. package com.xiaokun.spbootpro.exception;
    2. import com.xiaokun.spbootpro.utils.JsonResponseBody;
    3. import com.xiaokun.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. }

    1. package com.xiaokun.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("xiaokun");
    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("123456");
    53. mpg.setDataSource(dsc);
    54. // 包配置
    55. PackageConfig pc = new PackageConfig();
    56. //pc.setModuleName(scanner("模块名"));
    57. pc.setParent("com.xiaokun.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 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. }

    工具类!!!

     

    1. package com.xiaokun.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. }
    1. package com.xiaokun.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> transfor(int cols, List goods){
    14. Map> 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. }

     

    1. package com.xiaokun.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. }

     

    1. package com.xiaokun.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. }
    1. package com.xiaokun.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. }

     

    1. package com.xiaokun.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. }
    1. package com.xiaokun.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 map;
    18. public String getUrl() {
    19. return url;
    20. }
    21. public void setUrl(String url) {
    22. this.url = url;
    23. }
    24. public Map getMap() {
    25. return map;
    26. }
    27. public void setMap(Map 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. }
    1. package com.xiaokun.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. }

     

    1. package com.xiaokun.spbootpro.validator;
    2. import javax.validation.Constraint;
    3. import javax.validation.Payload;
    4. import java.lang.annotation.*;
    5. @Documented
    6. @Constraint(
    7. validatedBy = {MobileValidator.class}
    8. )
    9. @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    10. @Retention(RetentionPolicy.RUNTIME)
    11. public @interface IsMobile {
    12. boolean required() default true;
    13. String message() default "手机号码格式错误!";
    14. Class[] groups() default {};
    15. Classextends Payload>[] payload() default {};
    16. }
    1. package com.xiaokun.spbootpro.validator;
    2. import com.xiaokun.spbootpro.utils.ValidatorUtils;
    3. import javax.validation.ConstraintValidator;
    4. import javax.validation.ConstraintValidatorContext;
    5. public class MobileValidator implements ConstraintValidator {
    6. private boolean required=false;
    7. @Override
    8. public void initialize(IsMobile constraintAnnotation) {
    9. this.required=constraintAnnotation.required();
    10. }
    11. @Override
    12. public boolean isValid(String mobile, ConstraintValidatorContext constraintValidatorContext) {
    13. if(!this.required)
    14. return false;
    15. return ValidatorUtils.isMobile(mobile);
    16. }
    17. }

    导入模板生成类

      

    将默认生成的class文件清空,再重新生成一遍

    生成一波 

    看到文件生成完成字样是不是以为生成成功了??不,idea虚晃一枪,吓唬你的!!

    怎么判断是否生成成功??

    看出区别了吗~~~~~

    没有生成成功,把生成失败的空的mapper,model等文件删除重新运行就好了。 

      生成成功案例~~~~~~

    @Autowired 标注接口,注入该接口的实现类

     

    IndexController.java    
    1. package com.xiaokun.spbootpro.controller;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.xiaokun.spbootpro.model.Goods;
    4. import com.xiaokun.spbootpro.service.IGoodsService;
    5. import com.xiaokun.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-07-17:09
    15. */
    16. @Controller
    17. public class IndexController {
    18. @Autowired
    19. private IGoodsService goodsService;
    20. @RequestMapping("/")
    21. public String index(Model model){
    22. //摆件花艺
    23. List goods01 = goodsService.list(new QueryWrapper()
    24. .eq("goods_type", "01")
    25. .last("limit 6"));
    26. //壁挂北欧
    27. List goods07 = goodsService.list(new QueryWrapper()
    28. .eq("goods_type", "07")
    29. .last("limit 12"));
    30. //以下代码为了方便首页数据展示,方便摆放
    31. DataUtils dataUtils=new DataUtils<>();
    32. Map> gt01=dataUtils.transfor(3,goods01);
    33. Map> gt07=dataUtils.transfor(4,goods07);
    34. model.addAttribute("gt01",gt01);
    35. model.addAttribute("gt07",gt07);
    36. return "index.html";
    37. }
    38. }

    解释一下代码中的cols:3 / 4是什么意思 

    使用debug调式,看一下有没有拿到理想的展示的数据

     理想展示效果👀

     首页数据绑定语法

    小编在写这一步时,前端代码调不出,手敲的代码不变色以及会报红,不知道你们会不会有这个问题,但这个问题很好解决加入html就好了⬇⬇

     语法:

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

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

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

     四、用户明文登录

    现在点击首页的登录按钮,是没有登录界面的,所以⬇⬇

    写一个公共的 controller ,先解决跳转登录的问题

    1. package com.xiaokun.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-08-14:31
    8. */
    9. @Controller
    10. public class PageController {
    11. @RequestMapping("/page/{page}")
    12. public String page(@PathVariable("page") String page){
    13. return page;
    14. }
    15. @RequestMapping("/page/{dir}/{page}")
    16. public String dir(@PathVariable("dir") String dir,@PathVariable("page") String page){
    17. return dir + "/" +page;
    18. }
    19. }

     

    跳转没问题喽~~~~~其他页面的跳转都ok了,因为这个类是公共的

     现在可以去编写登录界面的方法一些什么的,创建一个文件包。

       

    验证是否可以使用 

     

     刷新一手,页面弹1就没问题,这样就可以放心写代码了

     login.js ⬇⬇

    1. $(function () {
    2. alert(1);
    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. package com.xiaokun.spbootpro.model.dto;
    2. import lombok.Data;
    3. import javax.validation.constraints.NotBlank;
    4. @Data
    5. public class UserDto {
    6. @NotBlank(message = "手机号码不能为空!")
    7. // @IsMobile
    8. private String mobile;
    9. @NotBlank(message = "密码不能为空!")
    10. private String password;
    11. }

    编写 UserController.java 处理login.js发送的请求
    1. package com.xiaokun.spbootpro.controller;
    2. import com.xiaokun.spbootpro.model.dto.UserDto;
    3. import com.xiaokun.spbootpro.service.IUserService;
    4. import com.xiaokun.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. /**
    11. *

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

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

    返回JsonResponseBody

    编写接口类 UserServiceImpl.java

    1. package com.xiaokun.spbootpro.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.xiaokun.spbootpro.exception.BusinessException;
    4. import com.xiaokun.spbootpro.model.User;
    5. import com.xiaokun.spbootpro.mapper.UserMapper;
    6. import com.xiaokun.spbootpro.model.dto.UserDto;
    7. import com.xiaokun.spbootpro.service.IUserService;
    8. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    9. import com.xiaokun.spbootpro.utils.JsonResponseBody;
    10. import com.xiaokun.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. /**
    16. *

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

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

    数据库明文密码内容 

     测试一波,登录的提示功能

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

    盐加密

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

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

    加载盐加密 

    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. if (res.code!=200){
    19. alert(res.msg);
    20. }else
    21. // alert(res.msg)
    22. location.href='/';
    23. },"json");
    24. });
    25. });

    UserServiceImpl.java变更如下

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

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

    21. *
    22. * @author xiaokun
    23. * @since 2022-11-07
    24. */
    25. @Service
    26. public class UserServiceImpl extends ServiceImpl 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是否为空 @Valid注解帮我做完了
    32. // 1.5.2)判断mobile格式是否正确 @IsMobile注解帮我做完了
    33. // 1.5.3)根据用户手机号码查询用户是否存在
    34. User user = userMapper.selectOne(new QueryWrapper()
    35. .eq("id", userDto.getMobile()));
    36. // 1.5.4)校验账号
    37. if(user==null){
    38. throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
    39. }
    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. }
    46. return new JsonResponseBody();
    47. }
    48. }

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

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

    UserServiceImpl.java变更如下

    1. package com.xiaokun.spbootpro.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    4. import com.xiaokun.spbootpro.exception.BusinessException;
    5. import com.xiaokun.spbootpro.mapper.UserMapper;
    6. import com.xiaokun.spbootpro.model.User;
    7. import com.xiaokun.spbootpro.model.dto.UserDto;
    8. import com.xiaokun.spbootpro.service.IRedisService;
    9. import com.xiaokun.spbootpro.service.IUserService;
    10. import com.xiaokun.spbootpro.utils.CookieUtils;
    11. import com.xiaokun.spbootpro.utils.JsonResponseBody;
    12. import com.xiaokun.spbootpro.utils.JsonResponseStatus;
    13. import com.xiaokun.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 java.util.UUID;
    19. /**
    20. * @author 小坤
    21. * @create 2022-11-09-9:18
    22. */
    23. @Service
    24. public class UserServiceImpl extends ServiceImpl implements IUserService {
    25. @Autowired
    26. private UserMapper userMapper;
    27. @Autowired
    28. private IRedisService redisService;
    29. @Override
    30. public JsonResponseBody toLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
    31. //1.判断账号和密码是否为空(已由JSP303完成)
    32. //2.判断手机号码格式是否正确(自定义验证注解)
    33. //3.根据手机号码查询用户对象信息
    34. User user = userMapper.selectOne(new QueryWrapper()
    35. .eq("id", userDto.getMobile()));
    36. //4.判断用户对象是否存在
    37. if(null==user)
    38. throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
    39. //5.判断用户对象密码与输入密码是否一致
    40. //将前端传入的第一道加密密码结合从数据库查询出来的随机盐进行加密(二道加密)
    41. String pwd = MD5Utils.formPassToDbPass(userDto.getPassword(), user.getSalt());
    42. if(!user.getPassword().equals(pwd))
    43. throw new BusinessException(JsonResponseStatus.USER_PASSWORD_ERROR);
    44. //6.将登陆用户对象与token令牌进行绑定保存到cookie和redis
    45. //创建登陆令牌token
    46. String token= UUID.randomUUID().toString().replace("-","");
    47. //将token令牌保存到cookie中
    48. CookieUtils.setCookie(req,resp,"token",token,7200);
    49. //将登陆token令牌与用户对象user绑定到redis中
    50. redisService.setUserToRedis(token,user);
    51. //将用户登陆的昵称设置到cookie中
    52. CookieUtils.setCookie(req,resp,"nickname",user.getNickname());
    53. return new JsonResponseBody<>();
    54. }
    55. }
    IRedisService
    1. package com.xiaokun.spbootpro.service;
    2. import com.xiaokun.spbootpro.model.User;
    3. /**
    4. * @author 小坤
    5. * @create 2022-11-09-8:36
    6. */
    7. public interface IRedisService {
    8. /**
    9. * 将用户对象user与登陆token令牌保存到redis
    10. * @param token 登陆令牌token
    11. * @param user 登陆用户对象user
    12. */
    13. void setUserToRedis(String token, User user);
    14. /**
    15. * 根据登陆token令牌到redis中获取对应的用户登陆对象信息
    16. * @param token 登陆令牌token
    17. * @return 登陆用户对象
    18. */
    19. User getUserToRedis(String token);
    20. }
    IRedisServiceImpl
    1. package com.xiaokun.spbootpro.service.impl;
    2. import com.xiaokun.spbootpro.model.User;
    3. import com.xiaokun.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-09-8:39
    11. */
    12. @Service
    13. public class IRedisServiceImpl implements IRedisService {
    14. @Autowired
    15. private RedisTemplate 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 getUserToRedis(String token) {
    22. return (User)redisTemplate.opsForValue().get("user:"+token);
    23. }
    24. }

    开启你在application.yml中使用的虚拟机或云服务器​

     并使用虚拟机连接器连接成功后启动redis

     运行测试,前端是否拿到token

  • 相关阅读:
    P01914100尹自杨
    基于ESP32调试ADS1115 16 位 ADC 模块
    【计算机毕业设计】新冠疫情隔离人员信息管理系统+vue源码
    一文讲解ARMv8内存属性与类型(Memory types and attributes)简介
    js获取当前地址栏链接参数
    Java序列化详解(基础入门)
    Vue Router的介绍
    java学习记录
    浅谈shadow dom
    什么是美颜SDK?如何创建自定义美颜直播应用?
  • 原文地址:https://blog.csdn.net/weixin_67450855/article/details/127731922