当前位置:   article > 正文

秒杀项目介绍&&登陆模块&&捕捉全局异常_layui对于后端抛异常怎么处理

layui对于后端抛异常怎么处理

一、秒杀项目介绍

1、介绍

秒杀无论是双十一购物还是 12306 抢票,秒杀场景已随处可见。简单来说,秒杀就是在同一时刻大量请求争抢购买同一商品并完成交易的过程。从架构视角来看,秒杀系统本质是一个高性能、高一致、高可用的三高系统。

通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动

2、特点

  • 高并发:秒杀的特点就是这样时间极短、 瞬间用户量大
  • 库存量少:一般秒杀活动商品量很少,这就导致了只有极少量用户能成功购买到。
  • 业务简单:流程比较简单,一般都是下订单、扣库存、支付订单
  • 恶意请求,数据库压力大
  • 秒杀的商品不需要添加到购物车
  • 秒杀系统独立部署

二、技术上点介绍

前端:Freemarker、LayUI、jQuery
后端:SpringBoot、MyBatisPlus、Lombok
中间件:RabbitMQ、Redis(redisson)
分布式协调框架:zookeeper

三、项目目标

1.安全优化:隐藏秒杀地址、验证码、接口限流
2.服务优化:RabbitMQ消息队列、接口优化、分布式锁
3.页面优化:缓存、静态化分离
4.分布式会话:用户登录、共享session
5.功能开发:商品列表、商品详情、秒杀、订单详情
6.系统压测:JMeter入门、自定义变量、压测

四、搭建环境

1、配置数据库以及表

导入sql代码

连接到IDEA

2、创建SpringBoot项目并配置POM

  1. <?xml version="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.0</modelVersion>
  5. <groupId>com.xhy</groupId>
  6. <artifactId>code</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <name>code</name>
  9. <description>Demo project for Spring Boot</description>
  10. <properties>
  11. <java.version>1.8</java.version>
  12. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  13. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  14. <spring-boot.version>2.4.1</spring-boot.version>
  15. </properties>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-aop</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-test</artifactId>
  28. <scope>test</scope>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-freemarker</artifactId>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-web</artifactId>
  37. </dependency>
  38. <dependency>
  39. <groupId>mysql</groupId>
  40. <artifactId>mysql-connector-java</artifactId>
  41. <scope>runtime</scope>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.projectlombok</groupId>
  45. <artifactId>lombok</artifactId>
  46. <optional>true</optional>
  47. </dependency>
  48. <!-- mybatis plus依赖 -->
  49. <dependency>
  50. <groupId>com.baomidou</groupId>
  51. <artifactId>mybatis-plus-boot-starter</artifactId>
  52. <version>3.5.1</version>
  53. </dependency>
  54. <!--mybatis-plus生成器-->
  55. <dependency>
  56. <groupId>com.baomidou</groupId>
  57. <artifactId>mybatis-plus-generator</artifactId>
  58. <version>3.5.2</version>
  59. </dependency>
  60. <!-- MD5依赖 -->
  61. <dependency>
  62. <groupId>commons-codec</groupId>
  63. <artifactId>commons-codec</artifactId>
  64. </dependency>
  65. <dependency>
  66. <groupId>org.apache.commons</groupId>
  67. <artifactId>commons-lang3</artifactId>
  68. <version>3.12.0</version>
  69. </dependency>
  70. <!-- valid验证依赖 -->
  71. <dependency>
  72. <groupId>org.springframework.boot</groupId>
  73. <artifactId>spring-boot-starter-validation</artifactId>
  74. </dependency>
  75. <!--redis-->
  76. <dependency>
  77. <groupId>org.springframework.boot</groupId>
  78. <artifactId>spring-boot-starter-data-redis</artifactId>
  79. </dependency>
  80. <!--hariki-->
  81. <dependency>
  82. <groupId>com.zaxxer</groupId>
  83. <artifactId>HikariCP</artifactId>
  84. </dependency>
  85. </dependencies>
  86. <dependencyManagement>
  87. <dependencies>
  88. <dependency>
  89. <groupId>org.springframework.boot</groupId>
  90. <artifactId>spring-boot-dependencies</artifactId>
  91. <version>${spring-boot.version}</version>
  92. <type>pom</type>
  93. <scope>import</scope>
  94. </dependency>
  95. </dependencies>
  96. </dependencyManagement>
  97. <build>
  98. <plugins>
  99. <plugin>
  100. <groupId>org.apache.maven.plugins</groupId>
  101. <artifactId>maven-compiler-plugin</artifactId>
  102. <version>3.8.1</version>
  103. <configuration>
  104. <source>1.8</source>
  105. <target>1.8</target>
  106. <encoding>UTF-8</encoding>
  107. </configuration>
  108. </plugin>
  109. <plugin>
  110. <groupId>org.springframework.boot</groupId>
  111. <artifactId>spring-boot-maven-plugin</artifactId>
  112. <version>2.4.1</version>
  113. <configuration>
  114. <mainClass>com.xhy.code.CodeApplication</mainClass>
  115. </configuration>
  116. <executions>
  117. <execution>
  118. <id>repackage</id>
  119. <goals>
  120. <goal>repackage</goal>
  121. </goals>
  122. </execution>
  123. </executions>
  124. </plugin>
  125. </plugins>
  126. </build>
  127. </project>

3 、配置application.yml

  1. spring:
  2. application:
  3. name: seckill
  4. datasource:
  5. url: jdbc:mysql://localhost:3306/seckill?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF8
  6. driver-class-name: com.mysql.cj.jdbc.Driver
  7. username: root
  8. password: 123456
  9. hikari:
  10. # 最小空闲连接数量
  11. minimum-idle: 5
  12. # 空闲连接存活最大时间,默认60000010分钟)
  13. idle-timeout: 180000
  14. # 连接池最大连接数,默认是10
  15. maximum-pool-size: 10
  16. # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
  17. auto-commit: true
  18. # 连接池名称
  19. pool-name: MyHikariCP
  20. # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认180000030分钟
  21. max-lifetime: 1800000
  22. # 数据库连接超时时间,默认30秒,即30000
  23. connection-timeout: 30000
  24. freemarker:
  25. #设置编码格式
  26. charset: UTF-8
  27. #后缀
  28. suffix: .ftl
  29. #文档类型
  30. content-type: text/html
  31. #模板前端
  32. template-loader-path: classpath:/templates/
  33. #启用模板
  34. enabled: true
  35. #开启静态资源
  36. mvc:
  37. static-path-pattern: /static/**
  38. mybatis-plus:
  39. mapper-locations: classpath*:/mapper/*Mapper.xml
  40. type-aliases-package: com.xhy.seckill.pojo
  41. configuration:
  42. map-underscore-to-camel-case: true
  43. logging:
  44. level:
  45. com.xhy.seckill.mapper: debug

5、 使用Mybatis-plus反向生成代码

  1. package com.code.ceckill.generator;
  2. import com.baomidou.mybatisplus.annotation.FieldFill;
  3. import com.baomidou.mybatisplus.generator.FastAutoGenerator;
  4. import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
  5. import com.baomidou.mybatisplus.generator.config.OutputFile;
  6. import com.baomidou.mybatisplus.generator.config.rules.DateType;
  7. import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
  8. import com.baomidou.mybatisplus.generator.fill.Column;
  9. import lombok.Data;
  10. import lombok.extern.slf4j.Slf4j;
  11. import java.util.Arrays;
  12. import java.util.Collections;
  13. import java.util.List;
  14. @SuppressWarnings("all")
  15. @Slf4j
  16. @Data
  17. public class MybatisPlusGenerator {
  18. //url: jdbc:mysql://localhost:3306/seckill?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF8
  19. protected static String URL = "jdbc:mysql://localhost:3306/seckill?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF8";
  20. protected static String USERNAME = "root";
  21. protected static String PASSWORD = "123456";
  22. protected static DataSourceConfig.Builder DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(URL, USERNAME, PASSWORD);
  23. public static void main(String[] args) {
  24. FastAutoGenerator.create(DATA_SOURCE_CONFIG)
  25. .globalConfig(
  26. (scanner/*lamdba*/, builder/*变量*/) ->
  27. builder.author(scanner.apply("请输入作者名称?"))
  28. .enableSwagger()
  29. .fileOverride()
  30. .outputDir(System.getProperty("user.dir") + "\\src\\main\\java")
  31. .commentDate("yyyy-MM-dd")
  32. .dateType(DateType.TIME_PACK)
  33. )
  34. .packageConfig((builder) ->
  35. builder.parent("com.xhy.seckill")
  36. .entity("pojo")
  37. .service("service")
  38. .serviceImpl("service.impl")
  39. .mapper("mapper")
  40. .xml("mapper.xml")
  41. .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "\\src\\main\\resources\\mapper"))
  42. )
  43. .injectionConfig((builder) ->
  44. builder.beforeOutputFile(
  45. (a, b) -> log.warn("tableInfo: " + a.getEntityName())
  46. )
  47. )
  48. .strategyConfig((scanner, builder) ->
  49. builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
  50. .addTablePrefix("tb_", "t_")
  51. .entityBuilder()
  52. .enableChainModel()
  53. .enableLombok()
  54. .enableTableFieldAnnotation()
  55. .addTableFills(
  56. new Column("create_time", FieldFill.INSERT)
  57. )
  58. .controllerBuilder()
  59. .enableRestStyle()
  60. .enableHyphenStyle()
  61. .build())
  62. .templateEngine(new FreemarkerTemplateEngine())
  63. .execute();
  64. }
  65. protected static List<String> getTables(String tables) {
  66. return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
  67. }
  68. }

 

6、启动类添加注解开启一些启动器

五、 用户登陆

登录密码的加密

用户登录的时候,登录信息由前端传递到后端是通过http协议进行传输的,由于http协议是明文传输的。所有的登录信息都在其body里面,所以将用户的登录密码进行明文传输安全性较低。所以在传输之前,我们在前端需要对password进行加密,通过MD5进行对称加密。

整个加密过程分为两次MD5加密:
(1)客户端:PASS = MD5(明文)
(2)服务端:PASS = MD5(用户输入+随机salt)

MD5加密的原理:MD5 (Message -digest algorithm 5)就是信息摘要的一种实现,它可以从任意长度的明文字符串生成128位的哈希值,也就是32位的16进制。

1、后端搭建

逆向生成代码生成user表的代码

 2、前端搭建(使用freemarker搭建)

 login.ftl

  1. <!DOCTYPE html>
  2. <html lang="zh">
  3. <head>
  4. <#include "common/head.ftl"/>
  5. <style>
  6. .layui-panel {
  7. position: absolute;
  8. width: 400px;
  9. top: 50%;
  10. left: 50%;
  11. transform: translate(-50%, -50%);
  12. padding: 15px 15px 0px 15px;
  13. border-radius: 20px;
  14. }
  15. .layui-form-label {
  16. padding: 9px 0px;
  17. }
  18. h3 {
  19. text-align: center;
  20. line-height: 45px;
  21. font-size: 40px;
  22. color: white;
  23. padding-bottom: 15px;
  24. }
  25. </style>
  26. </head>
  27. <body>
  28. <div>
  29. <div class="layui-panel layui-bg-cyan">
  30. <h3>用户登录</h3>
  31. <div class="layui-form-item">
  32. <label class="layui-form-label">用户账号</label>
  33. <div class="layui-input-block">
  34. <input type="text" id="mobile" autocomplete="on" class="layui-input" value="18684671234">
  35. </div>
  36. </div>
  37. <div class="layui-form-item">
  38. <label class="layui-form-label">用户密码</label>
  39. <div class="layui-input-block">
  40. <input type="password" id="password" autocomplete="on" class="layui-input" value="123456">
  41. </div>
  42. </div>
  43. <div class="layui-form-item" style="text-align:center;">
  44. <button class="layui-btn" id="login" style="width:46%">登录</button>
  45. <button class="layui-btn layui-btn-normal" id="register" style="width:46%">注册</button>
  46. </div>
  47. </div>
  48. </div>
  49. <script src="${ctx}/static/asset/js/project/md5.js"></script>
  50. <script src="${ctx}/static/asset/js/project/login.js"></script>
  51. </body>
  52. </html>

head.ftl

  1. <meta charset="UTF-8">
  2. <title>秒杀项目</title>
  3. <script src="/static/asset/js/layui/layui.js" type="text/javascript"></script>
  4. <link href="/static/asset/js/layui/css/layui.css" rel="stylesheet" type="text/css"/>
  5. <meta http-equiv="Expires" content="0">
  6. <meta http-equiv="Pragma" content="no-cache">
  7. <meta http-equiv="Cache-control" content="no-cache">
  8. <meta http-equiv="Cache" content="no-cache">
  9. <#assign ctx>
  10. ${springMacroRequestContext.getContextPath()}
  11. </#assign>

Layui 开发使用文档 - 入门指南

layui定义模块(login.js)

  1. layui.define(()=>{
  2. /**
  3. * 得到layui中封装jQuery
  4. *
  5. */
  6. let $=layui.jquery
  7. //给登陆按钮设置事件
  8. $(login).click(()=>{
  9. // alert("-----")
  10. var mobile = $(mobile).val();
  11. var password=$(password).val();
  12. })
  13. //将数据给后台(前后端axios。普通开发ajax)
  14. $.ajax({
  15. url:"",//后台登录接口
  16. data:{
  17. // 需要携带的数据
  18. mobile,
  19. password
  20. },
  21. datatype: "json",//后端给你的数据类型
  22. success(e){
  23. // 成功的回调函数
  24. },
  25. error(e){
  26. // 报错的回调函数
  27. }
  28. })
  29. })

专门用于跳转路径:PathController类

  1. package com.code.seckill.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.PathVariable;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. @Controller
  6. public class PathController {
  7. // 登录跳首页
  8. @RequestMapping("/")
  9. public String toPath(){
  10. return "login";
  11. }
  12. // 跳所有二级页面
  13. @RequestMapping("/{dir}/{path}")
  14. public String toPath(@PathVariable("dir") String dir,@PathVariable("path") String path){
  15. return dir+"/"+path;
  16. }
  17. }

解决端口占用找不到页面 

在yml文件中加入 

 访问端口8081

 六、MD5加密

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

1、导入相关的工具包

加入工具类:MD5Utils.java

  1. package com.code.seckill.util;
  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. @SuppressWarnings("all")
  13. public class MD5Utils {
  14. //加密盐,与前端一致
  15. private static String salt = "f1g2h3j4";
  16. /**
  17. * md5加密
  18. *
  19. * @param src
  20. * @return
  21. */
  22. public static String md5(String src) {
  23. return DigestUtils.md5Hex(src);
  24. }
  25. /**
  26. * 获取加密的盐
  27. *
  28. * @return
  29. */
  30. public static String createSalt() {
  31. return UUID.randomUUID().toString().replace("-", "");
  32. }
  33. /**
  34. * 将前端的明文密码通过MD5加密方式加密成后端服务所需密码
  35. * 注意:该步骤实际是在前端完成!!!
  36. *
  37. * @param inputPass 明文密码
  38. * @return
  39. */
  40. public static String inputPassToFormpass(String inputPass) {
  41. //混淆固定盐salt,安全性更可靠
  42. String str = salt.charAt(1) + "" + salt.charAt(5) + inputPass + salt.charAt(0) + "" + salt.charAt(3);
  43. return md5(str);
  44. }
  45. /**
  46. * 将后端密文密码+随机salt生成数据库的密码
  47. *
  48. * @param formPass
  49. * @param salt
  50. * @return
  51. */
  52. public static String formPassToDbPass(String formPass, String salt) {
  53. //混淆固定盐salt,安全性更可靠
  54. String str = salt.charAt(7) + "" + salt.charAt(9) + formPass + salt.charAt(1) + "" + salt.charAt(5);
  55. return md5(str);
  56. }
  57. /**
  58. * 将用户输入的密码转换成数据库的密码
  59. *
  60. * @param inputPass 明文密码
  61. * @param salt 盐
  62. * @return
  63. */
  64. public static String inputPassToDbPass(String inputPass, String salt) {
  65. String formPass = inputPassToFormpass(inputPass);
  66. String dbPass = formPassToDbPass(formPass, salt);
  67. return dbPass;
  68. }
  69. public static void main(String[] args) {
  70. String formPass = inputPassToFormpass("123456");
  71. System.out.println("前端加密密码:" + formPass);
  72. String salt = createSalt();
  73. System.out.println("后端加密随机盐:" + salt);
  74. String dbPass = formPassToDbPass(formPass, salt);
  75. System.out.println("后端加密密码:" + dbPass);
  76. String dbPass1 = inputPassToDbPass("123456", salt);
  77. System.out.println("最终加密密码:" + dbPass1);
  78. }
  79. }

2、新建vo类:userVo

用于前后端传值:

  1. package com.code.seckill.vo;
  2. import lombok.Data;
  3. @Data
  4. public class UserVo {
  5. //手机号
  6. private String mobile;
  7. //密码
  8. private String password;
  9. }

3、后端方法

IUserService

  1. package com.code.seckill.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.code.seckill.pojo.User;
  4. import com.code.seckill.util.response.ResponseResult;
  5. import com.code.seckill.vo.UserVo;
  6. /**
  7. * <p>
  8. * 用户信息表 服务类
  9. * </p>
  10. *
  11. * @author xhy-gitHub
  12. * @since 2022-03-15
  13. */
  14. public interface IUserService extends IService<User> {
  15. ResponseResult<?> findByAccount(UserVo userVo);
  16. }

UserServiceImpl

  1. package com.code.seckill.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  5. import com.code.seckill.mapper.UserMapper;
  6. import com.code.seckill.pojo.User;
  7. import com.code.seckill.service.IUserService;
  8. import com.code.seckill.util.MD5Utils;
  9. import com.code.seckill.util.ValidatorUtils;
  10. import com.code.seckill.util.response.ResponseResult;
  11. import com.code.seckill.util.response.ResponseResultCode;
  12. import com.code.seckill.vo.UserVo;
  13. import org.springframework.stereotype.Service;
  14. /**
  15. * <p>
  16. * 用户信息表 服务实现类
  17. * </p>
  18. *
  19. * @author xhy-gitHub
  20. * @since 2022-03-15
  21. */
  22. @Service
  23. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
  24. @Override
  25. public ResponseResult<?> findByAccount(UserVo userVo) {
  26. //先判断信息是否符合(账号是否是手机号,密码是不是空的)
  27. if (!ValidatorUtils.isMobile(userVo.getMobile())) {
  28. return ResponseResult.failure(ResponseResultCode.USER_ACCOUNT_NOT_MOBLIE);
  29. }
  30. if (!StringUtils.isBlank(userVo.getPassword())) {
  31. return ResponseResult.failure(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
  32. }
  33. // 再去数据库查出对应的用户(mobile)
  34. User user = this.getOne(new QueryWrapper<User>().eq("id", userVo.getMobile()));
  35. if (user == null) {
  36. return ResponseResult.failure(ResponseResultCode.USER_ACCOUNT_NOT_FIND);
  37. }
  38. // 比较密码
  39. // 二重加密(前端->后端,后端->数据库)
  40. String salt=user.getSalt();
  41. // 将前台的加密密码和后端的盐再次进行加密
  42. String newPassword= MD5Utils.formPassToDbPass(userVo.getPassword(),salt);
  43. if(!newPassword.equals(user.getPassword())){
  44. return ResponseResult.failure(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
  45. }
  46. return ResponseResult.success();
  47. }
  48. }

UserController

  1. package com.code.seckill.controller;
  2. import com.code.seckill.service.IUserService;
  3. import com.code.seckill.util.response.ResponseResult;
  4. import com.code.seckill.vo.UserVo;
  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. /**
  9. * <p>
  10. * 用户信息表 前端控制器
  11. * </p>
  12. *
  13. * @author xhy-gitHub
  14. * @since 2022-03-15
  15. */
  16. @RestController
  17. @RequestMapping("/user")
  18. public class UserController {
  19. @Autowired
  20. private IUserService userService;
  21. // 用户登录
  22. @RequestMapping("/login")
  23. public ResponseResult<?> login(UserVo userVo){
  24. // 调用service的登录验证
  25. return userService.findByAccount(userVo);
  26. }
  27. }

4、前端代码

将md5.js放到js文件中的project文件下

  1. /*
  2. * JavaScript MD5
  3. * https://github.com/blueimp/JavaScript-MD5
  4. *
  5. * Copyright 2011, Sebastian Tschan
  6. * https://blueimp.net
  7. *
  8. * Licensed under the MIT license:
  9. * https://opensource.org/licenses/MIT
  10. *
  11. * Based on
  12. * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
  13. * Digest Algorithm, as defined in RFC 1321.
  14. * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
  15. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  16. * Distributed under the BSD License
  17. * See http://pajhome.org.uk/crypt/md5 for more info.
  18. */
  19. /* global define */
  20. (function ($) {
  21. 'use strict'
  22. /*
  23. * Add integers, wrapping at 2^32. This uses 16-bit operations internally
  24. * to work around bugs in some JS interpreters.
  25. */
  26. function safeAdd (x, y) {
  27. var lsw = (x & 0xffff) + (y & 0xffff)
  28. var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
  29. return (msw << 16) | (lsw & 0xffff)
  30. }
  31. /*
  32. * Bitwise rotate a 32-bit number to the left.
  33. */
  34. function bitRotateLeft (num, cnt) {
  35. return (num << cnt) | (num >>> (32 - cnt))
  36. }
  37. /*
  38. * These functions implement the four basic operations the algorithm uses.
  39. */
  40. function md5cmn (q, a, b, x, s, t) {
  41. return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b)
  42. }
  43. function md5ff (a, b, c, d, x, s, t) {
  44. return md5cmn((b & c) | (~b & d), a, b, x, s, t)
  45. }
  46. function md5gg (a, b, c, d, x, s, t) {
  47. return md5cmn((b & d) | (c & ~d), a, b, x, s, t)
  48. }
  49. function md5hh (a, b, c, d, x, s, t) {
  50. return md5cmn(b ^ c ^ d, a, b, x, s, t)
  51. }
  52. function md5ii (a, b, c, d, x, s, t) {
  53. return md5cmn(c ^ (b | ~d), a, b, x, s, t)
  54. }
  55. /*
  56. * Calculate the MD5 of an array of little-endian words, and a bit length.
  57. */
  58. function binlMD5 (x, len) {
  59. /* append padding */
  60. x[len >> 5] |= 0x80 << (len % 32)
  61. x[((len + 64) >>> 9 << 4) + 14] = len
  62. var i
  63. var olda
  64. var oldb
  65. var oldc
  66. var oldd
  67. var a = 1732584193
  68. var b = -271733879
  69. var c = -1732584194
  70. var d = 271733878
  71. for (i = 0; i < x.length; i += 16) {
  72. olda = a
  73. oldb = b
  74. oldc = c
  75. oldd = d
  76. a = md5ff(a, b, c, d, x[i], 7, -680876936)
  77. d = md5ff(d, a, b, c, x[i + 1], 12, -389564586)
  78. c = md5ff(c, d, a, b, x[i + 2], 17, 606105819)
  79. b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330)
  80. a = md5ff(a, b, c, d, x[i + 4], 7, -176418897)
  81. d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426)
  82. c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341)
  83. b = md5ff(b, c, d, a, x[i + 7], 22, -45705983)
  84. a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416)
  85. d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417)
  86. c = md5ff(c, d, a, b, x[i + 10], 17, -42063)
  87. b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162)
  88. a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682)
  89. d = md5ff(d, a, b, c, x[i + 13], 12, -40341101)
  90. c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290)
  91. b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329)
  92. a = md5gg(a, b, c, d, x[i + 1], 5, -165796510)
  93. d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632)
  94. c = md5gg(c, d, a, b, x[i + 11], 14, 643717713)
  95. b = md5gg(b, c, d, a, x[i], 20, -373897302)
  96. a = md5gg(a, b, c, d, x[i + 5], 5, -701558691)
  97. d = md5gg(d, a, b, c, x[i + 10], 9, 38016083)
  98. c = md5gg(c, d, a, b, x[i + 15], 14, -660478335)
  99. b = md5gg(b, c, d, a, x[i + 4], 20, -405537848)
  100. a = md5gg(a, b, c, d, x[i + 9], 5, 568446438)
  101. d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690)
  102. c = md5gg(c, d, a, b, x[i + 3], 14, -187363961)
  103. b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501)
  104. a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467)
  105. d = md5gg(d, a, b, c, x[i + 2], 9, -51403784)
  106. c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473)
  107. b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734)
  108. a = md5hh(a, b, c, d, x[i + 5], 4, -378558)
  109. d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463)
  110. c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562)
  111. b = md5hh(b, c, d, a, x[i + 14], 23, -35309556)
  112. a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060)
  113. d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353)
  114. c = md5hh(c, d, a, b, x[i + 7], 16, -155497632)
  115. b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640)
  116. a = md5hh(a, b, c, d, x[i + 13], 4, 681279174)
  117. d = md5hh(d, a, b, c, x[i], 11, -358537222)
  118. c = md5hh(c, d, a, b, x[i + 3], 16, -722521979)
  119. b = md5hh(b, c, d, a, x[i + 6], 23, 76029189)
  120. a = md5hh(a, b, c, d, x[i + 9], 4, -640364487)
  121. d = md5hh(d, a, b, c, x[i + 12], 11, -421815835)
  122. c = md5hh(c, d, a, b, x[i + 15], 16, 530742520)
  123. b = md5hh(b, c, d, a, x[i + 2], 23, -995338651)
  124. a = md5ii(a, b, c, d, x[i], 6, -198630844)
  125. d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415)
  126. c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905)
  127. b = md5ii(b, c, d, a, x[i + 5], 21, -57434055)
  128. a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571)
  129. d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606)
  130. c = md5ii(c, d, a, b, x[i + 10], 15, -1051523)
  131. b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799)
  132. a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359)
  133. d = md5ii(d, a, b, c, x[i + 15], 10, -30611744)
  134. c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380)
  135. b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649)
  136. a = md5ii(a, b, c, d, x[i + 4], 6, -145523070)
  137. d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379)
  138. c = md5ii(c, d, a, b, x[i + 2], 15, 718787259)
  139. b = md5ii(b, c, d, a, x[i + 9], 21, -343485551)
  140. a = safeAdd(a, olda)
  141. b = safeAdd(b, oldb)
  142. c = safeAdd(c, oldc)
  143. d = safeAdd(d, oldd)
  144. }
  145. return [a, b, c, d]
  146. }
  147. /*
  148. * Convert an array of little-endian words to a string
  149. */
  150. function binl2rstr (input) {
  151. var i
  152. var output = ''
  153. var length32 = input.length * 32
  154. for (i = 0; i < length32; i += 8) {
  155. output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff)
  156. }
  157. return output
  158. }
  159. /*
  160. * Convert a raw string to an array of little-endian words
  161. * Characters >255 have their high-byte silently ignored.
  162. */
  163. function rstr2binl (input) {
  164. var i
  165. var output = []
  166. output[(input.length >> 2) - 1] = undefined
  167. for (i = 0; i < output.length; i += 1) {
  168. output[i] = 0
  169. }
  170. var length8 = input.length * 8
  171. for (i = 0; i < length8; i += 8) {
  172. output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32)
  173. }
  174. return output
  175. }
  176. /*
  177. * Calculate the MD5 of a raw string
  178. */
  179. function rstrMD5 (s) {
  180. return binl2rstr(binlMD5(rstr2binl(s), s.length * 8))
  181. }
  182. /*
  183. * Calculate the HMAC-MD5, of a key and some data (raw strings)
  184. */
  185. function rstrHMACMD5 (key, data) {
  186. var i
  187. var bkey = rstr2binl(key)
  188. var ipad = []
  189. var opad = []
  190. var hash
  191. ipad[15] = opad[15] = undefined
  192. if (bkey.length > 16) {
  193. bkey = binlMD5(bkey, key.length * 8)
  194. }
  195. for (i = 0; i < 16; i += 1) {
  196. ipad[i] = bkey[i] ^ 0x36363636
  197. opad[i] = bkey[i] ^ 0x5c5c5c5c
  198. }
  199. hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8)
  200. return binl2rstr(binlMD5(opad.concat(hash), 512 + 128))
  201. }
  202. /*
  203. * Convert a raw string to a hex string
  204. */
  205. function rstr2hex (input) {
  206. var hexTab = '0123456789abcdef'
  207. var output = ''
  208. var x
  209. var i
  210. for (i = 0; i < input.length; i += 1) {
  211. x = input.charCodeAt(i)
  212. output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f)
  213. }
  214. return output
  215. }
  216. /*
  217. * Encode a string as utf-8
  218. */
  219. function str2rstrUTF8 (input) {
  220. return unescape(encodeURIComponent(input))
  221. }
  222. /*
  223. * Take string arguments and return either raw or hex encoded strings
  224. */
  225. function rawMD5 (s) {
  226. return rstrMD5(str2rstrUTF8(s))
  227. }
  228. function hexMD5 (s) {
  229. return rstr2hex(rawMD5(s))
  230. }
  231. function rawHMACMD5 (k, d) {
  232. return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d))
  233. }
  234. function hexHMACMD5 (k, d) {
  235. return rstr2hex(rawHMACMD5(k, d))
  236. }
  237. function md5 (string, key, raw) {
  238. if (!key) {
  239. if (!raw) {
  240. return hexMD5(string)
  241. }
  242. return rawMD5(string)
  243. }
  244. if (!raw) {
  245. return hexHMACMD5(key, string)
  246. }
  247. return rawHMACMD5(key, string)
  248. }
  249. if (typeof define === 'function' && define.amd) {
  250. define(function () {
  251. return md5
  252. })
  253. } else if (typeof module === 'object' && module.exports) {
  254. module.exports = md5
  255. } else {
  256. $.md5 = md5
  257. }
  258. })(this)

前端密码进行加密

login.js(上面基础上)

  1. layui.define(()=>{
  2. // 得到layui中封装的jquery
  3. let $=layui.jquery
  4. let layer=layui.layer
  5. // 给登录按钮设置事件
  6. $(login).click(()=>{
  7. // 取到表单的值
  8. let mobile = $("#mobile").val();
  9. let password=$("#password").val();
  10. // 前端加密的盐
  11. let salt= "f1g2h3j4";
  12. if(password){
  13. // 将密码和盐混在一起
  14. password=salt.charAt(1) + "" + salt.charAt(5) + password + salt.charAt(0) + "" + salt.charAt(3);
  15. // 进行MD5加密
  16. password=md5(password)
  17. }
  18. console.log(password)
  19. // 将数据给后台(前后端分离axios,普通开发ajax)
  20. $.ajax({
  21. url:"/user/login",//后台登录接口
  22. data:{
  23. // 需要携带的数据
  24. mobile,
  25. password
  26. },
  27. datatype: "json",//后端给你的数据类型
  28. success(e){
  29. // 成功的回调函数
  30. layer.msg(e.message,{icon: 6});
  31. },
  32. error(e){
  33. // 报错的回调函数
  34. }
  35. })
  36. })
  37. })

 

 七、全局异常抓获

1、创建validate文件下自定义注解

IsMobile
  1. package com.code.seckill.util.validate;
  2. import com.code.seckill.util.response.ResponseResultCode;
  3. import javax.validation.Constraint;
  4. import javax.validation.Payload;
  5. import java.lang.annotation.*;
  6. @SuppressWarnings("all")
  7. @Documented
  8. @Constraint(
  9. validatedBy = {IsMobileValidator.class}
  10. )
  11. @Target({ElementType.FIELD, ElementType.PARAMETER})
  12. @Retention(RetentionPolicy.RUNTIME)
  13. public @interface IsMobile {
  14. ResponseResultCode code() default ResponseResultCode.UNKNOWN;
  15. String message() default "";
  16. Class<?>[] groups() default {};
  17. Class<? extends Payload>[] payload() default {};
  18. }
IsRequired
  1. package com.code.seckill.util.validate;
  2. import com.code.seckill.util.response.ResponseResultCode;
  3. import javax.validation.Constraint;
  4. import javax.validation.Payload;
  5. import java.lang.annotation.*;
  6. @SuppressWarnings("all")
  7. @Documented
  8. @Constraint(
  9. validatedBy = {IsRequiredValidator.class}
  10. )
  11. @Target({ElementType.FIELD, ElementType.PARAMETER})
  12. @Retention(RetentionPolicy.RUNTIME)
  13. public @interface IsRequired {
  14. ResponseResultCode code() default ResponseResultCode.UNKNOWN;
  15. String message() default "";
  16. Class<?>[] groups() default {};
  17. Class<? extends Payload>[] payload() default {};
  18. }

加入各自的解析器

IsMobileValidator
  1. package com.code.seckill.util.validate;
  2. import com.code.seckill.util.ValidatorUtils;
  3. import javax.validation.ConstraintValidator;
  4. import javax.validation.ConstraintValidatorContext;
  5. @SuppressWarnings("all")
  6. public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
  7. @Override
  8. public boolean isValid(String mobile, ConstraintValidatorContext context) {
  9. return ValidatorUtils.isMobile(mobile);
  10. }
  11. }
IsRequiredValidator
  1. package com.code.seckill.util.validate;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.apache.commons.lang3.StringUtils;
  4. import javax.validation.ConstraintValidator;
  5. import javax.validation.ConstraintValidatorContext;
  6. @SuppressWarnings("all")
  7. @Slf4j
  8. public class IsRequiredValidator implements ConstraintValidator<IsRequired, String> {
  9. @Override
  10. public boolean isValid(String str, ConstraintValidatorContext context) {
  11. return StringUtils.isNotBlank(str);
  12. }
  13. }

2、给Vo类加上注解(解析器)

  1. package com.code.seckill.vo;
  2. import com.code.seckill.util.response.ResponseResultCode;
  3. import com.code.seckill.util.validate.IsMobile;
  4. import com.code.seckill.util.validate.IsRequired;
  5. import lombok.Data;
  6. @Data
  7. public class UserVo {
  8. //手机号
  9. @IsMobile(code = ResponseResultCode.USER_ACCOUNT_NOT_FIND)
  10. private String mobile;
  11. //密码
  12. @IsRequired(code = ResponseResultCode.USER_CREDENTIAL_NOT_BE_EMPTY)
  13. private String password;
  14. }

3、创建response文件下的ThrowableAdvice(获取异常类)

  1. package com.code.seckill.util.response;
  2. import com.code.seckill.exception.BusinessException;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.ui.Model;
  5. import org.springframework.validation.BindException;
  6. import org.springframework.web.bind.annotation.ExceptionHandler;
  7. import org.springframework.web.bind.annotation.RestControllerAdvice;
  8. @SuppressWarnings("all")
  9. @RestControllerAdvice
  10. @Slf4j
  11. public class ThrowableAdvice {
  12. @JsonResponseResult
  13. @ExceptionHandler(value = {BusinessException.class})
  14. public ResponseResultCode globalBusinessException(Model m, Exception e) {
  15. log.error(e.toString());
  16. return ((BusinessException) e).getResponseResultCode();
  17. }
  18. @JsonResponseResult
  19. @ExceptionHandler(value = {BindException.class})
  20. public ResponseResultCode globalBindException(Model m, Exception e) {
  21. log.error(e.toString());
  22. BindException error = (BindException) e;
  23. return (ResponseResultCode) error.getFieldError().getArguments()[1];
  24. }
  25. @JsonResponseResult
  26. @ExceptionHandler(value = {Throwable.class})
  27. public ResponseResultCode globalException(Model m, Exception e) {
  28. log.error(e.toString());
  29. return ResponseResultCode.UNKNOWN;
  30. }
  31. }

4、在UserController类方法中加入注解开启jsr303验证

@Valid

5、在实现类抛出异常

  1. package com.code.seckill.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
  4. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  5. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  6. import com.code.seckill.exception.BusinessException;
  7. import com.code.seckill.mapper.UserMapper;
  8. import com.code.seckill.pojo.User;
  9. import com.code.seckill.service.IUserService;
  10. import com.code.seckill.util.MD5Utils;
  11. import com.code.seckill.util.ValidatorUtils;
  12. import com.code.seckill.util.response.ResponseResult;
  13. import com.code.seckill.util.response.ResponseResultCode;
  14. import com.code.seckill.vo.UserVo;
  15. import org.springframework.stereotype.Service;
  16. import java.util.Date;
  17. /**
  18. * <p>
  19. * 用户信息表 服务实现类
  20. * </p>
  21. *
  22. * @author xhy-gitHub
  23. * @since 2022-03-15
  24. */
  25. @Service
  26. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
  27. @Override
  28. public ResponseResult<?> findByAccount(UserVo userVo) {
  29. // 先判断信息是否符合(账号是否是手机号码,密码是不是空)
  30. if(!ValidatorUtils.isMobile(userVo.getMobile())){
  31. throw new BusinessException(ResponseResultCode.USER_ACCOUNT_NOT_MOBLIE);
  32. }
  33. if(StringUtils.isBlank(userVo.getPassword())){
  34. throw new BusinessException(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
  35. }
  36. // 再去数据库查出对应的用户(mobile)
  37. User user=this.getOne(new QueryWrapper<User>().eq("id",userVo.getMobile()));
  38. if(user==null){
  39. throw new BusinessException(ResponseResultCode.USER_ACCOUNT_NOT_FIND);
  40. }
  41. // 比较密码
  42. // 二重加密(前端->后端,后端->数据库)
  43. String salt=user.getSalt();
  44. // 将前台的加密密码和后端的盐再次进行加密
  45. String newPassword=MD5Utils.formPassToDbPass(userVo.getPassword(),salt);
  46. if(!newPassword.equals(user.getPassword())){
  47. throw new BusinessException(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
  48. }
  49. // 修改最后的登录时间
  50. this.update(new UpdateWrapper<User>().eq("id",userVo.getMobile()).set("last_login_date",new Date()).setSql("login_count=login_count+1"));
  51. return ResponseResult.success();
  52. }
  53. }

密码错误时不会报错

登陆模块完成!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/807335?site
推荐阅读
相关标签
  

闽ICP备14008679号