Antifrag

返回

项目开发总结#

本次项目开发学习前前后后花费了数半个月,虽然学习的项目章节不太多,但也受益匪浅

不论是从最开始的环境搭建,还是之后的微服务、小程序、网关配置等等方面,也是学到了很多技巧,但这其中我也遇到了很多困难,本次开发总结我将详细讲述我的开发流程,其中遇到的一些困难,以及如何去解决这些困难

环境配置#

首先是在最开始的环境配置当中,我们需要先下载虚拟机环境(模拟公司、企业当中的服务器)

image-20240711091119491

可看到虚拟机环境已经帮我们配置好了,这些服务是一些正在运行当中的服务

然后就是虚拟机也需要配置一下内网IP方便后续服务的访问

image-20240711091843448

内网配置好后在配置本地ip

image-20240711092123041

可以看到本地服务地址与虚拟机地址是一样的,然后后面就是一些服务

image-20240711092321796

在这之后我们就可以成功的访问服务了

我们先从本地访问服务域名,通过本地hosts文件配置,将服务地址传输到虚拟机,再通过虚拟机nginx服务将其反向代理到各个服务

image-20240711093843963

Maven私服#

Maven私服配置根据服务不同,进行相应配置

git代码管理#

对于代码的管理,我觉得是相对重要的,这在开发当中是必不可少的

分支管理#
  • 不同功能的分支形成代码隔离,发布时从主分支进行代码发布,测试在测试分支,开发在开发分支,减少出错。。
  • 分支之间互不影响,开发者可以独立开发,提高团队开发效率
分支管理策略#

master

主分支,用于部署生产环境的分支,是所有分支的主干,与当前线上代码保持统一

dev

开发分支,开发各类分支的合并分支,即当前最新功能分支

feature

功能分支,一般用于新技术的开发,开发完成后合并到master/dev分支,随后便可删除分支

release

用于测试的分支,如遇bug,将其推到 fixbug分支进行修复

fixbug

bug修复分支,完成修复后,将其合并到master/dev

tag标签

master发布开发环境后,用tag来进行版本标记

然后后面就是一些分支开发测试(这里就不做多的篇幅解释)

image-20240711100917580

项目列表#

这些服务后续会用到

image-20240711101111389

Jenkins#

在学习本次项目之前,我也才只接触Jenkins过一次,那还是在上次2023年的一次靶机练习,记得当时也是打的迷迷糊糊,不过经过本次学习后,我也更深层次的体验到了Jenkins的用法

从最开始的任务构建到参数、版本号、微服务地址、服务名称、端口号、git地址等等一系列的配置,在到脚本构建,一键运行,就完成了项目的一个搭建,通过测试便可查看是否搭建成功

image-20240711111202282

image-20240711111228419

测试成功后,将Jenkins中web钩子设定,绑定git,测试推送

Jenkins总结#

从Jenkins的使用流程来看,我认为Jenkins就是用于将git进行连接配置,然后直接启动服务的中间件,他可以直接将服务上传到docker容器中(前提是在环境中已经配置好了docker,Maven,git),他方便我们对服务的一个管理,是一个强大的中间件。

登录业务#

关于登录业务,我觉得文档上给的图就已经完美诠释了整个登录流程

image-20240717144904295

  1. 首先前端先从WebManager 后端服务管理中获取 验证码 (并携带key)
  2. 后端服务管理生成验证码
  3. 讲验证码和key值传入Redis
  4. Redis返回值
  5. 后端返回验证码图传给前端
  6. 前端输入账号密码验证码
  7. 后端从Redis中进行匹配验证码
  8. Redis返回结果
  9. 后端进行校验
    1. 假如验证码匹配失败,就返回验证码错误
    2. 假如匹配成功,从Redis中删除验证码值,返回后端
  10. 进行用户名密码校验
    1. 错误:返回前端用户名或密码错误
    2. 成功:生成token,返回登录成功

验证码功能的实现#

这个功能的实现文档中是基于算法做的验证码,我在实际的过程中使用的是4位随机数所做的验证码,其实也大差不差

后端实现过程#

从git拉取代码 sl-express-ms-web-manager

在idea中进行基础jdkManven配置 然后就可以开始实现了

hutool验证码文档说明

39

在Controller中调用Service中的方法:

image-20240703165756274

测试#

可以看到验证码

image-20240703165907670

这样这么一个接口就完成了

然后接下来我们还需要将前端接口进行连接

前端部署#

同样的前端,我们先需要将代码拉取

拉取project-slwl-admin-vue代码

image-20240717152419712

修改.env.production.stu文件,设置前后端交互接口改为本机地址和端口:

image-20240704171035290

提交到git中,使用Jenkins部署

选择develop分支,开始构建

image-20240704171130051

访问地址进行测试:http://admin.sl-express.com/

image-20240704171240463

可以看到请求为200

image-20240704171414493

登录功能实现#

刚刚实现的是验证码功能,登录功能还没有实现,我们现在紧接着实现登录

AuthServiceImpl中实现代码

image-20240704174459771

image-20240704174515533

Controller中调用:

image-20240704174750039

测试#

可以看到测试有数据

image-20240704174842874

权限管家#

权限管家是一个通用的权限管理中台服务,在权限管家中可以管理用户,管理后台系统的菜单,以及角色的管理。

改造登录接口为权限管家的接口

image-20240705150255954

测试

可以看到测试成功

image-20240705150518581

到这里我们的登录接口也完成了

用户端登录和统一网关#

问题

首先是最开始的微信小程序,

在登录接口和小程序登录代码实现完成后

小程序还是会出现400系列错误问题

然后就是token实现后不出token的问题

解决#

对于登录后还是会出现400系错误的问题

这个是由于此项目是我们学校老师为我们配置后出现的小程序账号、密钥不同的原因,导致数据不能正常进行传递引发的客户端错误

通过项目测试打印出账号,进行更换实现成功配置

(这边报错的图忘记截出来了只有最后的实现了)

实现流程#

接下来就该实现用户端的登录和网关配置

因为用户端是基于微信小程序开发的

微信小程序登录的流程:

image-20240717155248822

  1. 点击按钮动态获取phoneCode
  2. wx.login()获取登录code
  3. request向后端发送code、phoneCode
  4. 后端接收并校验登录凭据appid+appsecret+code
  5. 微信接口进行处理
  6. 返回session_key+openid等
  7. 通过openid 向数据库进行用户查询
  8. 后端接收返回用户信息,如果是不存在说明新用户,返回null
  9. 后端向微信接口获取用户手机号接口
  10. 微信进行内部处理
  11. 返回手机号
  12. 后端新增用户或更新用户手机号
  13. 生成token
  14. 将token传入微信小程序
  15. 微信小程序wx.request()请求头携带token
  16. 后端校验token,获取到openid
  17. 后端进行处理
  18. 将响应数据返回微信小程序

登录实现#

env.js中修改本地接口

image-20240706174048764

可以看到本地请求为502

这是由于本地网关并没有配置导致的后面会进行配置

image-20240706174430919

请求参数(2个参数,code:登录临时凭证,phoneCode:获取手机号的凭证):

image-20240706174413712

拉取后端代码 sl-express-ms-web-customer

登录接口#

登录接口的实现是在com.sl.ms.web.customer.controller.UserController#login

image-20240706174821877

可以看到代码是由UserLoginVO响应数据对象和UserLoginRequestVO请求参数对象组成

我们现看看UserLoginVO响应数据对象

UserLoginRequestVO请求参数对象组成

用户微服务#

用户端通过微信登陆流程图

流程图

在Jenkins部署sl-express-ms-user-service

image-20240708105311036

image-20240708105407141

数据库已在服务器部署

小程序登录#

image-20240708105610880

通过小程序登录测试

image-20240708105827124

获取手机号#

想要获取到手机号的要先获取到微信access_token,在获取到手机号,否则获取不到

com.sl.ms.web.customer.service.impl

image-20240709104615345

image-20240709104632774

    @Test
    void getPhone() throws IOException {
        //在测试用例中输入自己获取到的 phonecode
        String phone = wechatService.getPhone("1aaaaa1111");
        System.out.println(phone);
    }
java
实现登录#

com.sl.ms.web.customer.service.impl.MemberServiceImpl#login

image-20240709104923601

生成token实现,这里采用的是RSA加密方式(密钥信息在nacos中的sl-express-ms-web-customer.properties配置)

com.sl.ms.web.customer.service.impl.TokenServiceImp

image-20240709105243144

启动服务,在小程序进行测试

image-20240709105534105

可以看到登录成功

双token三验证#

单token存在的问题#

在日常使用中,我们会频繁的进行登录,而在登录的过程中,会先生成jwt的token,前端接收到token将其保存下来,在进行后端服务请求的时候,在请求中携带token,服务端对token进行校验和鉴权,这种模式叫做 单token模式

在单token模式中,会出现token有效时间的问题,有效时间太短容易过期,太长容易被攻击截取。

在另一方面,token是无状态的,就是说我们不能自如的控制token的状态,无法让他主动失效。

方案设计#

解决问题

  • token有效期长不安全

    • 登录成功后,生成2个token,分别是:access_token、refresh_token,前者有效期短(如:5分钟),后者的有效期长(如:24小时)

    • 正常请求后端服务时,携带access_token,如果发现access_token失效,就通过refresh_token到后台服务中换取新的access_token和refresh_token,这个可以理解为token的续签

    • 以此往复,直至refresh_token过期,需要用户重新登录

  • token的无状态性

    • 为了使token有状态,也就是后端可以控制其提前失效,需要将refresh_token设计成只能使用一次

    • 需要将refresh_token存储到redis中,并且要设置过期时间

    • 这样的话,服务端如果检测到用户token有安全隐患(如:异地登录),只需要将refresh_token失效即可

代码实现#

生成刷新token#

生成刷新refresh_token的主要逻辑有两点:

  • 生成jwt格式的token,有效期时间一般小时为单位
  • 将token存入到redis,使token有状态,并且确保只能使用一次

com.sl.ms.web.customer.service.impl.TokenServiceImpl

public static final String REDIS_REFRESH_TOKEN_PREFIX = "SL_CUSTOMER_REFRESH_TOKEN_";
java

image-20240709111838201

刷新token#

刷新token的动作是在refresh_token过期之后进行的,主要实现关键点有:

  • 校验refresh_token是否被伪造以及是否在有效期内
  • 从redis中查询,是否不存在,如果不存在说明已经失效或已经使用过,如果存在,就需要将其删除
  • 重新生成一对token,响应结果

com.sl.ms.web.customer.service.impl.TokenServiceImpl

image-20240709112251066

在Controller中调用上述方法

com.sl.ms.web.customer.controller.UserController

image-20240709112416024

Service中调用

com.sl.ms.web.customer.service.impl.MemberServiceImpl

image-20240709112532232

完善用户端登录#

用户端登录成功后,需要在补一个token,之前已经写过一个了

image-20240710082924254

测试

image-20240710083035707

网关#

网关作用#

网关能够更好的管理项目分配,每一个客户端都将通过网关传递到服务端

网关就是每个客户端的中间商,他还能对token、以及用户的权限进行有效的校验

比如说:司机不能登录到快递员端,快递员不能登录到司机端

image-20240710095125453

拉取代码#

sl-express-gateway中拉取网关代码:

拉取代码后,创建develop分支进行开发

bootstrap-local.yml

自定义过滤器配置#

image-20240710095615927

过滤器实现#

在过滤器中主要实现以下几个功能:

  • 白名单放行
  • 请求头中的token是否有效
  • 权限是否匹配(校验角色)
  • 向下游微服务传递解析token的数据以及token值

代码实现:

测试#

将网关启动,测试http://127.0.0.1:9527/manager/doc.html#/home看能不能正常看到页面

image-20240710100121839

配置前端

将测试URL改为VUE_APP_BASE_URL = 'http://192.168.150.1:9527/manager'

image-20240710100159599

将代码上传git

在Jenkins构造项目

访问http://admin.sl-express.com/

image-20240710100755861

验证码端口可以看到是9527,部署成功

image-20240710100922312

代码优化#

优化一#

将校验逻辑抽取到独立的类中,这样在每个端的GatewayFilterFactory中就可以共用了。

com.sl.gateway.filter.TokenGatewayFilter

image-20240717161839857

com.sl.gateway.filter.ManagerTokenGatewayFilterFactory

image-20240717161734922

优化二#

通过前面的优化一已经将过滤器抽取到一个独立的类中,这样就可以在多个过滤器工厂中通用了,但也是存在一些问题的,例如:

  • 不同的终端校验的角色id是不同的
  • 用户端是不需要校验角色的
  • 用户端校验token的逻辑与其他三端是不一样的
  • 用户端请求头中的token参数名与其他三端也不一样

基于这些问题,就能够意识到,单纯的抽取代码是不够的,需要进一步的优化。想一想,该怎么优化呢?

优化的思路就是,在TokenGatewayFilter中保留四端通用的逻辑,不同的逻辑抽取到接口中,由四端具体的实现。

自定义AuthFilter接口:

com.sl.gateway.filter.AuthFilter

image-20240717161552569

总结#

在这次实训中,后面其实还有挺多章节的内容,我没有完成,我只是将其完整的看了一遍

虽然没有完成整个实训,但我已经粗略的领略到了项目开发流程,从开始的环境搭建到后面微服务的实现,我学习到了中间件的使用,容器的配置,git的管理等等,这些都是在平常学习中不会遇到的问题,所以这次实训也是为我带来了一些别样的收获

项目总结
https://antifrag.cn/blog/sl%E6%80%BB%E7%BB%93
作者 l6kmw
发布于 2024年7月17日