文章目录
- 1.数据库表设计
- 1.IDEA连接数据库
- 2.修改application.yml中数据库的名称为seckill
- 3.IDEA创建数据库seckill
- 4.创建数据表 seckill_user
- 5.密码加密分析
- 1.传统方式(不安全)
- 2.改进方式(两次加密加盐)
- 2.密码加密功能实现
- 1.pom.xml添加md5加密的依赖,刷新maven
- 2.编写工具类MD5Util.java,完成加密
- 3.测试
- 1.引入jupiter 5.7.2依赖
- 2.测试方法
- 3.编写实体类映射表
- 1.创建pojo目录
- 2.User.java
- 4.创建Mapper接口和Mapper.xml
- 1.创建UserMapper.java
- 2.创建UserMapper.xml
- 5.响应信息类
- 1.创建vo目录
- 2.RespBeanEnum.java 响应信息枚举类
- 3.RespBean.java 响应信息的Bean
- 6.编写接受登录信息的vo以及手机号校验工具类
- 1.LoginVo.java
- 2.ValidatorUtil.java
- 7.编写service层
- 1.UserService.java
- 2.UserServiceImpl.java
- 8.编写controller层
- 1.引入校验的依赖
- 2.LoginController.java
- 3.引入静态资源和模板页
- 4.解析前端登录
- 1.点击登录,调用login
- 2.将密码加密加盐之后向后端 /login/doLogin 发送请求
- 5.测试访问首页
- 1.首先检查资源是否到了target目录,如果没到需要maven重新编译
- 2.测试访问 http://localhost:9092/seckill/login/toLogin
- 6.前端区分多环境
- 1.可以看到前端请求时使用的`/`其实只是本机的服务发现,也就是http://localhost:9092/,所以需要区分多环境
- 2.首先新增一个配置类 GlobalControllerAdvice.java 全局控制器通知,用于向前端传递全局变量
- 3.在application.yml和application-prod.yml编写baseUrl,区分多环境
- 4.login.html 读取baseUrl
- 1.在script标签内添加一行代码使其能够解析Thymeleaf
- 2.修改请求,格式为 baseUrl + 资源路径
- 7.在数据库添加一条记录,可以模拟用户登录
- 1.假设密码是admin,在前端输出一下第一次加密加盐的结果
- 2.使用后端的工具类也来加密一下,比对结果,发现结果一致
- 3.在第二次加密的时候重新设置一个不同的盐为niubi666,模拟不同用户
- 4.将结果放到数据库中
- 5.此时的用户名和密码
- 8.编写登录的controller并测试
- 1.LoginController.java
- 2.测试,登录成功
1.数据库表设计
1.IDEA连接数据库
2.修改application.yml中数据库的名称为seckill
3.IDEA创建数据库seckill
4.创建数据表 seckill_user
DROP TABLE IF EXISTS `seckill_user`;
CREATE TABLE `seckill_user`
(
`id` BIGINT(20) NOT NULL COMMENT '用户 ID, 设为主键, 唯一 手机号',
`nickname` VARCHAR(255) NOT NULL DEFAULT '',
`password` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'MD5(MD5(pass 明文+固定salt)+salt)',
`slat` VARCHAR(10) NOT NULL DEFAULT '',
`head` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '头像',
`register_date` DATETIME DEFAULT NULL COMMENT '注册时间',
`last_login_date` DATETIME DEFAULT NULL COMMENT '最后一次登录时间',
`login_count` INT(11) DEFAULT '0' COMMENT '登录次数',
PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
select * from seckill_user;
5.密码加密分析
1.传统方式(不安全)
2.改进方式(两次加密加盐)
2.密码加密功能实现
1.pom.xml添加md5加密的依赖,刷新maven
<!--md5 依赖-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
2.编写工具类MD5Util.java,完成加密
package com.sxs.seckill.utils;
import org.apache.commons.codec.digest.DigestUtils;
/**
* Description: MD5加密工具类
*
* @Author sun
* @Create 2024/5/5 14:23
* @Version 1.0
*/
public class MD5Util {
/**
* 将一个字符串转换为MD5
* @param src
* @return
*/
public static String md5(String src) {
return DigestUtils.md5Hex(src);
}
// 固定的salt
public static final String SALT = "dgeb2g4t";
// 第一次加密加盐
public static String inputPassToMidPass(String inputPass) {
// 加盐
String str = SALT.charAt(0) + inputPass + SALT.charAt(6);
return md5(str);
}
/**
* 第二次加密加盐
* @param midPass
* @param salt
* @return
*/
public static String midPassToDBPass(String midPass, String salt) {
String str = salt.charAt(0) + midPass + salt.charAt(5);
return md5(str);
}
/**
* 两次加密
* @param input
* @param saltDB
* @return
*/
public static String inputPassToDBPass(String input, String saltDB) {
String midPass = inputPassToMidPass(input);
String dbPass = midPassToDBPass(midPass, saltDB);
return dbPass;
}
}
3.测试
1.引入jupiter 5.7.2依赖
<!-- jupiter测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>compile</scope>
</dependency>
2.测试方法
package com.sxs.seckill.utils;
/**
* Description: 测试MD5加密方法
*
* @Author sun
* @Create 2024/5/5 14:47
* @Version 1.0
*/
public class MD5UtilTest {
public static void main(String[] args) {
String input = "123456";
System.out.println("第一次加密:" + MD5Util.inputPassToMidPass(input));
System.out.println("第二次加密:" + MD5Util.midPassToDBPass(MD5Util.inputPassToMidPass(input), MD5Util.SALT));
System.out.println("两次加密:" + MD5Util.inputPassToDBPass(input, MD5Util.SALT));
}
}
3.编写实体类映射表
1.创建pojo目录
2.User.java
package com.sxs.seckill.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* Description:
*
* @Author sun
* @Create 2024/5/5 14:59
* @Version 1.0
*/
@Data
@TableName("seckill_user") // 指定表名
public class User implements Serializable { // 实现序列化接口
private static final long serialVersionUID = 1L;
/**
* 用户 ID是手机号码
*/
@TableId(value = "id", type = IdType.ASSIGN_ID) // 指定主键,IdType.ASSIGN_ID表示自己指定ID,不自增
private Long id;
private String nickname;
/**
* MD5(MD5(pass 明文+固定 salt)+salt)
*/
private String password;
private String slat;
/**
* 头像
*/
private String head;
/**
* 注册时间
*/
private Date registerDate;
/**
* 最后一次登录时间
*/
private Date lastLoginDate;
/**
* 登录次数
*/
private Integer loginCount;
}
4.创建Mapper接口和Mapper.xml
1.创建UserMapper.java
package com.sxs.seckill.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sxs.seckill.pojo.User;
/**
* Description:
*
* @Author sun
* @Create 2024/5/5 15:04
* @Version 1.0
*/
public interface UserMapper extends BaseMapper<User> {
}
2.创建UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sxs.seckill.mapper.UserMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.sxs.seckill.pojo.User">
<id column="id" property="id"/>
<result column="nickname" property="nickname"/>
<result column="password" property="password"/>
<result column="slat" property="slat"/>
<result column="head" property="head"/>
<result column="register_date" property="registerDate"/>
<result column="last_login_date" property="lastLoginDate"/>
<result column="login_count" property="loginCount"/>
</resultMap>
</mapper>
5.响应信息类
1.创建vo目录
2.RespBeanEnum.java 响应信息枚举类
package com.sxs.seckill.vo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
/**
* Description: 响应枚举类
*
* @Author sun
* @Create 2024/5/5 15:15
* @Version 1.0
*/
@Getter
@AllArgsConstructor
@ToString
public enum RespBeanEnum {
// 通用
SUCCESS(200, "SUCCESS"),
ERROR(500, "服务端异常"),
//登录模块
LOGIN_ERROR(500210, "用户名或者密码错误"),
MOBILE_ERROR(500211, "手机号码格式不正确"),
BING_ERROR(500212, "参数绑定异常"),
MOBILE_NOT_EXIST(500213, "手机号码不存在"),
PASSWORD_UPDATE_FAIL(500214, "更新密码失败");
// 响应码和响应信息
private final Integer code;
private final String message;
}
3.RespBean.java 响应信息的Bean
package com.sxs.seckill.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Description:
*
* @Author sun
* @Create 2024/5/5 15:32
* @Version 1.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {
private long code;
private String message;
private Object obj;
// 成功后
public static RespBean success(Object obj) {
return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(), obj);
}
public static RespBean success() {
return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(), null);
}
// 失败各有不同
public static RespBean error(RespBeanEnum respBeanEnum) {
return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(), null);
}
public static RespBean error(RespBeanEnum respBeanEnum, Object obj) {
return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(), obj);
}
}
6.编写接受登录信息的vo以及手机号校验工具类
1.LoginVo.java
package com.sxs.seckill.vo;
/**
* Description: 登录实体类
*
* @Author sun
* @Create 2024/5/5 15:43
* @Version 1.0
*/
public class LoginVo {
private String mobile;
private String password;
}
2.ValidatorUtil.java
package com.sxs.seckill.utils;
import org.junit.jupiter.api.Test;
/**
* Description: 校验工具类
*
* @Author sun
* @Create 2024/5/5 15:45
* @Version 1.0
*/
public class ValidatorUtil {
/**
* 校验手机号是否有效
* @param phoneNumber 手机号
* @return 如果手机号有效返回 true,否则返回 false
*/
public static boolean isMobile(String phoneNumber) {
// 中国大陆的手机号码正则表达式,要求是11位数字,以1开头,第二位是3-9,后面是任意数字共11位
String regex = "^1[3-9]\\d{9}$";
return phoneNumber.matches(regex);
}
// 测试
@Test
public void t1() {
System.out.println(isMobile("12345678901"));
System.out.println(isMobile("1234567890"));
// 正确的
System.out.println(isMobile("13604969873"));
}
}
7.编写service层
1.UserService.java
package com.sxs.seckill.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sxs.seckill.pojo.User;
import com.sxs.seckill.vo.LoginVo;
import com.sxs.seckill.vo.RespBean;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Description:
*
* @Author sun
* @Create 2024/5/5 15:58
* @Version 1.0
*/
public interface UserService extends IService<User> {
/**
* 用户登录校验
* @param loginVo
* @param request
* @param response
* @return
*/
RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response);
}
2.UserServiceImpl.java
package com.sxs.seckill.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sxs.seckill.mapper.UserMapper;
import com.sxs.seckill.pojo.User;
import com.sxs.seckill.service.UserService;
import com.sxs.seckill.utils.MD5Util;
import com.sxs.seckill.utils.ValidatorUtil;
import com.sxs.seckill.vo.LoginVo;
import com.sxs.seckill.vo.RespBean;
import com.sxs.seckill.vo.RespBeanEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Description:
*
* @Author sun
* @Create 2024/5/5 15:59
* @Version 1.0
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Resource
private UserMapper userMapper;
@Override
public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
// 获取用户输入的手机号和密码,此时的密码已经是midPass
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
// 判断手机号和密码是否为空
if (StringUtils.isBlank(mobile) || StringUtils.isBlank(password)) {
return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
// 校验手机号是否正确
if (!ValidatorUtil.isMobile(mobile)) {
return RespBean.error(RespBeanEnum.MOBILE_ERROR);
}
// 根据手机号查询用户
User user = userMapper.selectById(mobile);
// 判断用户是否为空
if (null == user) {
return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
// 判断密码是否正确
if (!MD5Util.midPassToDBPass(password, user.getSlat()).equals(user.getPassword())) {
return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
// 返回成功
return RespBean.success();
}
}
8.编写controller层
1.引入校验的依赖
<!--validation 参数校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.4.5</version>
</dependency>
2.LoginController.java
package com.sxs.seckill.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Description:
*
* @Author sun
* @Create 2024/5/5 16:41
* @Version 1.0
*/
@Slf4j
@Controller
@RequestMapping("/login")
public class LoginController {
/**
* 访问/login/toLogin会跳转到templates/login.html
* @return
*/
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
}
3.引入静态资源和模板页
4.解析前端登录
1.点击登录,调用login
2.将密码加密加盐之后向后端 /login/doLogin 发送请求
5.测试访问首页
1.首先检查资源是否到了target目录,如果没到需要maven重新编译
2.测试访问 http://localhost:9092/seckill/login/toLogin
6.前端区分多环境
1.可以看到前端请求时使用的/
其实只是本机的服务发现,也就是http://localhost:9092/,所以需要区分多环境
2.首先新增一个配置类 GlobalControllerAdvice.java 全局控制器通知,用于向前端传递全局变量
package com.sxs.seckill.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
/**
* Description: 全局控制器通知,用于向前端传递全局变量
*
* @Author sun
* @Create 2024/5/5 17:08
* @Version 1.0
*/
@ControllerAdvice
public class GlobalControllerAdvice {
// 从配置文件中读取baseUrl,从而使前端可以区分多环境
@Value("${baseurl}")
private String baseurl;
@ModelAttribute("baseUrl")
public String baseUrl() {
// 这里可以根据环境变量或其他逻辑来动态确定 URL
return baseurl;
}
}
3.在application.yml和application-prod.yml编写baseUrl,区分多环境
4.login.html 读取baseUrl
1.在script标签内添加一行代码使其能够解析Thymeleaf
th:inline="javascript"
2.修改请求,格式为 baseUrl + 资源路径
7.在数据库添加一条记录,可以模拟用户登录
1.假设密码是admin,在前端输出一下第一次加密加盐的结果
2.使用后端的工具类也来加密一下,比对结果,发现结果一致
3.在第二次加密的时候重新设置一个不同的盐为niubi666,模拟不同用户
4.将结果放到数据库中
5.此时的用户名和密码
- 19588888888:195+八个八
- admin
8.编写登录的controller并测试
1.LoginController.java
@ResponseBody
@RequestMapping("/doLogin")
public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
RespBean respBean = userService.doLogin(loginVo, request, response);
// 返回
return respBean;
}
2.测试,登录成功