超詳細(xì)講解Java秒殺項目登陸模塊的實現(xiàn)
一、項目前準(zhǔn)備
1、新建項目


2、導(dǎo)入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mybatis plus依賴 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis-plus生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<!-- MD5依賴 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- valid驗證依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--hariki 連接池-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>3、執(zhí)行sql腳本
得到表:

首先查看登錄的表數(shù)據(jù):

4、配置yml文件
spring:
application:
name: seckill
datasource:
url: jdbc:mysql://localhost:3306/seckill?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
hikari:
# 最小空閑連接數(shù)量
minimum-idle: 5
# 空閑連接存活最大時間,默認(rèn)600000(10分鐘)
idle-timeout: 180000
# 連接池最大連接數(shù),默認(rèn)是10
maximum-pool-size: 10
# 此屬性控制從池返回的連接的默認(rèn)自動提交行為,默認(rèn)值:true
auto-commit: true
# 連接池名稱
pool-name: MyHikariCP
# 此屬性控制池中連接的最長生命周期,值0表示無限生命周期,默認(rèn)1800000即30分鐘
max-lifetime: 1800000
# 數(shù)據(jù)庫連接超時時間,默認(rèn)30秒,即30000
connection-timeout: 30000
freemarker:
#設(shè)置編碼格式
charset: UTF-8
#后綴
suffix: .ftl
#文檔類型
content-type: text/html
#模板前端
template-loader-path: classpath:/templates/
#啟用模板
enabled: true
# 開啟靜態(tài)資源
mvc:
static-path-pattern: /static/**
mybatis-plus:
mapper-locations: classpath*:/mapper/*Mapper.xml
type-aliases-package: com.example.seckill.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.example.seckill.mapper: debug
由于2.4.1版本是沒有templates目錄,而我配置的模板的位置在此處,所以在resource內(nèi)新建templates目錄

5、在啟動類加入注解
SeckillApplication類:
//開啟切面
@EnableAspectJAutoProxy
//開啟事務(wù)
@EnableTransactionManagement
//掃描mapper層
@MapperScan("com.example.seckill.mapper")6、自動生成器
package com.example.seckill.generator;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("all")
@Slf4j
@Data
public class MybatisPlusGenerator {
protected static String URL = "jdbc:mysql://localhost:3306/seckill?useEncoding=utf8mb4&serverTimezone=Asia/Shanghai&useSSL=false";
protected static String USERNAME = "root";
protected static String PASSWORD = "123456";
protected static DataSourceConfig.Builder DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(URL, USERNAME, PASSWORD);
public static void main(String[] args) {
FastAutoGenerator.create(DATA_SOURCE_CONFIG)
.globalConfig(
(scanner/*lamdba*/, builder/*變量*/) ->
builder.author(scanner.apply("請輸入作者名稱?"))
.enableSwagger()
.fileOverride()
.outputDir(System.getProperty("user.dir") + "\\src\\main\\java")
.commentDate("yyyy-MM-dd")
.dateType(DateType.TIME_PACK)
)
.packageConfig((builder) ->
builder.parent("com.example.seckill")
.entity("pojo")
.service("service")
.serviceImpl("service.impl")
.mapper("mapper")
.xml("mapper.xml")
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "\\src\\main\\resources\\mapper"))
)
.injectionConfig((builder) ->
builder.beforeOutputFile(
(a, b) -> log.warn("tableInfo: " + a.getEntityName())
)
)
.strategyConfig((scanner, builder) ->
builder.addInclude(getTables(scanner.apply("請輸入表名,多個英文逗號分隔?所有輸入 all")))
.addTablePrefix("tb_", "t_")
.entityBuilder()
.enableChainModel()
.enableLombok()
.enableTableFieldAnnotation()
.addTableFills(
new Column("create_time", FieldFill.INSERT)
)
.controllerBuilder()
.enableRestStyle()
.enableHyphenStyle()
.build())
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}
}相關(guān)文件生成:

自動生成時,mapper文件未加入注解,無法加載到spring容器中
@Repository
二、前端構(gòu)建
1、導(dǎo)入layui

2、將界面放到template

head.ftl文件:
<meta charset="UTF-8">
<title>秒殺項目</title>
<script src="/static/asset/js/layui/layui.js" type="text/javascript"></script>
<link href="/static/asset/js/layui/css/layui.css" rel="stylesheet" type="text/css"/>
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
<#assign ctx>
${springMacroRequestContext.getContextPath()}
</#assign>goodsList.ftl文件:
<!DOCTYPE html>
<html lang="en">
<head>
<#include "../common/head.ftl">
</head>
<body>
<h1>這是商品展示界面</h1>
</body>
</html>login.ftl文件:
<!DOCTYPE html>
<html lang="zh">
<head>
<#include "common/head.ftl"/>
<style>
.layui-panel {
position: absolute;
width: 400px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 15px 15px 0px 15px;
border-radius: 20px;
}
.layui-form-label {
padding: 9px 0px;
}
h3 {
text-align: center;
line-height: 45px;
font-size: 40px;
color: white;
padding-bottom: 15px;
}
</style>
</head>
<body>
<div>
<div class="layui-panel layui-bg-cyan">
<h3>用戶登錄</h3>
<div class="layui-form-item">
<label class="layui-form-label">用戶賬號</label>
<div class="layui-input-block">
<input type="text" id="mobile" autocomplete="on" class="layui-input" value="18684671234">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">用戶密碼</label>
<div class="layui-input-block">
<input type="password" id="password" autocomplete="on" class="layui-input" value="123456">
</div>
</div>
<div class="layui-form-item" style="text-align:center;">
<button class="layui-btn" id="login" style="width:46%">登錄</button>
<button class="layui-btn layui-btn-normal" id="register" style="width:46%">注冊</button>
</div>
</div>
</div>
<script src="${ctx}/static/asset/js/md5.js"></script>
<script src="${ctx}/static/asset/js/project/login.js"></script>
</body>
</html>3、在js目錄下新建目錄project
新建JavaScript文件login:
layui.define(()=>{
// 得到layui中封裝的jquery
let $=layui.jquery
// 給登錄按鈕設(shè)置事件
$(login).click(()=>{
// 取到表單的值
let mobile = $("#mobile").val();
let password=$("#password").val();
// 將數(shù)據(jù)給后臺(前后端分離axios,普通開發(fā)ajax)
$.ajax({
url:"",//后臺登錄接口
data:{
// 需要攜帶的數(shù)據(jù)
mobile,
password
},
datatype: "json",//后端給你的數(shù)據(jù)類型
success(e){
// 成功的回調(diào)函數(shù)
},
error(e){
// 報錯的回調(diào)函數(shù)
}
})
})
})4、新建controller類
專門用于跳轉(zhuǎn)路徑:PathController類
package com.example.seckill.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PathController {
// 登錄跳首頁
@RequestMapping("/")
public String toPath(){
return "login";
}
// 跳所有二級頁面
@RequestMapping("/{dir}/{path}")
public String toPath(@PathVariable("dir") String dir,@PathVariable("path") String path){
return dir+"/"+path;
}
}得到界面:

三、MD5加密
1、導(dǎo)入幫助包與exception包
①、exception
package com.example.seckill.exception;
import com.example.seckill.util.response.ResponseResultCode;
import lombok.Data;
@SuppressWarnings("all")
@Data
public class BusinessException extends RuntimeException {
private ResponseResultCode responseResultCode;
public BusinessException(ResponseResultCode responseResultCode) {
this.responseResultCode = responseResultCode;
}
}②、response包,用于后面的全局異常
JsonResponseParse :
package com.example.seckill.util.response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@SuppressWarnings("all")
@RestControllerAdvice
@Slf4j
public class JsonResponseParse implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
//返回值決定他是否需要進入beforeBodyWrite
return methodParameter.getMethod().isAnnotationPresent(JsonResponseResult.class);
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//更改返回值
if (o == null) {
return ResponseResult.success();
}
if (o instanceof Integer) {
return ResponseResult.failure(ResponseResultCode.queryCode((Integer) o));
}
if (o instanceof ResponseResultCode) {
return ResponseResult.failure((ResponseResultCode) o);
}
if (o instanceof ResponseResult) {
return o;
}
return ResponseResult.success(o);
}
}JsonResponseResult :
package com.example.seckill.util.response;
import java.lang.annotation.*;
@SuppressWarnings("all")
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface JsonResponseResult {
}ResponseResult:
package com.example.seckill.util.response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseResult<T> implements Serializable {
private int code;
private String message;
private T data;
private Long total;
/**
* 私有構(gòu)造, 只允許通過static調(diào)用構(gòu)造
*
* @param resultCode 結(jié)果枚舉
* @param data 響應(yīng)數(shù)據(jù)
*/
private ResponseResult(ResponseResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.data = data;
}
/**
* 私有構(gòu)造, 只允許通過static調(diào)用構(gòu)造
*
* @param resultCode 結(jié)果枚舉
* @param data 響應(yīng)數(shù)據(jù)
*/
private ResponseResult(ResponseResultCode resultCode, Long total, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.data = data;
this.total = total;
}
/**
* 成功調(diào)用返回的結(jié)果(無數(shù)據(jù)攜帶)
*/
public static ResponseResult success() {
return success(null);
}
/**
* 成功調(diào)用返回的結(jié)果(數(shù)據(jù)攜帶)
*
* @param data 攜帶的數(shù)據(jù)
*/
public static <T> ResponseResult success(T data) {
return new ResponseResult(ResponseResultCode.SUCCESS, data);
}
/**
* 成功調(diào)用返回的結(jié)果(分頁使用)
*
* @param data 攜帶的數(shù)據(jù)
* @param total 數(shù)據(jù)總條數(shù)
*/
public static <T> ResponseResult success(T data, Long total) {
return new ResponseResult(ResponseResultCode.SUCCESS, total, data);
}
/**
* 失敗調(diào)用返回的結(jié)果(數(shù)據(jù)攜帶)
*
* @param resultCode 狀態(tài)枚舉
* @param data 攜帶的數(shù)據(jù)
*/
public static <T> ResponseResult failure(ResponseResultCode resultCode, T data) {
return new ResponseResult(resultCode, data);
}
/**
* 失敗調(diào)用返回的結(jié)果(無數(shù)據(jù)攜帶)
*
* @param resultCode 狀態(tài)枚舉
*/
public static ResponseResult failure(ResponseResultCode resultCode) {
return failure(resultCode, null);
}
}ResponseResultCode :
package com.example.seckill.util.response;
import java.io.Serializable;
@SuppressWarnings("all")
public enum ResponseResultCode implements Serializable {
/* 正常狀態(tài) */
SUCCESS(200, "成功"),
FAILURE(300, "失敗"),
UNKNOWN(400, "未知錯誤"),
/**
* 用戶code范圍: 1000;
*/
USER_ACCOUNT_NOT_FIND(1001, "用戶名不存在"),
USER_ACCOUNT_DISABLED(1002, "該用戶已被禁用"),
USER_PASSWORD_NOT_MATCH(1003, "該用戶密碼不一致"),
USER_PERMISSION_ERROR(1004, "該用戶不具備訪問權(quán)限"),
USER_STATE_OFF_LINE(1005, "該用戶未登錄"),
USER_CREDENTIAL_NOT_BE_EMPTY(1006, "用戶的登錄信息不能為空值"),
USER_ACCOUNT_NOT_MOBLIE(1007, "該用戶登錄信息格式不符合"),
USER_LOGIN_ERROR(1008, "登錄失敗"),
/**
* 其它異常: 4000;
*/
TICKET_ERROR(4001, "TICKET失效,請重新登錄"),
/**
* 商品異常: 6000;
*/
GOODS_ADD_ERROR(6001, "商品添加失敗"),
GOODS_EDIT_ERROR(6002, "商品修改失敗"),
GOODS_REMOVE_ERROR(6003, "商品刪除失敗"),
/**
* 秒殺商品異常: 8000
*/
SECKILL_GOODS_ADD_ERROR(8001, "秒殺商品增加失敗"),
/**
* 秒殺訂單異常: 10000
*/
SECKILL_ORDER_ERROR(10001, "秒殺訂單增加失敗"),
SECKILL_ORDER_QUANTITY_ERROR(10002, "秒殺商品數(shù)量不足"),
SECKILL_ORDER_EXISTS_ERROR(10002, "秒殺訂單已經(jīng)存在"),
;
private final Integer code;
private final String message;
ResponseResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public static ResponseResultCode queryCode(Integer code) {
for (ResponseResultCode value : values()) {
if (code.equals(value.code)) {
return value;
}
}
return UNKNOWN;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}RestThrowableAdvice :
package com.example.seckill.util.response;
import com.example.seckill.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Arrays;
@SuppressWarnings("all")
@RestControllerAdvice
@Slf4j
public class RestThrowableAdvice {
@JsonResponseResult
@ExceptionHandler(value = {BusinessException.class})
public Object globalBusinessException(Model m, Exception e) {
log.error(e.toString());
return ((BusinessException) e).getResponseResultCode();
}
@JsonResponseResult
@ExceptionHandler(value = {BindException.class})
public Object globalBindException(Model m, Exception e) {
log.error(e.toString());
BindException error = (BindException) e;
return Arrays
.stream(error.getFieldError().getArguments())
.filter(arg -> arg instanceof ResponseResultCode)
.findAny()
.orElse(ResponseResultCode.UNKNOWN);
}
@JsonResponseResult
@ExceptionHandler(value = {Throwable.class})
public Object globalException(Model m, Exception e) {
log.error(e.toString());
return ResponseResultCode.UNKNOWN;
}
}③、MD5Utils
package com.example.seckill.util;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
import java.util.UUID;
/**
* MD5加密
* 用戶端:password=MD5(明文+固定Salt)
* 服務(wù)端:password=MD5(用戶輸入+隨機Salt)
* 用戶端MD5加密是為了防止用戶密碼在網(wǎng)絡(luò)中明文傳輸,服務(wù)端MD5加密是為了提高密碼安全性,雙重保險。
*/
@Component
@SuppressWarnings("all")
public class MD5Utils {
//加密鹽,與前端一致
private static String salt = "f1g2h3j4";
/**
* md5加密
*
* @param src
* @return
*/
public static String md5(String src) {
return DigestUtils.md5Hex(src);
}
/**
* 獲取加密的鹽
*
* @return
*/
public static String createSalt() {
return UUID.randomUUID().toString().replace("-", "");
}
/**
* 將前端的明文密碼通過MD5加密方式加密成后端服務(wù)所需密碼
* 注意:該步驟實際是在前端完成!??!
*
* @param inputPass 明文密碼
* @return
*/
public static String inputPassToFormpass(String inputPass) {
//混淆固定鹽salt,安全性更可靠
String str = salt.charAt(1) + "" + salt.charAt(5) + inputPass + salt.charAt(0) + "" + salt.charAt(3);
return md5(str);
}
/**
* 將后端密文密碼+隨機salt生成數(shù)據(jù)庫的密碼
*
* @param formPass
* @param salt
* @return
*/
public static String formPassToDbPass(String formPass, String salt) {
//混淆固定鹽salt,安全性更可靠
String str = salt.charAt(7) + "" + salt.charAt(9) + formPass + salt.charAt(1) + "" + salt.charAt(5);
return md5(str);
}
/**
* 將用戶輸入的密碼轉(zhuǎn)換成數(shù)據(jù)庫的密碼
*
* @param inputPass 明文密碼
* @param salt 鹽
* @return
*/
public static String inputPassToDbPass(String inputPass, String salt) {
String formPass = inputPassToFormpass(inputPass);
String dbPass = formPassToDbPass(formPass, salt);
return dbPass;
}
public static void main(String[] args) {
String formPass = inputPassToFormpass("123456");
System.out.println("前端加密密碼:" + formPass);
String salt = createSalt();
System.out.println("后端加密隨機鹽:" + salt);
String dbPass = formPassToDbPass(formPass, salt);
System.out.println("后端加密密碼:" + dbPass);
String dbPass1 = inputPassToDbPass("123456", salt);
System.out.println("最終加密密碼:" + dbPass1);
}
}ValidatorUtils :
package com.example.seckill.util;
import org.apache.commons.lang3.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@SuppressWarnings("all")
public class ValidatorUtils {
private static final Pattern mobile_pattern = Pattern.compile("[1]([0-9])[0-9]{9}$");
public static boolean isMobile(String mobile) {
if (StringUtils.isEmpty(mobile)) {
return false;
}
Matcher matcher = mobile_pattern.matcher(mobile);
return matcher.matches();
}
}2、新建vo類
用于前后端傳值:
package com.example.seckill.vo;
import lombok.Data;
@Data
public class UserVo {
// 手機號
private String mobile;
// 密碼
private String password;
}3、登錄方法:
IUserService層:
package com.example.seckill.service;
import com.example.seckill.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.vo.UserVo;
/**
* <p>
* 用戶信息表 服務(wù)類
* </p>
*
* @author lv
* @since 2022-03-15
*/
public interface IUserService extends IService<User> {
ResponseResult<?> findByAccount(UserVo userVo);
}UserServiceImpl類:
package com.example.seckill.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.seckill.pojo.User;
import com.example.seckill.mapper.UserMapper;
import com.example.seckill.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.seckill.util.ValidatorUtils;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.util.response.ResponseResultCode;
import com.example.seckill.vo.UserVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* <p>
* 用戶信息表 服務(wù)實現(xiàn)類
* </p>
*
* @author lv
* @since 2022-03-15
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public ResponseResult<?> findByAccount(UserVo userVo) {
// 先判斷信息是否符合(賬號是否是手機號碼,密碼是不是空)
if(!ValidatorUtils.isMobile(userVo.getMobile())){
return ResponseResult.failure(ResponseResultCode.USER_ACCOUNT_NOT_MOBLIE);
}
if(!StringUtils.isBlank(userVo.getPassword())){
return ResponseResult.failure(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
}
// 再去數(shù)據(jù)庫查出對應(yīng)的用戶(mobile)
User user=this.getOne(new QueryWrapper<User>().eq("id",userVo.getMobile()));
if(user==null){
return ResponseResult.failure(ResponseResultCode.USER_ACCOUNT_NOT_FIND);
}
// 比較密碼
if(userVo.getPassword().equals(user.getPassword())){
return ResponseResult.failure(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
}
return ResponseResult.success();
}
}UserController類:
package com.example.seckill.controller;
import com.example.seckill.service.IUserService;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 用戶信息表 前端控制器
* </p>
*
* @author lv
* @since 2022-03-15
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
// 用戶登錄
@RequestMapping("/login")
public ResponseResult<?> login(UserVo userVo){
// 調(diào)用service的登錄驗證
return userService.findByAccount(userVo);
}
}4、密碼加密
①、將md5.js放到j(luò)s文件中
②、前端密碼進行加密
login.js文件:
layui.use(["jquery","layer"],()=>{
// 得到layui中封裝的jquery
let $=layui.jquery
let layer=layui.layer
// 給登錄按鈕設(shè)置事件
$(login).click(()=>{
// 取到表單的值
let mobile = $("#mobile").val();
let password=$("#password").val();
// 前端加密的鹽
let salt= "f1g2h3j4";
if(password){
// 將密碼和鹽混在一起
password=salt.charAt(1) + "" + salt.charAt(5) + password + salt.charAt(0) + "" + salt.charAt(3);
// 進行MD5加密
password=md5(password)
}
console.log(password)
// 將數(shù)據(jù)給后臺(前后端分離axios,普通開發(fā)ajax)
$.ajax({
url:"/user/login",//后臺登錄接口
data:{
// 需要攜帶的數(shù)據(jù)
mobile,
password
},
datatype: "json",//后端給你的數(shù)據(jù)類型
success(e){
// 成功的回調(diào)函數(shù)
layer.msg(e.message,{icon: 6});
},
error(e){
// 報錯的回調(diào)函數(shù)
}
})
})
})UserServiceImpl文件:
package com.example.seckill.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.seckill.pojo.User;
import com.example.seckill.mapper.UserMapper;
import com.example.seckill.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.seckill.util.MD5Utils;
import com.example.seckill.util.ValidatorUtils;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.util.response.ResponseResultCode;
import com.example.seckill.vo.UserVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* <p>
* 用戶信息表 服務(wù)實現(xiàn)類
* </p>
*
* @author lv
* @since 2022-03-15
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public ResponseResult<?> findByAccount(UserVo userVo) {
// 先判斷信息是否符合(賬號是否是手機號碼,密碼是不是空)
if(!ValidatorUtils.isMobile(userVo.getMobile())){
return ResponseResult.failure(ResponseResultCode.USER_ACCOUNT_NOT_MOBLIE);
}
if(StringUtils.isBlank(userVo.getPassword())){
return ResponseResult.failure(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
}
// 再去數(shù)據(jù)庫查出對應(yīng)的用戶(mobile)
User user=this.getOne(new QueryWrapper<User>().eq("id",userVo.getMobile()));
if(user==null){
return ResponseResult.failure(ResponseResultCode.USER_ACCOUNT_NOT_FIND);
}
// 比較密碼
// 二重加密(前端->后端,后端->數(shù)據(jù)庫)
String salt=user.getSalt();
// 將前臺的加密密碼和后端的鹽再次進行加密
String newPassword=MD5Utils.formPassToDbPass(userVo.getPassword(),salt);
if(!newPassword.equals(user.getPassword())){
return ResponseResult.failure(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
}
return ResponseResult.success();
}
}得到密鑰,登錄成功:


四、 全局異常抓獲
1、給實體類userVo加入注解
package com.example.seckill.vo;
import com.example.seckill.util.response.ResponseResultCode;
import com.example.seckill.util.validate.IsMobile;
import com.example.seckill.util.validate.IsRequired;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
public class UserVo {
// 手機號
@IsMobile(code = ResponseResultCode.USER_ACCOUNT_NOT_FIND)
private String mobile;
// 密碼
@IsRequired(code = ResponseResultCode.USER_CREDENTIAL_NOT_BE_EMPTY)
private String password;
}2、導(dǎo)入幫助包validate,異常抓獲
IsMobile:
package com.example.seckill.util.validate;
import com.example.seckill.util.response.ResponseResultCode;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@SuppressWarnings("all")
@Documented
@Constraint(
// 告知使用哪個解析器
validatedBy = {IsMobileValidator.class}
)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsMobile {
ResponseResultCode code() default ResponseResultCode.UNKNOWN;
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}IsMobileValidator:
package com.example.seckill.util.validate;
import com.example.seckill.util.ValidatorUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
//IsMobile的解析類
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
@Override
public boolean isValid(String mobile, ConstraintValidatorContext context) {
// 調(diào)用幫助類判斷格式是否正確
return ValidatorUtils.isMobile(mobile);
}
}IsRequired:
package com.example.seckill.util.validate;
import com.example.seckill.util.response.ResponseResultCode;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@SuppressWarnings("all")
@Documented
@Constraint(
// 告知使用哪個解析器
validatedBy = {IsRequiredValidator.class}
)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsRequired {
ResponseResultCode code() default ResponseResultCode.UNKNOWN;
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}IsRequiredValidator:
package com.example.seckill.util.validate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@Slf4j
public class IsRequiredValidator implements ConstraintValidator<IsRequired, String> {
@Override
public boolean isValid(String str, ConstraintValidatorContext context) {
return StringUtils.isNotBlank(str);
}
}ThrowableAdvice:
package com.example.seckill.util.response;
import com.example.seckill.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局異常
*/
//RestController的增強類
@RestControllerAdvice
@Slf4j
//抓取異常
public class ThrowableAdvice {
// 第一種方法
// 將你的結(jié)果直接封裝為ResponseResult
@JsonResponseResult
// 抓捕一個異常
@ExceptionHandler(value = {BusinessException.class})
public ResponseResultCode globalBusinessException(Model m, Exception e) {
log.error(e.toString());
e.printStackTrace();
return ((BusinessException) e).getResponseResultCode();
}
// 第二種方法
//@JsonResponseResult
//@ExceptionHandler(value = {BusinessException.class})
//public Object globalBusinessException(Model m, Exception e) {
// Object[] arguments=((BindException)e).getFieldError().getArguments();
// 找到該注解上的響應(yīng)碼并且返回
// return Arrays.stream(arguments)
// .filter(t->t instanceof ResponseResultCode)
// .findAny()
// .orElse(ResponseResultCode.UNKNOWN);
//}
@JsonResponseResult
@ExceptionHandler(value = {BindException.class})
public ResponseResultCode globalBindException(Model m, Exception e) {
log.error(e.toString());
BindException error = (BindException) e;
e.printStackTrace();
return (ResponseResultCode) error.getFieldError().getArguments()[1];
}
@JsonResponseResult
@ExceptionHandler(value = {Throwable.class})
public ResponseResultCode globalException(Model m, Exception e) {
log.error(e.toString());
e.printStackTrace();
return ResponseResultCode.UNKNOWN;
}
}3、在UserController類方法中加入注解
開啟jsr303驗證
@Valid

4、實現(xiàn)類拋出異常
package com.example.seckill.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.example.seckill.exception.BusinessException;
import com.example.seckill.pojo.User;
import com.example.seckill.mapper.UserMapper;
import com.example.seckill.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.seckill.util.MD5Utils;
import com.example.seckill.util.ValidatorUtils;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.util.response.ResponseResultCode;
import com.example.seckill.vo.UserVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* <p>
* 用戶信息表 服務(wù)實現(xiàn)類
* </p>
*
* @author lv
* @since 2022-03-15
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public ResponseResult<?> findByAccount(UserVo userVo) {
// 先判斷信息是否符合(賬號是否是手機號碼,密碼是不是空)
// 由于UserVo加入了注解,就無需拋出異常
// if(!ValidatorUtils.isMobile(userVo.getMobile())){
// throw new BusinessException(ResponseResultCode.USER_ACCOUNT_NOT_MOBLIE);
// }
// if(StringUtils.isBlank(userVo.getPassword())){
// throw new BusinessException(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
// }
// 再去數(shù)據(jù)庫查出對應(yīng)的用戶(mobile)
User user=this.getOne(new QueryWrapper<User>().eq("id",userVo.getMobile()));
if(user==null){
throw new BusinessException(ResponseResultCode.USER_ACCOUNT_NOT_FIND);
}
// 比較密碼
// 二重加密(前端->后端,后端->數(shù)據(jù)庫)
String salt=user.getSalt();
// 將前臺的加密密碼和后端的鹽再次進行加密
String newPassword=MD5Utils.formPassToDbPass(userVo.getPassword(),salt);
if(!newPassword.equals(user.getPassword())){
throw new BusinessException(ResponseResultCode.USER_PASSWORD_NOT_MATCH);
}
// 修改最后的登錄時間
this.update(new UpdateWrapper<User>().eq("id",userVo.getMobile()).set("last_login_date",new Date()).setSql("login_count=login_count+1"));
return ResponseResult.success();
}
}密碼錯誤時不會報錯:

本期內(nèi)容結(jié)束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
到此這篇關(guān)于超詳細(xì)講解Java秒殺項目登陸模塊的實現(xiàn)的文章就介紹到這了,更多相關(guān)Java 登陸模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中使用輾轉(zhuǎn)相除法求最大公約數(shù)
這篇文章主要介紹了Java中使用輾轉(zhuǎn)相除法求最大公約數(shù),本文直接給出代碼實例,需要的朋友可以參考下2015-05-05
java中實現(xiàn)對象排序的兩種方法(Comparable,Comparator)
這篇文章主要給大家介紹了關(guān)于java中實現(xiàn)對象排序的兩種方法,一種是實現(xiàn)Comparable進行排序,另一種是實現(xiàn)Comparator進行排序,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
freemarker?jsp?java內(nèi)存方式實現(xiàn)分頁示例
這篇文章主要介紹了freemarker?jsp?java內(nèi)存方式實現(xiàn)分頁示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06

