开发环境搭建
首先把资料下载,网盘链接:黑马程序员苍穹外资料链接
鉴于很多Windows的朋友没有下载百度网盘客户端或者出现打不开、登录不上的问题,在这里推荐大家使用Tampermonkey+IDM 的脚本管理器方式下载
-
首先在浏览器中下载Tampermonkey拓展
在其添加脚本中下载网盘直链下载助手 -
下载多线程下载器IDM(并且下载使用浏览器扩展)
IDM 全称为 Internet Download Manager,仅支持 Windows 平台,Mac 平台可使用 NDM 代替。- 打开 IDM 选项,将自己的浏览器加入 IDM 的捕获列表中。
- 把要支持 IDM 下载的后缀名添加到 IDM 选项 -> 文件类型里。
后缀:
3GP 7Z AAC ACE AI AIF ALZ APK APP APPX APPXBUNDLE ARC ARJ ASF AVI BH BIN BR BUNDLE BZ BZ2 CDA CSV DIF DLL DMG DOC DOCX EGG EPS EXE FLV GZ GZIP IMG IPA ISO ISZ JAR KEXT LHA LZ LZH LZMA M4A M4V MDB MID MKV MOV MP3 MP4 MPA MPE MPEG MPG MSI MSIX MSIXBUNDLE MSU MUI OGG OGV PDF PKG PPT PPTX PSD PST PUB QT R0* R1* RA RAR RM RMVB RTF SEA SIT SITX SLDM SLDX TAR TBZ TBZ2 TGZ TIF TIFF TLZ TXZ UDF VOB VSD VSDM VSDX VSS VSSM VST VSTM VSTX WAR WAV WBK WIM WKS WMA WMD WMS WMV WMZ WP5 WPD WPS XLS XLSX XPS XZ Z ZIP ZIPX ZPAQ ZSTD
-
打开网页版百度网盘就可以下载了
一. 前端环境搭建
前端工程基于nginx部署
注意:必须将nginx相关代码存放到英文目录下,否则nginx不能启动
启动之后打开浏览器搜索locatehost即可
鉴于之后要经常使用它,我能可以使用edge浏览器的应用功能,将该网站作为应用安装,快速使用
如图:
二. 后端环境搭建
后端工程基于Maven分模块开发构建该spring项目
1.进入idea项目
用idea打开项目在pom.xml文件中引入依赖
直接clean然后install即可
该项目结构比较简单,就是一个父工程聚合三个子模块的单体架构,可以说是当前主流的前后端中的必过项目了
这里对具体项目结构不再详细阐述
2.提交git仓库(+推送github远程仓库)
具体配置idea的github远程仓库这里不再描述
3.数据库环境搭建
复制sky.sql代码到数据库控制台中运行即可
共有11张表
4.前后端联调(在源代码中项目已经实现登录功能)
具体流程如下:
启动之前先使用maven工具中compile进行编译
注意在这里把数据库配置改成自己的
启动项目:
运行结果:
nginx反向代理好处:
- 提高访问速度
- 进行负载均衡
- 保证后端服务安全
- 解决跨域问题
三. 完善登录功能(md5加密存储)
思路:
- 将密码加密后存储,提高安全性
- 使用md5加密方式对明文密码加密
视频中是直接使用了DigestUtils中的MD5加密来提高安全性,鉴于MD5现在已经有点不太安全,市面上已经有了破解的方法,这里使用MD5加盐处理来进一步提高安全性
由于前端问题,这里不再提高注册的方法,直接自创一个类把需要的盐和加密后的密码获取,手动输入到数据库中对应字段
// 生成一个新的盐值
private static final SecureRandom RANDOM = new SecureRandom();
//随机数生成器
private static final int SALT_LENGTH = 16;//盐的长度
String salt = generateSalt();
// 使用生成的盐值对密码进行加密
String password = "123456";
String hashedPassword = hashPassword(password, salt);
// 打印生成的盐值和加密后的密码
System.out.println("Salt: " + salt);
System.out.println("Hashed Password: " + hashedPassword);
// 生成盐的方法
private String generateSalt() {
byte[] salt = new byte[SALT_LENGTH];
RANDOM.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
// 对密码进行加密的方法
private String hashPassword(String password, String salt) {
String saltedPassword = salt + password;
return DigestUtils.md5DigestAsHex(saltedPassword.getBytes());
}
登录验证问题
1.首先打开pojo模块中实体类的employee,添加salt字段
package com.sky.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;
private Integer status;
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
private Long createUser;
private Long updateUser;
private String salt;
}
2.在数据库中employee表新建一个salt字段,注意得是字符串类型
3.修改EmployeeServiceImpl类,添加加密处理
package com.sky.service.impl;
import com.sky.constant.MessageConstant;
import com.sky.constant.StatusConstant;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.entity.Employee;
import com.sky.exception.AccountLockedException;
import com.sky.exception.AccountNotFoundException;
import com.sky.exception.PasswordErrorException;
import com.sky.mapper.EmployeeMapper;
import com.sky.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.Base64;
import java.security.SecureRandom;
@Slf4j
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
private static final SecureRandom RANDOM = new SecureRandom();//随机数生成器
private static final int SALT_LENGTH = 16;//盐的长度
/**
* 员工登录
*
* @param employeeLoginDTO
* @return
*/
public Employee login(EmployeeLoginDTO employeeLoginDTO) {
String username = employeeLoginDTO.getUsername();
String password = employeeLoginDTO.getPassword();
//1、根据用户名查询数据库中的数据
Employee employee = employeeMapper.getByUsername(username);
//2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
if (employee == null) {
//账号不存在
throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
}
//密码比对
//对前端传来的密码进行md5加盐处理,然后再进行比对
String salt = employee.getSalt();//盐
String hashedPassword = hashPassword(password, salt);//加密后的密码
if (!hashedPassword.equals(employee.getPassword())) {
//密码错误
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}
if (employee.getStatus() == StatusConstant.DISABLE) {
//账号被锁定
throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
}
//3、返回实体对象
return employee;
}
//对密码进行加密
private String hashPassword(String password, String salt) {
String saltedPassword = salt + password;
return DigestUtils.md5DigestAsHex(saltedPassword.getBytes());
}
}
然后就可以登录成功了
接口文档的导入
这里使用apifox软件来导入
导入时选择YApi的json格式来导入
具体如下:
基于Swagger的Knife4j
使用方式:
- 导入Knife4j的Maven坐标
- 在配置类中加入Knife4j相关配置
- 设置静态资源映射,否则接口文档页面无法访问
具体代码在server中配置文件WebMvcConfiguration类中
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 设置静态资源映射
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
运行项目后在浏览器输入http://localhost:8080//doc.html即可进入
配置apifox中环境变量
调试:
可以成功
Swagger常用注解
注解说明(OpenAPI3规范)
@Tag
用于说明或定义的标签。
部分参数:
name:名称
description:描述
@Schema
用于描述实体类属性的描述、示例、验证规则等,比如 POJO 类及属性。
部分参数:
name:名称
title:标题
description:描述
example:示例值
required:是否为必须
format:属性的格式。如 @Schema(format = "email")
maxLength 、 minLength:指定字符串属性的最大长度和最小长度
maximum 、 minimum:指定数值属性的最大值和最小值
pattern:指定属性的正则表达式模式
type: 数据类型(integer,long,float,double,string,byte,binary,boolean,date,dateTime,password),必须是字符串。如 @Schema=(type="integer")
implementation :具体的实现类,可以是类本身,也可以是父类或实现的接口
@Content
内容注解。
部分参数:
mediaType:内容的类型。比如:application/json
schema:内容的模型定义,使用 @Schema 注解指定模型的相关信息。
@RequestBody(content = @Content(mediaType = "application/json", schema = @Schema(implementation = User.class)))
@PostMapping("/users")
public void createUser(User user) {
// ...
}
@Hidden
某个元素(API 操作、实体类属性等)是否在 API 文档中隐藏。
如,getUserById 方法不会显示在 API 文档中
使用在实体类字段中,实现对敏感信息或不需要公开的元素进行隐藏。如:用户密码字段
@Operation
描述 API 操作的元数据信息。常用于 controller 上
部分参数:
summary:简短描述
description :更详细的描述
hidden:是否隐藏
tags:标签,用于分组API
operationId:操作的唯一标识符,建议使用唯一且具有描述性的名称
parameters:指定相关的请求参数,使用 @Parameter 注解来定义参数的详细属性。
requestBody:指定请求的内容,使用 @RequestBody 注解來指定请求的类型。
responses:指定操作的返回内容,使用 @ApiResponse 注解定义返回值的详细属性。
@Parameter
用于描述 API 操作中的参数
部分参数:
name : 指定的参数名
in:参数来源,可选 query、header、path 或 cookie,默认为空,表示忽略
ParameterIn.QUERY 请求参数
ParameterIn.PATH 路径参数
ParameterIn.HEADER header参数
ParameterIn.COOKIE cookie 参数
description:参数描述
required:是否必填,默认为 false
schema :参数的数据类型。如 schema = @Schema(type = "string")
@Parameters
包含多个 @Parameter 注解,指定多个参数。
代码参考:
包含了 param1 和 param2 两个参数
@RequestBody
API 请求的注解
description:请求信息的描述
content:请求的内容
required:是否必须
@ApiResponse
API 的响应信息。
部分参数:
responseCode:响应的 HTTP 状态码
description:响应信息的描述
content:响应的内容