苍穹外卖-day02

前后端联调
后端的初始工程中已经实现了登录功能,直接进行前后端联调测试即可
提示
- 在application-dev.yml中配置好自己的数据库密码

实现思路:

注:可以通过
断点调试跟踪后端程序的执行过程
1.Controller层
- 在sky-server模块中,com.sky.controller.admin.EmployeeController
/**
* 登录
*
* @param employeeLoginDTO
* @return
*/
@PostMapping("/login")
public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
log.info("员工登录:{}", employeeLoginDTO);
Employee employee = employeeService.login(employeeLoginDTO);
//登录成功后,生成jwt令牌
Map<String, Object> claims = new HashMap<>();
// 将员工id存入claims
claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
String token = JwtUtil.createJWT(
// 管理员的秘钥
jwtProperties.getAdminSecretKey(),
// 管理员的过期时间
jwtProperties.getAdminTtl(),
// 管理员的claims
claims);
// 构建返回对象
EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
// 封装数据
.id(employee.getId())
.userName(employee.getUsername())
.name(employee.getName())
.token(token)
.build();
// 返回结果
return Result.success(employeeLoginVO);
}2.Service层
- 在sky-server模块中,com.sky.service.impl.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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
/**
* 员工登录
*
* @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);
}
//密码比对
// TODO 后期需要进行md5加密,然后再进行比对
if (!password.equals(employee.getPassword())) {
//密码错误
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}
if (employee.getStatus() == StatusConstant.DISABLE) {
//账号被锁定
throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
}
//3、返回实体对象
return employee;
}
}3.Mapper层
- 在sky-server模块中,com.sky.mapper.EmployeeMapper
package com.sky.mapper;
import com.sky.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface EmployeeMapper {
/**
* 根据用户名查询员工
* @param username
* @return
*/
@Select("select * from employee where username = #{username}")
Employee getByUsername(String username);
}JWT令牌加密技术
- 用户登录流程:

提示
JWT(JSON Web Token)是一种用于身份验证和授权的开放标准。它由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature)。其中,签名是用于验证令牌的完整性和可信任性。





package com.sky.test;
import io.jsonwebtoken.*;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
import java.util.UUID;
@SpringBootTest
public class JWTTest {
// 定义有效期(有效期为24小时)
private static final long time = 1000 * 60 * 60 * 24;
// 定义签名信息
private String signature = "admin";
// JWT加密
@Test
public void jwt() {
// 创建JwtBuilder对象
JwtBuilder jwtBuilder = Jwts.builder();
// 组装JWT
String jwtToken = jwtBuilder
// header头部
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
// payload负载
.claim("username", "xingji") // 设置加密的用户名
.claim("rale", "admin") // 设置加密的角色
.setSubject("admin-test") // 设置加密的主题
.setIssuedAt(new Date(System.currentTimeMillis() + time)) // 设置加密有效期
.setId(UUID.randomUUID().toString()) // 设置加密的id
// signature签名
.signWith(SignatureAlgorithm.HS256, signature) // 设置签名算法和签名信息
.compact(); // 组装JWT(拼接header.payload.signature)
// 打印经过加密的JWT信息
System.out.println(jwtToken);
}
// JWT解密
@Test
public void parse() {
// header头部 + payload负载 + signature签名 = 拼接的JWT信息
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InhpbmdqaSIsInJhbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJpYXQiOjE3NzU5MTA0MTYsImp0aSI6ImZmNWY3OTA4LTcxZWEtNDRhZS05MWM1LTZiYzQwN2ZkNzVlYyJ9.GYCyJz4SQZhK7k4ZX-kEPRD4OPWfRi1Ft_kGtnXl_BY";
// 解密流程
// 创建JwtParser对象
JwtParser jwtParser = Jwts.parser();
// 解析JWT信息
Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(token);
// 获取解析后的JWT信息
Claims claims = claimsJws.getBody();
// 打印解析后的JWT信息
System.out.println(claims.get("username"));
System.out.println(claims.get("rale"));
System.out.println(claims.getSubject());
System.out.println(claims.getExpiration());
System.out.println(claims.getId());
}
}

- JWT的优势:
授权信息完全存储在客户端- 服务端
不需要存储任何信息,不需要部署分布式存储系统
- JWT的劣势:
- 想撤回JWT很难
- 过期时间尽可能设短,比如从一天改为1小时
- Payload里不宜存放敏感信息

- 验证登录的代码界面

- JwtUtil工具类
jwt加密:

**jwt解密: **

Nginx负载均衡和反向代理

注意

nginx反向代理,就是将
前端发送的动态请求由nginx转发到后端服务器

所谓负载均衡,就是把
大量的请求按照我们指定的方式均衡的分配给集群中的每台服务器

- 反向代理:
有反向代理就有正向代理,而二者的区别很明显:反向代理隐藏服务器,正向代理隐藏客户端
正向代理是客户端发送请求后通过代理服务器访问目标服务器,代理服务器代表客户端发送请求并将响应返回给客户端。正向代理隐藏了客户端的真实身份和位置信息,为客户端提供代理访问互联网的功能。
反向代理是位于目标服务器和客户端之间的代理服务器,它代表服务器接收客户端的请求并将请求转发到真正的目标服务器上,并将得到的响应返回给客户端。反向代理隐藏了服务器的真实身份和位置信息,客户端只知道与反向代理进行通信,而不知道真正的服务器。

# 反向代理,处理管理端发送的请求
location /api/ {
proxy_pass http://localhost:8080/admin/;
#proxy_pass http://webservers/admin/;
}
# 反向代理,处理用户端发送的请求
location /user/ {
proxy_pass http://webservers/user/;
}反向代理
可以缓存后端响应,使得相同的请求不需要再次发送到服务器,有效降低了服务器的访问压力。
- 负载均衡:
Nginx 的负载均衡功能允许将请求分发给多个应用服务器,以均衡负载和提高系统的可扩展性和可靠性。下面是一些常用的 Nginx 负载均衡配置方法:
轮询(Round Robin):这是默认的负载均衡策略。Nginx将请求依次分发给每个后端服务器,确保每个服务器都能获得相同的请求数量。
IP 哈希(IP Hash):Nginx 使用客户端 IP 地址的哈希值来决定将请求发送给哪个后端服务器。这种方式可以确保同一客户端的请求始终发送到同一个后端服务器,适用于某些需要会话保持的场景。
加权轮询(Weighted Round Robin):可以为每个后端服务器设置权重,高权重的服务器将获得更多的请求。这种方式可以根据服务器的性能和处理能力来分配负载。
最少连接(Least Connections):Nginx 根据当前连接数来选择最空闲的后端服务器,将请求发送给它。这样可以确保负载更均衡,避免某些服务器过载。

upstream webservers{
server 127.0.0.1:8080 weight=90 ; # weight权重越大就首先分配服务器资源
#server 127.0.0.1:8088 weight=10 ;
}通过这个Nginx就可以把请求
分发给指定的多台服务器,并且我们也设置了权重。不过因为本次演示的是单机项目,我们只有一台电脑,因此我们把第二个地址注释了起来。
MD5加密登录
注意
为了防止数据库泄露带来的
用户账号密码安全性问题,我们即使是在数据库中也不会进行明文存储密码,而是存储MD5加密方法加密后的一串字符串。


1.将密码加密后存储,提高安全性
2.使用MD5加密方式对明文密码加密

贡献者
更新日志
37c26-优化MySQL文章封面于





