超详细讲解Java秒杀项目登陆模块的实现

目录

  • 一、项目前准备
    • 1、新建项目
    • 2、导入依赖
    • 3、执行sql脚本
    • 4、配置yml文件
    • 5、在启动类加入注解
    • 6、自动生成器
  • 二、前端构建
    • 1、导入layui
    • 2、将界面放到template
    • 3、在js目录下新建目录project
    • 4、新建controller类
  • 三、MD5加密
    • 1、导入帮助包与exception包
    • 2、新建vo类
    • 3、登录方法:
    • 4、密码加密
  • 四、 全局异常抓获
    • 1、给实体类userVo加入注解
    • 2、导入帮助包validate,异常抓获
    • 3、在UserController类方法中加入注解
    • 4、实现类抛出异常

一、项目前准备
1、新建项目
超详细讲解Java秒杀项目登陆模块的实现
文章图片

超详细讲解Java秒杀项目登陆模块的实现
文章图片


2、导入依赖
org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-aoporg.springframework.bootspring-boot-starter-testtestorg.springframework.bootspring-boot-starter-freemarkerorg.springframework.bootspring-boot-starter-webmysqlmysql-connector-javaruntimeorg.projectlomboklomboktruecom.baomidoumybatis-plus-boot-starter3.5.1com.baomidoumybatis-plus-generator3.5.2commons-codeccommons-codecorg.apache.commonscommons-lang33.12.0org.springframework.bootspring-boot-starter-validationorg.springframework.bootspring-boot-starter-data-rediscom.zaxxerHikariCPio.springfoxspringfox-swagger22.9.2io.springfoxspringfox-swagger-ui2.9.2


3、执行sql脚本
得到表:
超详细讲解Java秒杀项目登陆模块的实现
文章图片

首先查看登录的表数据:
超详细讲解Java秒杀项目登陆模块的实现
文章图片


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:
# 最小空闲连接数量
minimum-idle: 5
# 空闲连接存活最大时间,默认600000(10分钟)
idle-timeout: 180000
# 连接池最大连接数,默认是10
maximum-pool-size: 10
# 此属性控制从池返回的连接的默认自动提交行为,默认值:true
auto-commit: true
# 连接池名称
pool-name: MyHikariCP
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
max-lifetime: 1800000
# 数据库连接超时时间,默认30秒,即30000
connection-timeout: 30000
freemarker:
#设置编码格式
charset: UTF-8
#后缀
suffix: .ftl
#文档类型
content-type: text/html
#模板前端
template-loader-path: classpath:/templates/
#启用模板
enabled: true
# 开启静态资源
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内新建templates目录
超详细讲解Java秒杀项目登陆模块的实现
文章图片


5、在启动类加入注解
SeckillApplication类:
//开启切面@EnableAspectJAutoProxy//开启事务@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@Datapublic 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 getTables(String tables) {return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(",")); } }

相关文件生成:
超详细讲解Java秒杀项目登陆模块的实现
文章图片

自动生成时,mapper文件未加入注解,无法加载到spring容器中
@Repository


二、前端构建
1、导入layui
超详细讲解Java秒杀项目登陆模块的实现
文章图片


2、将界面放到template
超详细讲解Java秒杀项目登陆模块的实现
文章图片

head.ftl文件:
秒杀项目<#assign ctx>${springMacroRequestContext.getContextPath()}

goodsList.ftl文件:
<#include "../common/head.ftl">这是商品展示界面

login.ftl文件:
<#include "common/head.ftl"/>.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; }用户登录


3、在js目录下新建目录project
新建JavaScript文件login:
layui.define(()=>{// 得到layui中封装的jquerylet $=layui.jquery// 给登录按钮设置事件$(login).click(()=>{// 取到表单的值let mobile = $("#mobile").val(); let password=$("#password").val(); // 将数据给后台(前后端分离axios,普通开发ajax)$.ajax({url:"",//后台登录接口data:{// 需要携带的数据mobile,password},datatype: "json",//后端给你的数据类型success(e){// 成功的回调函数 },error(e){// 报错的回调函数 } })}) })


4、新建controller类
专门用于跳转路径: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; @Controllerpublic 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; } }

得到界面:
超详细讲解Java秒杀项目登陆模块的实现
文章图片


三、MD5加密
1、导入帮助包与exception包
①、exception
package com.example.seckill.exception; import com.example.seckill.util.response.ResponseResultCode; import lombok.Data; @SuppressWarnings("all")@Datapublic 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@Slf4jpublic class JsonResponseParse implements ResponseBodyAdvice { @Overridepublic boolean supports(MethodParameter methodParameter, Class aClass) {//返回值决定他是否需要进入beforeBodyWritereturn methodParameter.getMethod().isAnnotationPresent(JsonResponseResult.class); } @Overridepublic 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 = https://www.it610.com/article/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@AllArgsConstructorpublic class ResponseResult implements Serializable { private int code; private String message; private T data; private Long total; /*** 私有构造, 只允许通过static调用构造** @param resultCode 结果枚举* @param data响应数据*/private ResponseResult(ResponseResultCode resultCode, T data) {this.code = resultCode.getCode(); this.message = resultCode.getMessage(); this.data = https://www.it610.com/article/data; } /*** 私有构造, 只允许通过static调用构造** @param resultCode 结果枚举* @param data响应数据*/private ResponseResult(ResponseResultCode resultCode, Long total, T data) {this.code = resultCode.getCode(); this.message = resultCode.getMessage(); this.data = data; this.total = total; } /*** 成功调用返回的结果(无数据携带)*/public static ResponseResult success() {return success(null); } /*** 成功调用返回的结果(数据携带)** @param data 携带的数据*/public static ResponseResult success(T data) {return new ResponseResult(ResponseResultCode.SUCCESS, data); } /*** 成功调用返回的结果(分页使用)** @param data携带的数据* @param total 数据总条数*/public static ResponseResult success(T data, Long total) {return new ResponseResult(ResponseResultCode.SUCCESS, total, data); } /*** 失败调用返回的结果(数据携带)** @param resultCode 状态枚举* @param data携带的数据*/public static ResponseResult failure(ResponseResultCode resultCode, T data) {return new ResponseResult(resultCode, data); } /*** 失败调用返回的结果(无数据携带)** @param resultCode 状态枚举*/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 { /* 正常状态 */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, "该用户不具备访问权限"),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, "秒杀商品数量不足"),SECKILL_ORDER_EXISTS_ERROR(10002, "秒杀订单已经存在"),; 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@Slf4jpublic class RestThrowableAdvice { @JsonResponseResult@ExceptionHandler(value = https://www.it610.com/article/{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 = https://www.it610.com/article/{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) * 服务端:password=MD5(用户输入+随机Salt) * 用户端MD5加密是为了防止用户密码在网络中明文传输,服务端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加密方式加密成后端服务所需密码* 注意:该步骤实际是在前端完成!!!** @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生成数据库的密码** @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); } /*** 将用户输入的密码转换成数据库的密码** @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; @Datapublic 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; /** * * 用户信息表 服务类 *
* * @author lv * @since 2022-03-15 */public interface IUserService extends IService { 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; /** * * 用户信息表 服务实现类 *
* * @author lv * @since 2022-03-15 */@Servicepublic class UserServiceImpl extends ServiceImpl implements IUserService { @Overridepublic 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); }//再去数据库查出对应的用户(mobile)User user=this.getOne(new QueryWrapper().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; /** * * 用户信息表 前端控制器 *
* * @author lv * @since 2022-03-15 */@RestController@RequestMapping("/user")public class UserController { @Autowiredprivate IUserService userService; //用户登录@RequestMapping("/login") public ResponseResult login(UserVo userVo){//调用service的登录验证return userService.findByAccount(userVo); } }


4、密码加密
①、将md5.js放到js文件中
②、前端密码进行加密
login.js文件:
layui.use(["jquery","layer"],()=>{// 得到layui中封装的jquerylet $=layui.jquerylet layer=layui.layer// 给登录按钮设置事件$(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)// 将数据给后台(前后端分离axios,普通开发ajax)$.ajax({url:"/user/login",//后台登录接口data:{// 需要携带的数据mobile,password},datatype: "json",//后端给你的数据类型success(e){// 成功的回调函数layer.msg(e.message,{icon: 6}); },error(e){// 报错的回调函数}})})})

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; /** * * 用户信息表 服务实现类 *
* * @author lv * @since 2022-03-15 */@Servicepublic class UserServiceImpl extends ServiceImpl implements IUserService {@Overridepublic 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); }//再去数据库查出对应的用户(mobile)User user=this.getOne(new QueryWrapper().eq("id",userVo.getMobile())); if(user==null){return ResponseResult.failure(ResponseResultCode.USER_ACCOUNT_NOT_FIND); }//比较密码//二重加密(前端->后端,后端->数据库)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(); }}

得到密钥,登录成功:
超详细讲解Java秒杀项目登陆模块的实现
文章图片

超详细讲解Java秒杀项目登陆模块的实现
文章图片


四、 全局异常抓获 【超详细讲解Java秒杀项目登陆模块的实现】
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; @Datapublic class UserVo { //手机号@IsMobile(code = ResponseResultCode.USER_ACCOUNT_NOT_FIND)private String mobile; //密码@IsRequired(code = ResponseResultCode.USER_CREDENTIAL_NOT_BE_EMPTY)private String password; }


2、导入帮助包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[] 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 { @Overridepublic boolean isValid(String mobile, ConstraintValidatorContext context) {//调用帮助类判断格式是否正确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[] 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; @Slf4jpublic class IsRequiredValidator implements ConstraintValidator { @Overridepublic 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 { //第一种方法//将你的结果直接封装为ResponseResult@JsonResponseResult//抓捕一个异常@ExceptionHandler(value = https://www.it610.com/article/{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(); //找到该注解上的响应码并且返回//return Arrays.stream(arguments)//.filter(t->t instanceof ResponseResultCode)//.findAny()//.orElse(ResponseResultCode.UNKNOWN); //}@JsonResponseResult@ExceptionHandler(value = https://www.it610.com/article/{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

超详细讲解Java秒杀项目登陆模块的实现
文章图片


4、实现类抛出异常
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; /** * * 用户信息表 服务实现类 *
* * @author lv * @since 2022-03-15 */@Servicepublic class UserServiceImpl extends ServiceImpl implements IUserService {@Overridepublic 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); //}//再去数据库查出对应的用户(mobile)User user=this.getOne(new QueryWrapper().eq("id",userVo.getMobile())); if(user==null){throw new BusinessException(ResponseResultCode.USER_ACCOUNT_NOT_FIND); }//比较密码//二重加密(前端->后端,后端->数据库)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().eq("id",userVo.getMobile()).set("last_login_date",new Date()).setSql("login_count=login_count+1")); return ResponseResult.success(); }}

密码错误时不会报错:
超详细讲解Java秒杀项目登陆模块的实现
文章图片

本期内容结束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
到此这篇关于超详细讲解Java秒杀项目登陆模块的实现的文章就介绍到这了,更多相关Java 登陆模块内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    推荐阅读