目录
技术点介绍:
前端:Freemarker、jQuery
后端:SpringBoot、MyBatisPlus、Lombok
中间件:Redis
导入数据库表
用户表:t_user
商品表:t_goods
订单表:t_order
订单项表:t_order_item
数据源表:t_dict_type
数据项表:t_dict_data

创建一个springboot项目,注意不要勾选如何组件
org.springframework.boot spring-boot-starter-parent 2.3.9.RELEASE
1.8 org.springframework.boot spring-boot-starter-freemarker org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime 5.1.44 org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine junit junit test com.baomidou mybatis-plus-boot-starter 3.4.0 com.baomidou mybatis-plus-generator 3.4.0 com.zaxxer HikariCP commons-codec commons-codec org.apache.commons commons-lang3 3.6 org.springframework.boot spring-boot-starter-validation org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 com.alipay.sdk alipay-easysdk 2.0.1 org.springframework.boot spring-boot-maven-plugin
application.yml:
server:
port: 8081
servlet:
context-path: /
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_ssm?useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=UTF8
driver-class-name: com.mysql.jdbc.Driver
password: 123456
username: root
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:
#文档类型
content-type: text/html
#模板前端
template-loader-path: classpath:/templates/
#启用模板
enabled: true
mvc:
static-path-pattern: /static/**
redis:
#服务端IP
host: 47.100.191.44
#端口
port: 6379
#密码
password: (不准看!!!)
#选择数据库
database: 0
#超时时间
timeout: 10000ms
#Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问
#Lettuce线程安全,Jedis线程非安全
lettuce:
pool:
#最大连接数,默认8
max-active: 8
#最大连接阻塞等待时间,默认-1
max-wait: 10000ms
#最大空闲连接,默认8
max-idle: 200
#最小空闲连接,默认0
min-idle: 5
#mybatis-plus配置
mybatis-plus:
#所对应的 XML 文件位置
mapper-locations: classpath*:/mapper/*Mapper.xml
#别名包扫描路径
type-aliases-package: com.xbb.spbootpro.model
configuration:
#驼峰命名规则
map-underscore-to-camel-case: true
#日志配置
logging:
level:
com.xbb.spbootpro.mapper: debug




在spbootproApplication.java里面加两个注解辅助

导入相关文件:如果需要学习请私信我!!!

创建一个测试类进行测试:
- package com.xbb.spbootpro.controller;
-
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- /**
- * @author冰冰
- * @create 2022-11-08 16:37
- */
- @Controller
- public class IndexController {
- @RequestMapping("/")
- public String index(){
- return "index.html";
- }
- }
先把后台代码跑起来,在访问即可!!
直接访问localhost:8081
如果 能访问成功说明 项目搭建完成!!!

导入我们提前准备好的文件夹

打开generator里面的CodeGenerator运行项目

生成的代码:

IndexController:
- package com.xbb.spbootpro.controller;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
- import com.xbb.spbootpro.model.Goods;
- import com.xbb.spbootpro.service.IGoodsService;
- import com.xbb.spbootpro.utils.DataUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- import java.util.List;
- import java.util.Map;
-
- /**
- * @author冰冰
- * @create 2022-11-08 16:37
- */
- @Controller
- public class IndexController {
- @Autowired
- private IGoodsService goodsService;
-
- @RequestMapping("/")
-
- public String index(Model model){
- //摆件花艺
- List
goods01 = goodsService.list(new QueryWrapper() - .eq("goods_type",01)
- .last("limit 6"));
- //壁挂
- List
goods07 = goodsService.list(new QueryWrapper() - .eq("goods_type",07)
- .last("limit 12"));
- //为了方便首页数据展示
- DataUtils
dataUtils = new DataUtils(); - Map
> gt01 = dataUtils.transfor(3,goods01); - Map
> gt07 = dataUtils.transfor(4,goods07); - model.addAttribute("gt01",gt01);
- model.addAttribute("gt07",gt07);
- return "index.html";
- }
- }
index.html:
<#include "common/head.html">
<#include "common/top.html">
< class="wrapper">


<#if gt01?? && gt01?size gt 0>
<#-- 遍历gt01中所有的key,是为了该key中的对象-->
<#list gt01?keys as key>
<#list gt01[key] as g>
-
- ${g.goodsName}
- ¥ ${g.goodsPrice}
#list>
#list>
#if>

注意:要重启服务!!
前台的价格发生的改变

在controller里面创建一个PageController类
- package com.xbb.spbootpro.controller;
-
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- /**
- * @author冰冰
- * @create 2022-11-09 11:05
- */
- @Controller
- public class PageController {
- @RequestMapping("/page/{page}")
- private String page(@PathVariable("page") String page){
-
- return page;
-
- }
-
- @RequestMapping("/page/dir/{page}")
- private String page(@PathVariable("dir")String dir
- ,@PathVariable("page") String page){
-
- return dir +"/"+ page;
-
- }
-
-
- }
重启服务,点击登陆即可跳到登陆界面如下图:

创建一个login.js:

在login.html里面添加:

在到login.js里面进行测试

刷新项目点击登陆看是否能不能弹出1,能弹1说明这个方法能用。

部分代码:
UserDto:
- package com.xbb.spbootpro.model.dto;
-
- import com.xbb.spbootpro.validator.IsMobile;
- import lombok.Data;
-
- import javax.validation.constraints.NotBlank;
-
- /**
- * @author冰冰
- * @create 2022-11-10 14:45
- */
- @Data
- public class UserDto {
- @NotBlank(message = "手机号码不能为空!")
- @IsMobile
- private String mobile;
- @NotBlank(message = "密码不能为空!")
- private String password;
- }
IsMobile:
- package com.xbb.spbootpro.validator;
-
- import javax.validation.Constraint;
- import javax.validation.Payload;
- import java.lang.annotation.*;
-
- @Documented
- @Constraint(
- validatedBy = {MobileValidator.class}
- )
- @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface IsMobile {
-
- boolean required() default true;
-
- String message() default "手机号码格式错误!";
-
- Class>[] groups() default {};
-
- Class extends Payload>[] payload() default {};
-
- }
UserController:
- package com.xbb.spbootpro.controller;
-
-
- import com.xbb.spbootpro.model.dto.UserDto;
- import com.xbb.spbootpro.service.IUserService;
- import com.xbb.spbootpro.utils.JsonResponseBody;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.validation.Valid;
-
- /**
- *
- * 用户信息表 前端控制器
- *
- *
- * @author xbb
- * @since 2022-11-09
- */
- @RestController
- @RequestMapping("/user")
- public class UserController {
- @Autowired
- private IUserService userService;
- @RequestMapping("/toLogin")
- public JsonResponseBody toLogin(@Valid UserDto userDto,
- HttpServletRequest req,
- HttpServletResponse resp){
-
-
- return userService.toLogin(userDto,req,resp);
- }
-
- }
UserServiceImpl:
- package com.xbb.spbootpro.service.impl;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.xbb.spbootpro.exception.BusinessException;
- import com.xbb.spbootpro.model.User;
- import com.xbb.spbootpro.mapper.UserMapper;
- import com.xbb.spbootpro.model.dto.UserDto;
- import com.xbb.spbootpro.service.IUserService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.xbb.spbootpro.utils.JsonResponseBody;
- import com.xbb.spbootpro.utils.JsonResponseStatus;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- /**
- *
- * 用户信息表 服务实现类
- *
- *
- * @author xbb
- * @since 2022-11-09
- */
- @Service
- public class UserServiceImpl extends ServiceImpl
implements IUserService { - @Autowired
- private UserMapper userMapper;
- @Override
- public JsonResponseBody toLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
- // 1.5.1)判断mobile和password是否为空
- // 1.5.2)判断mobile格式是否正确
- // 1.5.3)根据用户手机号码查询用户是否存在
- User user= userMapper.selectOne(new QueryWrapper
().eq("id",userDto.getMobile())); -
- // 1.5.4)校验账号
- if(user==null){
- throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
- }
- // 1.5.5)校验密码
- if(userDto.getPassword().equals(user.getPassword())){
- throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
- }
-
- return new JsonResponseBody();
- }
- }
效果:
什么也不填:

写了正确的手机号:

密码正确,手机号错误

前端加密:防止客户端浏览器F12导致密码泄露
后端加密:防止数据库数据泄露导致密码泄露
后台加密:
MD5Utils:
- package com.xbb.spbootpro.utils;
-
- 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
- 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(4)+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) {
- //d7aaa28e3b8e6c88352bd5e7c23829f9
- //5512a78a188b318c074a15f9b056a712
- String formPass = inputPassToFormpass("123456");
- System.out.println("前端加密密码:"+formPass);
- String salt = createSalt();
- System.out.println("后端加密随机盐:"+salt);
- String dbPass = formPassToDbPass(formPass, salt);
- System.out.println("后端加密密码:"+dbPass);
- System.out.println("-------------------------------------------");
- String dbPass1 = inputPassToDbPass("123456", salt);
- System.out.println("最终加密密码:"+dbPass1);
- }
- }

前台加密:
login.js:
- $(function () {
- alert(6);
-
- //登陆向后台发送ajax请求
- $("#login").click(function () {
- let mobile = $("#mobile").val();
- let password = $("#password").val();
-
-
- //1.密码加密
- //1) 定义固定盐
- let salt='f1g2h3j4';
- //2) 固定盐混淆
- let temp=salt.charAt(1)+""+salt.charAt(5)+password+salt.charAt(0)+""+salt.charAt(3);
- //3) 使用MD5完成前端第一次加密
- let pwd=md5(temp);
-
-
-
- $.post("/user/toLogin",{
- mobile:mobile,
- password:password
- },function (res) {
- alert(res.msg)
- },"json");
- });
- });
UserServiceImpl:
- package com.xbb.spbootpro.service.impl;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.xbb.spbootpro.exception.BusinessException;
- import com.xbb.spbootpro.model.User;
- import com.xbb.spbootpro.mapper.UserMapper;
- import com.xbb.spbootpro.model.dto.UserDto;
- import com.xbb.spbootpro.service.IUserService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.xbb.spbootpro.utils.JsonResponseBody;
- import com.xbb.spbootpro.utils.JsonResponseStatus;
- import com.xbb.spbootpro.utils.MD5Utils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- /**
- *
- * 用户信息表 服务实现类
- *
- *
- * @author xbb
- * @since 2022-11-09
- */
- @Service
- public class UserServiceImpl extends ServiceImpl
implements IUserService { - @Autowired
- private UserMapper userMapper;
- @Override
- public JsonResponseBody toLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
- // 1.5.1)判断mobile和password是否为空
- // 1.5.2)判断mobile格式是否正确
- // 1.5.3)根据用户手机号码查询用户是否存在
- User user= userMapper.selectOne(new QueryWrapper
().eq("id",userDto.getMobile())); -
- // 1.5.4)校验账号
- if(user==null){
- throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
- }
- //前台传递到后台的密码,要进过工具类md5加密一次,才有可能跟数据库密码匹配上
- String pwd = MD5Utils.formPassToDbPass(userDto.getPassword(),user.getSalt());
- // 1.5.5)校验密码
- if(!pwd.equals(user.getPassword())){
- throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
- }
-
- return new JsonResponseBody();
- }
- }
UserServiceImpl.java变更如下
- package com.xbb.spbootpro.service.impl;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.xbb.spbootpro.exception.BusinessException;
- import com.xbb.spbootpro.model.User;
- import com.xbb.spbootpro.mapper.UserMapper;
- import com.xbb.spbootpro.model.dto.UserDto;
- import com.xbb.spbootpro.service.IRedisService;
- import com.xbb.spbootpro.service.IUserService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.xbb.spbootpro.utils.CookieUtils;
- import com.xbb.spbootpro.utils.JsonResponseBody;
- import com.xbb.spbootpro.utils.JsonResponseStatus;
- import com.xbb.spbootpro.utils.MD5Utils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.util.UUID;
-
- /**
- *
- * 用户信息表 服务实现类
- *
- *
- * @author xbb
- * @since 2022-11-09
- */
- @Service
- public class UserServiceImpl extends ServiceImpl
implements IUserService { - @Autowired
- private UserMapper userMapper;
- @Autowired
- private IRedisService iRedisService;
- @Override
- public JsonResponseBody toLogin(UserDto userDto, HttpServletRequest req, HttpServletResponse resp) {
- // 1.5.1)判断mobile和password是否为空
- // 1.5.2)判断mobile格式是否正确
- // 1.5.3)根据用户手机号码查询用户是否存在
- User user= userMapper.selectOne(new QueryWrapper
().eq("id",userDto.getMobile())); -
- // 1.5.4)校验账号
- if(user==null){
- throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
- }
- //前台传递到后台的密码,要进过工具类md5加密一次,才有可能跟数据库密码匹配上
- String pwd = MD5Utils.formPassToDbPass(userDto.getPassword(),user.getSalt());
- // 1.5.5)校验密码
- if(!pwd.equals(user.getPassword())){
- throw new BusinessException(JsonResponseStatus.USER_USERNAME_ERROR);
- }
-
-
- //6.将登陆用户对象与token令牌进行绑定保存到cookie和redis
- //创建登陆令牌token
- String token= UUID.randomUUID().toString().replace("-","");
- //将token令牌保存到cookie中
- CookieUtils.setCookie(req,resp,"token",token,7200);
- //将登陆token令牌与用户对象user绑定到redis中
- iRedisService.setUserToRedis(token,user);
- //将用户登陆的昵称设置到cookie中
- CookieUtils.setCookie(req,resp,"nickname",user.getNickname());
- return new JsonResponseBody<>();
- }
- }
IRedisService.java
- package com.xbb.spbootpro.service;
-
- import com.xbb.spbootpro.model.User;
-
- /**
- * @author冰冰
- * @create 2022-11-10 16:39
- */
- public interface IRedisService {
- void setUserToRedis(String token, User user);
-
- User getUserByToken(String token);
- }
RedisServiceImpl:
- package com.xbb.spbootpro.service.impl;
-
- import com.xbb.spbootpro.model.User;
- import com.xbb.spbootpro.service.IRedisService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.stereotype.Service;
-
- /**
- * @author冰冰
- * @create 2022-11-10 16:43
- */
-
- @Service
- public class RedisServiceImpl implements IRedisService {
-
- @Autowired
- private RedisTemplate
redisTemplate; -
- @Override
- public void setUserToRedis(String token, User user) {
- redisTemplate.opsForValue().set("user:"+token,user,7200L);
- }
-
- @Override
- public User getUserByToken(String token) {
- return (User) redisTemplate.opsForValue().get("user:"+token);
- }
- }