Java 登陆模块
爱嘤斯塔 人气:0一、项目前准备
1、新建项目
2、导入依赖
<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、执行sql脚本
得到表:
首先查看登录的表数据:
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目录
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 @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(",")); } }
相关文件生成:
自动生成时,mapper文件未加入注解,无法加载到spring容器中
@Repository
二、前端构建
1、导入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 // 给登录按钮设置事件 $(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; @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、导入帮助包与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; /** * 私有构造, 只允许通过static调用构造 * * @param resultCode 结果枚举 * @param data 响应数据 */ private ResponseResult(ResponseResultCode resultCode, T data) { this.code = resultCode.getCode(); this.message = resultCode.getMessage(); this.data = 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 <T> ResponseResult success(T data) { return new ResponseResult(ResponseResultCode.SUCCESS, data); } /** * 成功调用返回的结果(分页使用) * * @param data 携带的数据 * @param total 数据总条数 */ public static <T> ResponseResult success(T data, Long total) { return new ResponseResult(ResponseResultCode.SUCCESS, total, data); } /** * 失败调用返回的结果(数据携带) * * @param resultCode 状态枚举 * @param data 携带的数据 */ public static <T> 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 @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) * 服务端: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; @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> * 用户信息表 服务类 * </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> * 用户信息表 服务实现类 * </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); } // 再去数据库查出对应的用户(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){ // 调用service的登录验证 return userService.findByAccount(userVo); } }
4、密码加密
①、将md5.js放到js文件中
②、前端密码进行加密
login.js文件:
layui.use(["jquery","layer"],()=>{ // 得到layui中封装的jquery let $=layui.jquery let 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; /** * <p> * 用户信息表 服务实现类 * </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); } // 再去数据库查出对应的用户(mobile) User user=this.getOne(new QueryWrapper<User>().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(); } }
得到密钥,登录成功:
四、 全局异常抓获
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、导入帮助包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) { // 调用帮助类判断格式是否正确 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 { // 第一种方法 // 将你的结果直接封装为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(); // 找到该注解上的响应码并且返回 // 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、实现类抛出异常
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> * 用户信息表 服务实现类 * </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); // } // 再去数据库查出对应的用户(mobile) User user=this.getOne(new QueryWrapper<User>().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<User>().eq("id",userVo.getMobile()).set("last_login_date",new Date()).setSql("login_count=login_count+1")); return ResponseResult.success(); } }
密码错误时不会报错:
本期内容结束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
加载全部内容