超詳細講解Java秒殺項目登陸模塊的實現(xià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
# 空閑連接存活最大時間,默認600000(10分鐘)
idle-timeout: 180000
# 連接池最大連接數(shù),默認是10
maximum-pool-size: 10
# 此屬性控制從池返回的連接的默認自動提交行為,默認值:true
auto-commit: true
# 連接池名稱
pool-name: MyHikariCP
# 此屬性控制池中連接的最長生命周期,值0表示無限生命周期,默認1800000即30分鐘
max-lifetime: 1800000
# 數(shù)據(jù)庫連接超時時間,默認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)于超詳細講解Java秒殺項目登陸模塊的實現(xiàn)的文章就介紹到這了,更多相關(guān)Java 登陸模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中使用輾轉(zhuǎn)相除法求最大公約數(shù)
這篇文章主要介紹了Java中使用輾轉(zhuǎn)相除法求最大公約數(shù),本文直接給出代碼實例,需要的朋友可以參考下2015-05-05java中實現(xiàn)對象排序的兩種方法(Comparable,Comparator)
這篇文章主要給大家介紹了關(guān)于java中實現(xiàn)對象排序的兩種方法,一種是實現(xiàn)Comparable進行排序,另一種是實現(xiàn)Comparator進行排序,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-12-12freemarker?jsp?java內(nèi)存方式實現(xiàn)分頁示例
這篇文章主要介紹了freemarker?jsp?java內(nèi)存方式實現(xiàn)分頁示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06