一.项目搭建
1.新建一个SpringBoot项目
2.导入依赖
Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎,类似JSP,Velocity,FreeMaker等,它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。
Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用
3.编辑配置文件 application.yml
spring:
#thymeleaf 配置
thymeleaf:
# 关闭缓存
cache: false
#配置数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///seckill?useUnicode=true&characterEncoding=utf-8&serverTimeZone=Asia/Shanghai
username: root
password: root
#配置连接池
hikari:
#连接池名字
pool-name: HikariPool
#最小空间连接数
minimum-idle: 5
#最大连接数,默认10,可以不设置
maximum-pool-size: 10
#连接最大存活时间,0表示永久,默认30分钟,可以不设置
max-lifetime: 1800000
#空闲连接存活最大时间,默认10分钟(600000ms),这里设置半个小时
idle-timeout: 1800000
#连接超时时间,默认30秒
connection-timeout: 30000
#心态机制,测试连接是否可用,如果有问题说明我们的连接池有问题
connection-test-query: select 1
#从连接池返回的连接自动提交
auto-commit: true
#Mybatis-plus配置
mybatis-plus:
#配置XXXMapper.xml的映射路径
mapper-locations: classpath*:/mapper/*Mapper.xml
#配置Mybatis别名包
type-aliases-package: cn.bs.seckill.pojo
#Mybatis Sql日志打印(指定方法接口所在的包,不是你xml所在的包)
logging:
level:
cn.bs.seckill.mapper: debug
4.把包建好
二 编写测试类
Model
是每次请求中都存在的默认参数,利用其addAttribute()
方法即可将服务器的值传递到jsp
页面中
1.新建一个TestController类
package cn.bs.seckill.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/*本类用于测试*/
@Controller
@RequestMapping("/test")
public class TestController {
//测试页面跳转
@RequestMapping("/hello")
public String hello(Model model){
model.addAttribute("msg","老王");
return "hello";
}
}
2.在tenplates包下新建一个hello.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<p th:text="'我是你亲爱的'+${msg}"></p>
</body>
</html>
3.启动项目,测试看是否成功
输入controller的地址,就会跳转到页面
三 编写数据库
1.新建一个数据库,seckill,注意选择编码格式
2.建表
CREATE TABLE t_user(
`id` BIGINT(20) NOT NULL COMMENT '用户ID,用手机号码代替',
`nickname` VARCHAR(200) NOT NULL COMMENT '昵称',
`password` VARCHAR(32) DEFAULT NULL COMMENT '前端传给后端先用md5加密(明文+固定的salt值),后端存数据库之前再加盐加密,双重加密',
`slat` VARCHAR(10) DEFAULT NULL COMMENT '盐值',
`head` VARCHAR(128) DEFAULT NULL COMMENT '头像',
`register_date` datetime DEFAULT NULL COMMENT '注册时间',
`last_login_date` datetime DEFAULT NULL COMMENT '最后一次登录时间',
`login_count` INT(10) DEFAULT '0' COMMENT '登录次数',
PRIMARY KEY(`id`)
)
四 编写工具类
1.需要用到md5加密,md5加密操作
2.编写各个包,这里需要添加一个vo包
- 1 创建LoginVo
package cn.bs.demoseckill.vo;
import com.sun.istack.internal.NotNull;
import lombok.Data;
//介绍账号密码
@Data
public class LoginVo {
@NotNull
//@IsMobile
private String mobile;
@NotNull
//@Length(min = 32)
private String password;
}
这里注释掉的东西后面我们在补回来
- 2 创建一个RespBean
package cn.bs.demoseckill.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {
private long code;
private String message;
private Object obj;
//登录成功返回的消息
public static RespBean success(){
return new RespBean(RespBeanEnum.SUCCESS.getCODE(), RespBeanEnum.SUCCESS.getMESSAGE(), null);
}
public static RespBean success(Object obj){
return new RespBean(RespBeanEnum.SUCCESS.getCODE(), RespBeanEnum.SUCCESS.getMESSAGE(), obj);
}
//登录失败返回的消息
public static RespBean error(RespBeanEnum pe){
return new RespBean(pe.getCODE(), pe.getMESSAGE(), null);
}
public static RespBean error(RespBeanEnum pe, Object obj){
return new RespBean(pe.getCODE(), pe.getMESSAGE(), obj);
}
}
- 3 创建一个RespBeaEnum
package cn.bs.demoseckill.vo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@ToString
@Getter
public enum RespBeanEnum {
SUCCESS("成功",200),
ERROR("服务器异常",500),
LOGIN_ERROR("休想白嫖,请登录",530),
LOGIN_FAILED("账号或者密码不正确",531),
LOGIN_FAILEDP("密码不对",532);
private final String MESSAGE;
private final Integer CODE;
}
五 导入静态资源
- 分析login跳转
六 编写登录功能
- 编写登录层
package cn.bs.seckill.controller;
import cn.bs.seckill.service.UserService;
import cn.bs.seckill.vo.LoginVo;
import cn.bs.seckill.vo.RespBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
//@RestController 返回的是类里面东西,Controller才是跳转到静态资源
@Slf4j
@RequestMapping("/login")
public class LoginController {
@Autowired
private UserService userService;
//跳转登录页面
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/doLogin")
@ResponseBody
public RespBean doLogin(LoginVo loginVo){
return userService.doLogin(loginVo);
}
}
@RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径;用于方法上,表示在类的父路径下追加方法上注解中的地址将会访问到该方法
@RequestMapping 后,<font color='red'>返回值通常解析为跳转路径</font>,加上 *@Responsebody* 后返回结果<font color='red'>不会被解析为跳转路径,而是直接写入HTTP 响应正文中</font>,一般在异步获取数据时使用【也就是AJAX】,所以你如果是ajax请求,请把它加上,不然报错你又瓜起
2.编写业务逻辑,先在UserService接口里面添加doLogin方法,然后去实现类实现
public interface UserService {
//登录接口
RespBean doLogin(LoginVo loginVo);
}
然后在实现类里写好我们的登录方法
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public RespBean doLogin(LoginVo loginVo) {
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
//判断账号密码是否为空
if (StringUtils.isEmpty(mobile)||StringUtils.isEmpty(password)) {
return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
//判断是否正确输入手机号
if (!ValidatorUtil.isMobile(mobile)){
return RespBean.error(RespBeanEnum.LOGIN_FAILED);
}
User user = userMapper.selectById(mobile);
//判断手机号是否存在
if (null==user){
return RespBean.error(RespBeanEnum.LOGIN_FAILED);
}
//判断密码是否正确
if (!(user.getPassword()).equals(MD5Util.toDBPassEpt(password))){
return RespBean.error(RespBeanEnum.LOGIN_FAILEDP);
}
return RespBean.success();
}
}
3.测试是否能登录
输入一个账号密码,我这里以123456为例子,把它做两次加密,然后存入数据库中
数据库里id就是登录的账号
七 引入303校验
1.导入依赖
<!--JSR303数据校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
我们为了判断用户是否输入了正确的账号格式编写了大量的代码,为了节省时间,我们可以用303数据校验
2.因为我的账号密码是通过传到后端是通过我们的LoginVo这个类里面的两个属性搞的,所以我们需要在这个类的两个属性上面加上判断注解
@Data
public class LoginVo {
@NotNull
@IsMobile
private String mobile;
@NotNull
@Length(min = 32)
private String password;
}
@IsMobile是我们自定义的注解,以后在开发当中,我们也可以仿照这种方式来自定义需要的注解
3.新建一个IsMobile注解,下面的代码不用自己写,可以通过进入@NotNull注解进入底层代码复制这一段,为什么需要自定义这个注解呢?因为我们需要判断手机号是否是必须输入的内容,而@NotNull做不到
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//校验规则
@Constraint(
validatedBy = {MobileValidator.class}
)
public @interface IsMobile {
//添加一个方法作为验证手机号为必填
boolean required() default true;
String message() default "{请输入正确的手机号}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
4.@Constraint( validatedBy = {MobileValidator.class})这个方法里面的参数,是校验规则,所以我们需要新建一个类来负责编写校验规则
public class MobileValidator implements ConstraintValidator<IsMobile, String> {
private boolean required = false;
//初始化,因为是手机号,所以我们就判断是否为必填
@Override
public void initialize(IsMobile constraintAnnotation) {
//调用此方法,把required赋值为真
required = constraintAnnotation.required();
}
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
//如果是必填
if (required) {
//判断手机号格式是否正确
return ValidatorUtil.isMobile(s);
}
//如果是非必填
else {
//返回为真
if (StringUtils.isEmpty(s)) {
return true;
} else {
//判断格式是否正确
return ValidatorUtil.isMobile(s);
}
}
}
}
其中initialize为初始化方法,可以在里面做一些初始化操作,isValid方法就是我们最终需要的校验方法了。可以在该方法中实现具体的校验步骤。
5.接下来把UserServiceImpl层里面的两个判断手机是否为空和格式是否正确的两段代码注释掉
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public RespBean doLogin(LoginVo loginVo) {
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
/* //判断账号密码是否为空
if (StringUtils.isEmpty(mobile)||StringUtils.isEmpty(password)) {
return RespBean.error(RespBeanEnum.LOGIN_ERROR);
}
//判断是否正确输入手机号
if (!ValidatorUtil.isMobile(mobile)){
return RespBean.error(RespBeanEnum.LOGIN_FAILED);
}*/
User user = userMapper.selectById(mobile);
//判断手机号是否存在
if (null==user){
return RespBean.error(RespBeanEnum.LOGIN_FAILED);
}
//判断密码是否正确
if (!(user.getPassword()).equals(MD5Util.toDBPassEpt(password))){
return RespBean.error(RespBeanEnum.LOGIN_FAILEDP);
}
return RespBean.success();
}
}
6.在LoginController层的doLogin方法的参数里加上我们的注解
@Controller
//@RestController 返回的是类里面东西,Controller才是跳转到静态资源
@Slf4j
@RequestMapping("/login")
public class LoginController {
@Autowired
private UserService userService;
//跳转登录页面
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/doLogin")
//注意必须的有Resp这个注解,不然
@ResponseBody
public RespBean doLogin( @Valid LoginVo loginVo){
/* log.info("{}",loginVo);
return null;*/
return userService.doLogin(loginVo);
}
}
7.开始测试,故意乱输手机号,后端就会收到异常