临沂网站关键词,深圳网站制作十年乐云seo品牌,wordpress导航加title,网站开发的设计思路测测你是什么人格吧#xff0c;地址#xff1a; MBTI 16种人格测试官网 系列文章目录 苍穹外卖是黑马程序员2023年的Java实战项目#xff0c;作为业余练手用#xff0c;需要源码或者课程的可以找我#xff0c;无偿分享 Java项目实战《苍穹外卖》 一、项目概述Java项目实战… 测测你是什么人格吧地址 MBTI 16种人格测试官网 系列文章目录 苍穹外卖是黑马程序员2023年的Java实战项目作为业余练手用需要源码或者课程的可以找我无偿分享 Java项目实战《苍穹外卖》 一、项目概述Java项目实战《苍穹外卖》 二、项目搭建Java项目实战《苍穹外卖》 三、登录功能 文章目录 系列文章目录一、功能实现1.1 实现思路1.2 数据库连接1.3 Service层1.4 ServiceImpl层1.5 Controller层1.6 Mapper层1.7 Postman 二、nginx2.1 思考2.2 反向代理2.2.1 nginx 反向代理的好处2.2.2 nginx 反向代理的配置方式 2.3 负载均衡2.3.1 nginx 负载均衡的配置方式2.3.2 nginx 负载均衡策略 三、会话跟踪方案3.1 什么是会话技术3.2 Cookie3.2.1 会话跟踪技术3.2.2 为什么这一切都是自动化进行的3.2.3 优缺点 3.3 Session3.3.1 会话跟踪技术3.3.2 优缺点 3.4 令牌技术 四、JWT令牌4.1 介绍4.2 JWT令牌的生成4.2.1 引入JWT的依赖4.2.2 测试类4.2.3 执行结果 4.3 校验JWT令牌4.3.1 测试类4.3.2 执行结果4.3.3 方案二官网校验 五、完善登录功能5.1 接口描述5.2 JWT工具类5.3 Controller5.4 拦截器 六、密码加密6.1 问题6.2 解决思路6.3 实现步骤6.3.1 使用SQLyog获取MD5加密6.3.2 修改数据库中明文密码6.3.3 完善登录功能6.3.4 Postman测试 一、功能实现
1.1 实现思路 1.2 数据库连接
编辑 sky-server/src/main/resources/application-dev.yml 文件
sky:datasource:driver-class-name: com.mysql.cj.jdbc.Driverhost: localhostport: 3306database: sky_take_outusername: root # 本地数据库账号password: root # 本地数据库密码1.3 Service层
编辑 sky-server/src/main/java/com/sky/service/EmployeeService.java 文件
public interface EmployeeService {/*** 员工登录* param employeeLoginDTO* return*/Employee login(EmployeeLoginDTO employeeLoginDTO);}1.4 ServiceImpl层
编辑 sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java 文件
/*** 登录** param employeeLoginDTO* return*/PostMapping(/login)public ResultEmployeeLoginVO login(RequestBody EmployeeLoginDTO employeeLoginDTO) {log.info(员工登录{}, employeeLoginDTO);//调用service方法查询数据库Employee employee employeeService.login(employeeLoginDTO);//登录成功后生成jwt令牌MapString, Object claims new HashMap();claims.put(JwtClaimsConstant.EMP_ID, employee.getId());String token JwtUtil.createJWT(jwtProperties.getAdminSecretKey(),jwtProperties.getAdminTtl(),claims);EmployeeLoginVO employeeLoginVO EmployeeLoginVO.builder().id(employee.getId()).userName(employee.getUsername()).name(employee.getName()).token(token).build();return Result.success(employeeLoginVO);}1.5 Controller层
编辑 sky-server/src/main/resources/application-dev.yml 文件
/*** 员工登录** 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);}//密码比对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;}1.6 Mapper层
编辑 sky-server/src/main/java/com/sky/mapper/EmployeeMapper.java 文件
Mapper
public interface EmployeeMapper {/*** 根据用户名查询员工* param username* return*/Select(select * from employee where username #{username})Employee getByUsername(String username);}1.7 Postman 二、nginx
2.1 思考
对登录功能测试完毕后接下来我们思考一个问题前端发送的请求是如何请求到后端服务的 2.2 反向代理
nginx 反向代理就是将前端发送的动态请求由 nginx 转发到后端服务器
2.2.1 nginx 反向代理的好处 提高访问速度 因为nginx本身可以进行缓存如果访问的同一接口并且做了数据缓存nginx就直接可把数据返回不需要真正地访问服务端从而提高访问速度。 进行负载均衡 所谓负载均衡,就是把大量的请求按照我们指定的方式均衡的分配给集群中的每台服务器。 保证后端服务安全 因为一般后台服务地址不会暴露所以使用浏览器不能直接访问可以把nginx作为请求访问的入口请求到达nginx后转发到具体的服务中从而保证后端服务的安全。
2.2.2 nginx 反向代理的配置方式
server{listen 80;server_name localhost;location /api/{proxy_pass http://localhost:8080/admin/; #反向代理}
}proxy_pass 该指令是用来设置代理服务器的地址可以是主机名称IP地址加端口号等形式。
如上代码的含义是监听80端口号 然后当我们访问 http://localhost:80/api/../.. 这样的接口的时候它会通过 location /api/ {} 这样的反向代理到 http://localhost:8080/admin/ 上来。 2.3 负载均衡
当如果服务以集群的方式进行部署时那nginx在转发请求到服务器时就需要做相应的负载均衡。其实负载均衡从本质上来说也是基于反向代理来实现的最终都是转发请求。
2.3.1 nginx 负载均衡的配置方式
upstream webservers{server 192.168.100.128:8080;server 192.168.100.129:8080;
}
server{listen 80;server_name localhost;location /api/{proxy_pass http://webservers/admin;#负载均衡}
}upstream 如果代理服务器是一组服务器的话我们可以使用upstream指令配置后端服务器组。
如上代码的含义是监听80端口号 然后当我们访问 http://localhost:80/api/../.. 这样的接口的时候它会通过 location /api/ {} 这样的反向代理到 http://webservers/admin根据webservers名称找到一组服务器根据设置的负载均衡策略(默认是轮询)转发到具体的服务器。
注 upstream后面的名称可自定义但要上下保持一致。 2.3.2 nginx 负载均衡策略
名称说明轮询默认方式weight权重方式默认为1权重越高被分配的客户端请求就越多ip_hash依据ip分配方式这样每个访客可以固定访问一个后端服务least_conn依据最少连接方式把请求优先分配给连接数少的后端服务url_hash依据url分配方式这样相同的url会被分配到同一个后端服务fair依据响应时间方式响应时间短的服务将会被优先分配 轮询 upstream webservers{server 192.168.100.128:8080;server 192.168.100.129:8080;
}weight upstream webservers{server 192.168.100.128:8080 weight90;server 192.168.100.129:8080 weight10;
}ip_hash upstream webservers{ip_hash;server 192.168.100.128:8080;server 192.168.100.129:8080;
}least_conn upstream webservers{least_conn;server 192.168.100.128:8080;server 192.168.100.129:8080;
}url_hash upstream webservers{hash request_uri;server 192.168.100.128:8080;server 192.168.100.129:8080;
}fair upstream webservers{server 192.168.100.128:8080;server 192.168.100.129:8080;fair;
}三、会话跟踪方案
3.1 什么是会话技术 在我们日常生活当中会话指的就是谈话、交谈。 在web开发当中会话指的就是浏览器与服务器之间的一次连接我们就称为一次会话。
会话跟踪 一种维护浏览器状态的方法服务器需要识别多次请求是否来自于同一浏览器以便在同一次会话的多次请求间共享数据。 服务器会接收很多的请求但是服务器是需要识别出这些请求是不是同一个浏览器发出来的。比如1和2这两个请求是不是同一个浏览器发出来的3和5这两个请求不是同一个浏览器发出来的。如果是同一个浏览器发出来的就说明是同一个会话。如果是不同的浏览器发出来的就说明是不同的会话。而识别多次请求是否来自于同一浏览器的过程我们就称为会话跟踪。 我们使用会话跟踪技术就是要完成在同一个会话中多个请求之间进行共享数据。 为什么要共享数据呢 由于HTTP是无状态协议在后面请求中怎么拿到前一次请求生成的数据呢此时就需要在一次会话的多次请求之间进行数据共享 3.2 Cookie
3.2.1 会话跟踪技术
cookie 是客户端会话跟踪技术它是存储在客户端浏览器的我们使用 cookie 来跟踪会话我们就可以在浏览器第一次发起请求来请求服务器的时候我们在服务器端来设置一个cookie。
比如第一次请求了登录接口登录接口执行完成之后我们就可以设置一个cookie在 cookie 当中我们就可以来存储用户相关的一些数据信息。比如我可以在 cookie 当中来存储当前登录用户的用户名用户的ID。
服务器端在给客户端在响应数据的时候会自动的将 cookie 响应给浏览器浏览器接收到响应回来的 cookie 之后会自动的将 cookie 的值存储在浏览器本地。接下来在后续的每一次请求当中都会将浏览器本地所存储的 cookie 自动地携带到服务端。
3.2.2 为什么这一切都是自动化进行的
是因为 cookie 它是 HTP 协议当中所支持的技术而各大浏览器厂商都支持了这一标准。在 HTTP 协议官方给我们提供了一个响应头和请求头
响应头 Set-Cookie 设置Cookie数据的请求头 Cookie携带Cookie数据的 3.2.3 优缺点 优点 HTTP协议中支持的技术像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带都是浏览器自动进行的是无需我们手动操作的 缺点 移动端APP(Android、IOS)中无法使用Cookie不安全用户可以自己禁用CookieCookie不能跨域 3.3 Session
Session是服务器端会话跟踪技术所以它是存储在服务器端的。而 Session 的底层其实就是基于 Cookie 来实现的。
3.3.1 会话跟踪技术 获取Session 如果我们现在要基于 Session 来进行会话跟踪浏览器在第一次请求服务器的时候我们就可以直接在服务器当中来获取到会话对象Session。如果是第一次请求Session 会话对象是不存在的这个时候服务器会自动的创建一个会话对象Session 。而每一个会话对象Session 它都有一个ID示意图中Session后面括号中的1就表示ID我们称之为 Session 的ID。 响应Cookie (JSESSIONID) 接下来服务器端在给浏览器响应数据的时候它会将 Session 的 ID 通过 Cookie 响应给浏览器。其实在响应头当中增加了一个 Set-Cookie 响应头。这个 Set-Cookie 响应头对应的值是不是cookie cookie 的名字是固定的 JSESSIONID 代表的服务器端会话对象 Session 的 ID。浏览器会自动识别这个响应头然后自动将Cookie存储在浏览器本地。 查找Session 接下来在后续的每一次请求当中都会将 Cookie 的数据获取出来并且携带到服务端。接下来服务器拿到JSESSIONID这个 Cookie 的值也就是 Session 的ID。拿到 ID 之后就会从众多的 Session 当中来找到当前请求对应的会话对象Session。 3.3.2 优缺点
优点 Session是存储在服务端的安全缺点 服务器集群环境下无法直接使用Session移动端APP(Android、IOS)中无法使用Cookie用户可以自己禁用CookieCookie不能跨域 PSSession 底层是基于Cookie实现的会话跟踪如果Cookie不可用则该方案也就失效了。 3.4 令牌技术
如果通过令牌技术来跟踪会话我们就可以在浏览器发起请求。在请求登录接口的时候如果登录成功就可以生成一个令牌令牌就是用户的合法身份凭证。接下来我在响应数据的时候就可以直接将令牌响应给前端。
接下来我们在前端程序当中接收到令牌之后就需要将这个令牌存储起来。这个存储可以存储在 cookie 当中也可以存储在其他的存储空间(比如localStorage)当中。
接下来在后续的每一次请求当中都需要将令牌携带到服务端。携带到服务端之后接下来我们就需要来校验令牌的有效性。如果令牌是有效的就说明用户已经执行了登录操作如果令牌是无效的就说明用户之前并未执行登录操作。
此时如果是在同一次会话的多次请求之间我们想共享数据我们就可以将共享的数据存储在令牌当中就可以了。 优缺点
优点 支持PC端、移动端解决集群环境下的认证问题减轻服务器的存储压力无需在服务器端存储 缺点 需要自己实现包括令牌的生成、令牌的传递、令牌的校验 针对于这三种方案现在企业开发当中使用的最多的就是第三种令牌技术进行会话跟踪。而前面的这两种传统的方案现在企业项目开发当中已经很少使用了。所以在我们的课程当中我们也将会采用令牌技术来解决案例项目当中的会话跟踪问题。 四、JWT令牌
4.1 介绍
JWT全称 JSON Web Token 官网https://jwt.io/ 定义了一种简洁的、自包含的格式用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在这些信息是可靠的。 简洁是指jwt就是一个简单的字符串。可以在请求参数或者是请求头当中直接传递。 自包含指的是jwt令牌看似是一个随机的字符串但是我们是可以根据自身的需求在jwt令牌中存储自定义的数据内容。如可以直接在jwt令牌中存储用户的相关信息。 简单来讲jwt就是将原始的json数据格式进行了安全的封装这样就可以直接基于jwt在通信双方安全的进行信息传输了。
JWT的组成 JWT令牌由三个部分组成三个部分之间使用英文的点来分割 Header(头 记录令牌类型、签名算法等。 例如{“alg”:“HS256”,“type”:“JWT”} Payload(有效载荷携带一些自定义信息、默认信息等。 例如{“id”:“1”,“username”:“Tom”} Signature(签名防止Token被篡改、确保安全性。将header、payload并加入指定秘钥通过指定签名算法计算而来。 签名的目的就是为了防jwt令牌被篡改而正是因为jwt令牌最后一个部分数字签名的存在所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡改了整个令牌在校验的时候都会失败所以它是非常安全可靠的。 JWT令牌最典型的应用场景就是登录认证
在浏览器发起请求来执行登录操作此时会访问登录的接口如果登录成功之后我们需要生成一个jwt令牌将生成的 jwt令牌返回给前端。前端拿到jwt令牌之后会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。服务端统一拦截请求之后先来判断一下这次请求有没有把令牌带过来如果没有带过来直接拒绝访问如果带过来了还要校验一下令牌是否是有效。如果有效就直接放行进行请求的处理。 4.2 JWT令牌的生成
4.2.1 引入JWT的依赖
!-- JWT依赖--
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version
/dependency4.2.2 测试类
新建 sky-server/src/test/java/JwtTest.java 文件
public class JwtTest {/*** 生成令牌*/Testpublic void getJwt() {HashMapString, Object claims new HashMap();claims.put(username, tom);claims.put(password, 123456);String jwt Jwts.builder().setClaims(claims) //自定义内容(载荷).signWith(SignatureAlgorithm.HS256,better) //签名算法.setExpiration(new Date(System.currentTimeMillis() 60*1000)) //有效期60秒.compact();System.out.println(jwt - jwt);// jwt - eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsImV4cCI6MTcwMDU2NTY5NywidXNlcm5hbWUiOiJ0b20ifQ.E0RTUaT5CoEod4NTgKoVMnH0BRdP2cOORKyw8KXMicM}
}4.2.3 执行结果 4.3 校验JWT令牌
4.3.1 测试类
编辑 sky-server/src/test/java/JwtTest.java 文件
public class JwtTest {.../*** 令牌解析*/Testpublic void parserJwt() {Claims claims Jwts.parser().setSigningKey(better)//指定签名密钥必须保证和生成令牌时使用相同的签名密钥.parseClaimsJws(eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsImV4cCI6MTcwMDU2NTg0MiwidXNlcm5hbWUiOiJ0b20ifQ.OJUWynySGYBT1b0BSKBKb2i0k0Gms5C33sd6pp_3jxg).getBody();System.out.println(令牌解析: claims);// 令牌解析: {password123456, exp1700565842, usernametom}}
}4.3.2 执行结果 4.3.3 方案二官网校验 五、完善登录功能
5.1 接口描述
WT令牌怎么返回给前端呢就需要再来看一下接口文档当中关于登录接口的描述
响应数据
参数格式application/json
参数说明
名称类型是否必须默认值备注其他信息codenumber必须响应码, 1 成功 ; 0 失败msgstring非必须提示信息datastring必须返回的数据 , jwt令牌
响应数据样例
{code: 1,msg: success,data: eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6YeR5bq4IiwiaWQiOjEsInVzZXJuYW1lIjoiamlueW9uZyIsImV4cCI6MTY2MjIwNzA0OH0.KkUc_CXJZJ8Dd063eImx4H9Ojfrr6XMJ-yVzaWCVZCo
}5.2 JWT工具类
查看 sky-common/src/main/java/com/sky/utils/JwtUtil.java 文件
public class JwtUtil {/*** 生成jwt* 使用Hs256算法, 私匙使用固定秘钥** param secretKey jwt秘钥* param ttlMillis jwt过期时间(毫秒)* param claims 设置的信息* return*/public static String createJWT(String secretKey, long ttlMillis, MapString, Object claims) {// 指定签名的时候使用的签名算法也就是header那部分SignatureAlgorithm signatureAlgorithm SignatureAlgorithm.HS256;// 生成JWT的时间long expMillis System.currentTimeMillis() ttlMillis;Date exp new Date(expMillis);// 设置jwt的bodyJwtBuilder builder Jwts.builder()// 如果有私有声明一定要先设置这个自己创建的私有的声明这个是给builder的claim赋值一旦写在标准的声明赋值之后就是覆盖了那些标准的声明的.setClaims(claims)// 设置签名使用的签名算法和签名使用的秘钥.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))// 设置过期时间.setExpiration(exp);return builder.compact();}/*** Token解密** param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个* param token 加密后的token* return*/public static Claims parseJWT(String secretKey, String token) {// 得到DefaultJwtParserClaims claims Jwts.parser()// 设置签名的秘钥.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))// 设置需要解析的jwt.parseClaimsJws(token).getBody();return claims;}}5.3 Controller
查看 sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java 文件
/*** 员工管理*/
RestController
RequestMapping(/admin/employee)
Slf4j //这是lombok的输出日志注解这个日志信息更加完整包含类的完整名日志打印的时间
public class EmployeeController {Autowiredprivate EmployeeService employeeService;Autowiredprivate JwtProperties jwtProperties;/*** 登录** param employeeLoginDTO* return*/PostMapping(/login)public ResultEmployeeLoginVO login(RequestBody EmployeeLoginDTO employeeLoginDTO, HttpSession session) {log.info(员工登录{}, employeeLoginDTO);Employee employee employeeService.login(employeeLoginDTO);//将登录数据员工id存储到session会话中session.setAttribute(employee,employee.getId());//生成令牌MapString, Object claims new HashMap();//将员工的id写入载荷claims.put(JwtClaimsConstant.EMP_ID,employee.getId());String token JwtUtil.createJWT(jwtProperties.getAdminSecretKey(),jwtProperties.getAdminTtl(),claims);//由于EmployeeLoginVO 使用Builder注解就可以使用链式构建对象EmployeeLoginVO employeeLoginVO EmployeeLoginVO.builder().id(employee.getId()).userName(employee.getUsername()).name(employee.getName()).token(token).build();return Result.success(employeeLoginVO);}/*** 退出** return*/PostMapping(/logout)public ResultString logout() {return Result.success();}}5.4 拦截器
查看 sky-server/src/main/java/com/sky/interceptor/JwtTokenAdminInterceptor.java 文件
/*** jwt令牌校验的拦截器*/
Component
Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt** param request* param response* param handler* return* throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法直接放行return true;}//1、从请求头中获取令牌String token request.getHeader(jwtProperties.getAdminTokenName());//2、校验令牌try {log.info(jwt校验:{}, token);Claims claims JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);Long empId Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());log.info(当前员工id, empId);//3、通过放行return true;} catch (Exception ex) {//4、不通过响应401状态码response.setStatus(401);return false;}}
}六、密码加密
6.1 问题
员工表中的密码是明文存储安全性太低。
6.2 解决思路
将密码加密后存储提高安全性 使用MD5加密方式对明文密码加密
6.3 实现步骤
6.3.1 使用SQLyog获取MD5加密 6.3.2 修改数据库中明文密码 6.3.3 完善登录功能
编辑 sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java 文件
Service
public class EmployeeServiceImpl implements EmployeeService {Autowiredprivate 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加密然后再进行比对//对用户输入的明文密码加密password DigestUtils.md5DigestAsHex(password.getBytes());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;}}6.3.4 Postman测试