超详细讲解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、新建项目

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();
    }
}

密码错误时不会报错:

本期内容结束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

(0)

相关推荐

  • Java使用正则表达式验证用户名和密码的方法

    要求: 用户名: 必须是6-10位字母.数字.下划线(这里字母.数字.下划线是指任意组合,没有必须三类均包含) 不能以数字开头 密码: 必须是6-20位的字母.数字.下划线(这里字母.数字.下划线是指任意组合,没有必须三类均包含) 效果展示: package cn.zyq.Aug05; import java.util.Scanner; public class Demo1 { public static void main(String[] args) { Scanner sc = new S

  • java验证用户是否已经登录 java实现自动登录

    本文为大家分享了java验证用户是否已经登录与实现自动登录的详细代码,供大家参考,具体内容如下 1.验证用户是否已经登录 package cn.hongxin.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletExceptio

  • java用户名密码验证示例代码分享

    类:NameII    权限:public方法:main    权限:public 参数:name,password,denglu,i;参数介绍:name,数据类型 String ,用来存储一个从 input 中获取的值,在本程序当中用作用户名的存放:password,数据类型 String ,用来存储一个从 input 中获取的值,在本程序当中用作密码的存放:denglu,数据类型 boolean,用来存储默认账户的登录状态,true 表示登录成功,false 表示尚未登录:i,数据类型 in

  • java客户端登陆服务器用户名验证

    本文实例为大家分享了java客户端登陆服务器用户名验证的具体实现代码,供大家参考,具体内容如下 客户端通过键盘录入用户名,服务端对用户名进行验证.  如果用户名存在,服务端显示xxx已登录,客户端显示xxx,欢迎登陆. 如果用户名不存在,服务端显示xxx尝试登陆,客户端显示xxx,用户名不存在.  最多登陆三次,防止暴力登陆. import java.io.*; import java.net.*; /* *客户端 */ class client { public static void mai

  • Java用户登录验证代码

    废话不多说了,关键代码如下所示: import java.util.*; public class Demo04 { public static void main(String[] args){ //声明变量 String root="jim";//用户名 int passwd=123456;//密码 int time=0;//循环次数 int sum=0;//总计次数 Scanner input=new Scanner(System.in);//获取键盘输入 //for循环内 fo

  • 超详细讲解Java秒杀项目用户验证模块的实现

    目录 一.用户验证 1.在方法内添加请求与反应 2.cookie操作的封装 3.UserServiceImpl 4.跳转界面PathController 二.全局session 1.导入依赖 2.配置yml文件redis 3.开启虚拟机 三.自定义redis实现功能 1.新建RedisConfig文件 2.实现全局session 四.使用参数解析器 1.新建WebConfig文件 2.定义参数解析器 3.PathController 4.访问主界面得到相关信息: 接着上期内容超详细讲解Java秒

  • JavaWeb简单用户登录注册实例代码(有验证码)

    需求 编写login登录界面(用户名,密码,验证码,登陆按钮,注册按钮) 设计关系数据库(编号,用户名,密码) 编写注册功能,将数据存储在数据库中.(姓名不能重复,设为主键,重复会注册失败) 编写登录功能 .首先获取验证码,先判断验证码是否正确,不正确则显示验证码错误.验证码正确后再获取用户名和密码,进行数据库的搜索比对,若正确则重定向到成功的界面,并且将用户名显示. jar包 技术选型 Servlet + JSP + Mysql + JDBCTemplate + Druid + BeanUti

  • 超详细讲解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类方

  • 超详细讲解Java线程池

    目录 池化技术 池化思想介绍 池化技术的应用 如何设计一个线程池 Java线程池解析 ThreadPoolExecutor使用介绍 内置线程池使用 ThreadPoolExecutor解析 整体设计 线程池生命周期 任务管理解析 woker对象 Java线程池实践建议 不建议使用Exectuors 线程池大小设置 线程池监控 带着问题阅读 1.什么是池化,池化能带来什么好处 2.如何设计一个资源池 3.Java的线程池如何使用,Java提供了哪些内置线程池 4.线程池使用有哪些注意事项 池化技术

  • 超详细讲解Java异常

    目录 一.Java异常架构与异常关键字 Java异常简介 Java异常架构 1.Throwable 2.Error(错误) 3.Exception(异常) 4.受检异常与非受检异常 Java异常关键字 二.Java异常处理 声明异常 抛出异常 捕获异常 如何选择异常类型 常见异常处理方式 1.直接抛出异常 2.封装异常再抛出 3.捕获异常 4.自定义异常 5.try-catch-finally 6.try-with-resource 三.Java异常常见面试题 1.Error 和 Excepti

  • 四个实例超详细讲解Java 贪心和枚举的特点与使用

    目录 贪心: 枚举: 1.朴素枚举 2.状压枚举    算法题1: 示例 算法题2: 示例 算法题3:  示例1 示例2 算法题4:  示例1 笔试技巧:学会根据数据范围猜知识点          一般1s 时间限制的题目,时间复杂度能跑到 1e8 左右( python 和 java 会少一些,所以建议大家使用c/c++ 做笔试题). n 范围 20 以内: O(n*2^n) 状压搜索 /dfs 暴搜 n 范围 200 以内: O(n^3) 三维 dp n 范围 3000 以内: O(n^2)

  • 四个实例超详细讲解Java 贪心和枚举的特点与使用

    目录 贪心: 枚举: 1.朴素枚举 2.状压枚举 算法题1: 示例 算法题2: 示例 算法题3: 示例1 示例2 算法题4: 示例 笔试技巧:学会根据数据范围猜知识点 一般1s 时间限制的题目,时间复杂度能跑到 1e8 左右( python 和 java 会少一些,所以建议大家使用c/c++ 做笔试题). n 范围 20 以内: O(n*2^n) 状压搜索 /dfs 暴搜 n 范围 200 以内: O(n^3) 三维 dp n 范围 3000 以内: O(n^2) 二维 dp 背包 枚举 二维前

  • Java 超详细讲解设计模式之中的建造者模式

    目录 1.什么是建造者模式? 2.建造者模式的定义 3.建造者模式的优缺点 4.建造者模式的结构 5.建造者模式代码演示 6.建造者模式的应用场景 7.建造者模式和工厂模式的区别 1.什么是建造者模式? 我们知道在软件开发过程中有时需要创建一个很复杂的对象,通常由多个子部件按一定的步骤组合而成. 例如,比如我们在自己在组装一台计算机的时候,需要有 CPU.主板.内存.硬盘.显卡.机箱.显示器.键盘.鼠标等部件组装而成的.比如学校需要采购100台计算机,学校不可能自己把零件买过来自己组装,肯定是告

  • Java 超详细讲解异常的处理

    目录 1.异常的概念和体系结构 1.1异常的概念 1.2异常的体系结构及分类 2.异常的处理 2.1防御式编程 2.2异常地抛出 2.3异常的捕获 (1)异常声明throws (2)try-catch捕获并处理 (3)finally 2.4异常的处理流程 3.自定义异常类 1.异常的概念和体系结构 1.1异常的概念 Java中,在程序执行过程中发生的不正常行为称为异常.比如之前一直遇到的: (1)算数异常 System.out.prinntln(10/0); (2)数组越界异常 int[] ar

  • Java 超详细讲解Spring MVC异常处理机制

    目录 异常处理机制流程图 异常处理的两种方式 简单异常处理器SimpleMappingExceptionResolver 自定义异常处理步骤 本章小结 异常处理机制流程图 系统中异常包括两类: 预期异常 通过捕获异常从而获取异常信息. 运行时异常RuntimeException 主要通过规范代码开发.测试等手段减少运行时异常的发生. 系统的Dao.Service.Controller出现都通过throws Exception向上抛出,最后SpringMVC前端控制器交由异常处理器进行异常处理,如

  • Java超详细讲解三大特性之一的封装

    目录 封装 封装的概念 Java中的包 java中类的成员-构造器 java中的this关键字 总结 说到面向对象则不得不提面向对象的三大特征:封装,继承,多态.那么今天就和大家先来介绍什么是封装. 封装 封装的概念 将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来对隐藏的信息进行操作和访问. 为什么需要封装? 当我们创建一个类的对象后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值.这里赋值操作要受到 属性的数据类型和存储范围的制约.除此之外,没有其他制约

随机推荐