day1-神领物流项目(基于微服务的物流项目)概述

复习时重点看

每个系列中第一个视频为前一个系列的面试总结。

每个系列开头部分有总体流程图介绍 

总体学习路线及技术

对项目课程的总结:

1. 第一天:项目概述
   - 介绍项目的背景和目标。
   - 环境搭建和准备。
   - 学习如何在面试中介绍你的项目,涵盖项目背景、团队规模、技术架构等。

2. 第二天:运费微服务
   - 讲解物流中的运费计算算法。
   - 重点是编写运费计算的代码,适合技术较弱的同学,确保掌握这一模块。

3. 第三天:支付微服务 - 编写代码
   - 对接微信支付和支付宝支付,按官方文档编写代码。 (代码比较复杂,要求debug读懂,支付模块和物流不绑定,每个企业或多或少懂,为面试亮点)

    第三天:支付微服务 - 补齐业务逻辑
   - 以业务为主,补全支付模块的业务逻辑和缺失部分,复杂度较高。(核心)

4. 第四天:路线规划 - Neo4j入门
   - 引入Neo4j图数据库,进行路线规划。

    第五天:路线规划微服务
   - 深入讲解如何使用Neo4j实现物流的路线规划,重点是图数据库的应用。

5. 第六天:智能调度 - 调度任务(调度模块流程较长,较为复杂)
   - 讲解智能调度的核心功能,包括如何安排运输任务和匹配车辆。

    第七天:智能调度 - 运输任务
   - 进一步讲解运输任务的匹配和优化,涵盖详细的调度逻辑。

    第八天:智能调度 - 作业范围
   - 讨论调度中的作业范围,确定网点和快递员的派送任务。

    第九天:智能调度 - 取派件调度

6. 第十天:物流信息微服务
    - 使用多级缓存(如Caffeine和Redis),解决缓存击穿、穿透和雪崩问题。(不是特别难)

7. 第十一天:分布式日志与链路追踪
    - 项目上线排查问题。讲解分布式日志和链路追踪,使用Skywalking和其他框架进行故障排查。

8. 第十二天:项目实战
    - 结合前面学习的内容,进行项目组实战,解决实际问题。

这十二天的课程内容紧密围绕物流项目,涵盖微服务开发、支付集成、智能调度、路线规划及系统优化等多个方面,旨在帮助大家掌握实战技能,提升面试和工作中的竞争力。希望大家在每一天的学习中都能积极参与,掌握每个模块的核心知识点。

项目概述

了解项目背景

神领物流是一个基于微服务架构体系的【生产级】物流项目系统,最接近企业真实场景,按照企业标准编写。物流公司在扩张后要打造一套新的TMS运输系统。业务侧重于展示车辆调研、线路规划等核心业务流程,操作智能化,大幅度提升人效及管控效率。

教学级的项目会在真实项目基础上做阉割。此项目除去自己开发的代码,其他代码也都在项目中。

项目组属于开发部门,开发部门有仓储系统WMS、运配系统TMS、单据系统OMS、计费系统BMS。项目四组。TMS项目组目前共8人,其中前端3人,后端5人,分模块开发。

神领物流系统类似顺丰速运,是向C端用户提供快递服务的系统。竞品有:顺丰、中通、圆通、京东快递等。 项目产品主要有4端产品:

注:c端为互联网,b端为企业

    1 用户端:基于微信小程序开发,外部客户使用,可以寄件、查询物流信息等。                              2 快递员端:基于安卓开发的手机APP,公司内部的快递员使用,可以接收取派件任务等。            3 司机端:基于安卓开发的手机APP,公司内的司机用,可以接收运输任务、上报位置等。            4 后台系统管理:基于vue开发,PC端使用,公司内部管理员用户使用,可以进行基础数据维护、订单管理、运单管理等。

功能架构:

RabbitMQ:消息队列,xxl.job:任务调度,seata:分布式事务,阿里云:发短信,图片视频等信息存储到oss,美团leaf:分布式id(面试点,面试官没见过但重要),支付宝微信:支付,skywalking:微服务出现问题进行链路追踪
MySQL:核心数据,MongoDB:1存储数据量大 2增删改查多的非核心数据 3适合存坐标数据
Elasticsearch:查询复杂,海量的数据,但对于微服务太大,用MongoDB解决
Redis:基于内存缓存的场景,分布式锁
Neo4j:求两点之间的最短路径(面试点,没用过但好用)

技术架构:

流程:                                                                                                                                                   1:四个端所有请求先通过nginx反向代理到网关中                                                                             2:在网关层进行验证能否登录,若可以,请求会分发到微服务(通过nacos获得微服务的地址,所有微服务都要将自己的信息,配置注册到nacos中)                                                                         3:微服务相互调用使用feign组件。(其中负载均衡由ribbon实现,熔断降级由sentinel实现,都是配置好的)     

 注:服务熔断是一种保护机制,用来防止一个微服务的故障蔓延到整个系统。当一个微服务出现问题时,熔断器会暂时中断对该服务的调用,避免影响其他服务的正常运行。   

服务降级是指在服务不可用或者响应超时的情况下,提供一个降级方案,比如返回一个预设的默认值或执行一个备用逻辑,以确保系统的基本功能可用。                                                               

    4:每一个微服务都对外提供增删改查的mvc层操作,都是由SpringBoot创建,用的mvc层框架组成为Spring+SpringMVC+Mybatisplus(如果操作MySQL为Mybatisplus,neo4j和MongoDB则用SpringData系列的框架,例如操作Reids时使用的为SpringDataRedis)

一些基础依赖服务:                                                                                                              redisson:分布式加锁    Jenkins(老头):持续集成,项目写完用他部署    seata:分布式事务    美团leaf:生成订单id    EagleMap:操作地图(高德,百度)    skywalking:进行链路追踪          传智权限管家: 抽取出来管理权限的    MyBatis-Plus:操作数据库的                                Knife4j:底层基于Swagger提供接口文档    Hutool:工具类(语法直观简单)

服务名称版本备注
MySQL8.0.29业务数据存储
Redis7.0.4用作缓存以及分布式锁
RabbitMQ3.9.17需要集成官方的延迟队列插件
Nacosv2.1.0配置中心与注册中心解决方案
Neo4j4.4.5基于图数据库计算运输路线
xxl-job2.3.0分布式定时任务框架
MongoDB4.4存储轨迹、作业范围等数据
Seata1.5.2分布式事务解决方案
Elasticsearch7.17.5分布式全文索引解决方案
Skywalking9.1.0链路追踪解决方案
Graylog4.3分布式日志解决方案
Leaf1.0.1美团点评分布式ID生成系统
EagleMap1.0黑马程序员自研开源项目
EagleMap - 地图服务中台 黑马程序员·研究院
权限管家1.0.7黑马程序员自研开源项目
权限管家 - 后端服务: 传智权限管家系统基于SpringCloud(Hoxton.SR3) +SpringBoot(2.2.5.RELEASE) 的微服务框架,是一款标准的RBAC权限关系理系统,并实现了多应用统一认证鉴权,统一权限管理的脚手架
nexus2.15.1maven私服
gogs0.12git代码管理
Jenkinslts-jdk11持续集成

实际使用:                                                                                                                                         
具体写代码在某个微服务中写。                                                                                                        
查看微服务启动成功否,在nacos注册中心中。
修改服务配置IP,在nacos配置中心中。
写一个定时任务,在xxl.job中
知道在哪里找代码即可,不用背代码

业务功能流程:

说明:

用户在【用户端】填写订单信息并下单后,生成订单
系统会根据订单地址范围内生成【取件任务】,
快递员上门取件成功后,根据重量计算运费,选择到付或寄付,基于订单生成【运单,运输过程基于运单转变状态】
用户对订单进行支付(wx或支付宝),会产生【交易单】
支付完成后由自动调度系统进行调度运输,并生成【运输单】,每一次运输是一个运输任务对应一个运输单
快件开始运输,会经历起始营业部、分拣中心、转运中心(转运中心到转运可能需要循环多次)、分拣中心、终点营业部之间的转运运输,在此期间会有多个【运输任务】
运输中依次往下个网点运输到达终点网点后,根据范围,排班,系统会生成【派件任务】,选择快递员进行派件作业
最后,用户将进行签收或拒收操作

开发模式、团队分工和开发环境的设置:

一、整体开发模式
项目采用前后端分离的开发模式:
前端已经开发完成,包括小程序、后台管理页面和APP。
后端开发采用微服务架构,共分为五个开发小组,每个小组负责一部分微服务。

二、开发环境
项目分为三个开发环境:
开发环境:开发人员在本地使用IDE进行开发和调试。
测试环境:代码经过本地测试后发布到测试环境,由测试团队进行测试,发现bug后进行修复。
生产环境:测试完成后,代码发布到生产环境,供外部互联网用户访问。
在学习过程中,模拟两个环境(开发环境和测试环境),其中测试环境通过虚拟机模拟,生产环境未搭建。

三、团队分工
项目分为五个开发小组:
第一组:负责基础工程,包括网关和基础服务。
其他小组:各自负责不同的微服务模块。

四、项目结构
项目采用独立的Project方式,每个微服务都是一个独立的项目,而不是聚合到一个项目中。这种方式有以下优点:
安全性:新的程序员只能接触到自己负责的代码,减少了代码泄漏的风险。
降低耦合度:各个微服务独立,避免了不必要的耦合。
减少编译错误:减少因为其他组的微服务出现错误而不会修改

五、依赖管理
由于各个微服务独立,可能会有相互依赖的情况,这通过Maven私服来解决:
公共依赖包:如parent工程、common依赖等发布到Maven私服中。
依赖解决:开发组通过Maven配置文件(配置坐标)从私服拉取依赖包,确保项目编译和运行。

六、拉取代码
新人入职后,只需要拉取自己负责的代码进行开发和调试:
只拉取相关工程:避免拉取无关代码,减少编译时间和不必要的错误。
解决依赖问题:通过向老员工获取setting.xml配置文件或整个仓库数据,解决依赖包的问题。

七、公司内部环境
公司会维护一个统一的测试环境或开发环境服务器,运行所有的微服务。开发人员可以在本地调试自己的服务,并通过内网调用其他服务。这种方式避免了在本地运行所有微服务的负担。

微服务调用关系:

四个前端(请求前缀路径不同)的请求发送到网关
四个微服务中接收对应四个端的请求。
四个微服务(写controller方法端口发送的请求地址)接收到请求之后,再去找基础微服务(基础微服务就是按照业务来划分的)     

                                                          

搭建项目环境

神领物流项目使用的JDK版本为11

开发环境说明

说明:

    加入开发团队后,首先需要从git仓库中拉取项目代码

    拉取到项目代码后,项目会通过Maven私服下载所需要的jar包

    开发工程师按照需求文档进行业务开发,在开发完成后将代码推送到git仓库

    代码推送到git后,git会通知Jenkins(自动化部署系统)

    Jenkins接收到通知后,拉取到最新的代码,并且将服务部署到服务器

导入虚拟机

为了模拟企业中的开发环境,需要通过VMware导入linux虚拟机,该虚拟机中已经安装了课程中所需要的各种环境,包括,git、maven私服、Jenkins、MySQL、RabbitMQ等。

果核剥壳:网页直接下载,这里下载16.2.5版本 

VMarepro16安装流程

密钥(三选一即可)

ZF3RO-FHED2-M80TY-8QYGC-NPKYF
YF390-OHF8P-M81RQ-2DXQE-M2UT6
ZF71R-DMX85-08DQY-8YMNC-PPHV8

神领物流虚拟机CentOS7导入手册

账号:root          密码:123321

在本linux中gogs代替git用来搭建个人仓库(gitlab太大),用法一样

通过dps命令可以查询上述列表,dps是自定义命令。 自定义命令方法如下:

vim ~/.bashrc

#增加如下内容
alias dps='docker ps --format "table{
  
  {.ID}}\t{
  
  {.Names}}\t{
  
  {.Status}}\t{
  
  {.Ports}}"'
#保存退出

#生效
source ~/.bashrc

IP+端口,域名的使用——概念及例子

使用finalshell连接centos7虚拟机

用户名:root   密码:123321

使用本地MySQLWorkbench连接虚拟机mysql

开启防火墙端口,虚拟机输入以下命令
sudo /sbin/iptables -I INPUT -p tcp --dport 3306 -j ACCEPT 

连接时conection name,default schema都不用输入

用户名密码:root/123

连接成功

配置本机hosts

在本机hosts文件中设置如下配置: 

192.168.150.101 git.sl-express.com
192.168.150.101 maven.sl-express.com
192.168.150.101 jenkins.sl-express.com
192.168.150.101 auth.sl-express.com
192.168.150.101 rabbitmq.sl-express.com
192.168.150.101 nacos.sl-express.com
192.168.150.101 neo4j.sl-express.com
192.168.150.101 xxl-job.sl-express.com
192.168.150.101 eaglemap.sl-express.com
192.168.150.101 seata.sl-express.com
192.168.150.101 skywalking.sl-express.com
192.168.150.101 api.sl-express.com
192.168.150.101 admin.sl-express.com

在linux上安装了nginx,根据域名的不同反向代理到指定的端口上

安装utools,安装hosts插件,使用管理员运行,在开发环境中复制以下配置。(双击绿点生效)

打开浏览器测试:NameBright - Coming Soon,显示以下页面则成功

服务列表

说明:如果通过域名访问,无需设置端口。

神领物流所有服务的介绍及安装流程

名称地址用户名/密码端口
gitNameBright - Coming Soonsl/sl12310880
nexus(maven私服)NameBright - Coming Soonadmin/admin1238081
jenkinsNameBright - Coming Soonroot/1238090
权限管家NameBright - Coming Soonadmin/1234568764
RabbitMQNameBright - Coming Soonsl/sl32115672
MySQL-root/1233306
nacosNameBright - Coming Soonnacos/nacos8848
neo4jNameBright - Coming Soonneo4j/neo4j1237474
xxl-jobNameBright - Coming Soonadmin/12345628080
EagleMapNameBright - Coming Sooneagle/eagle8484
seataNameBright - Coming Soonseata/seata7091
GatewayNameBright - Coming Soon-9527
adminNameBright - Coming Soon-80
skywalkingNameBright - Coming Soon-48080
Redis-1233216379
MongoDB-sl/12332127017

对于gogs(git),用到哪个仓库将哪个仓库的代码下载。例如搜索到网关后,复制链接下载

每一个仓库两个分支,complete为写好的用于参考。master为自己编写的

配置Maven私服

公司的jar包会在上面发布

在本地的maven(建议版本为3.6.x)配置中配置,配置文件参考如下: settings.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<settings
        xmlns="http://maven.apache.org/SETTINGS/1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <!-- 本地仓库 -->
    <localRepository>F:\maven\repository</localRepository>

    <!-- 配置私服中deploy的账号 -->
    <servers>
        <server>
            <id>sl-releases</id>
            <username>deployment</username>
            <password>deployment123</password>
        </server>
        <server>
            <id>sl-snapshots</id>
            <username>deployment</username>
            <password>deployment123</password>
        </server>
    </servers>

    <!-- 使用阿里云maven镜像,排除私服资源库 -->
    <mirrors>
        <mirror>
            <id>mirror</id>
            <mirrorOf>central,jcenter,!sl-releases,!sl-snapshots</mirrorOf>
            <name>mirror</name>
            <url>https://maven.aliyun.com/nexus/content/groups/public</url>
        </mirror>
    </mirrors>

    <profiles>
        <profile>
            <id>sl</id>
            <!-- 配置项目deploy的地址 -->
            <properties>
                <altReleaseDeploymentRepository>
                    sl-releases::default::http://maven.sl-express.com/nexus/content/repositories/releases/
                </altReleaseDeploymentRepository>
                <altSnapshotDeploymentRepository>
                    sl-snapshots::default::http://maven.sl-express.com/nexus/content/repositories/snapshots/
                </altSnapshotDeploymentRepository>
            </properties>
            <!-- 配置项目下载依赖的私服地址 -->
            <repositories>
                <repository>
                    <id>sl-releases</id>
                    <url>http://maven.sl-express.com/nexus/content/repositories/releases/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                </repository>
                <repository>
                    <id>sl-snapshots</id>
                    <url>http://maven.sl-express.com/nexus/content/repositories/snapshots/</url>
                    <releases>
                        <enabled>false</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
        </profile>
    </profiles>

    <activeProfiles>
         <!-- 激活配置 -->
        <activeProfile>sl</activeProfile>
    </activeProfiles>

</settings>

仓库依赖管理:
本地开发环境依赖管理分为两个仓库:
私服仓库:存放公司内部的依赖包。
中央仓库:使用阿里云镜像下载开源依赖包。
项目中的依赖分为开源依赖(从中央仓库下载)和公司内部依赖(从私服下载)。


私服仓库分为:
snapshot 仓库:存放开发中的临时版本,不稳定。
release 仓库:存放稳定版本,建议主要依赖该版本。
可以通过私服页面查看,管理,搜索不同版本的依赖包。

服务版本

nexus(maven私服)中可以查看依赖包

服务名版本号
sl-express-parent1.4
sl-express-common1.2-SNAPSHOT
其他微服务1.1-SNAPSHOT

开发任务

接下来是你加入到开发一组后接到的第一个任务,具体内容是:
后台管理系统只允许管理员登录,非管理员(司机或快递员)是没有权限登录的,现在的情况是,任何角色的人都能登录到后台管理系统,应该是当非管理员登录时需要提示没有权限。 这个可以算是一个bug修复的工作。接下来,你需要思考下,该如何解决这个问题。

解决步骤:(其中找文档,了解上下文逻辑可能比写代码更费时间)
      1 先确定鉴权工作是在哪里完成的(当前要做的功能在哪个微服务中解决,可以问项目经理)
              通过前面的系统架构,可以得知是在网关中完成的
      2 拉取到网关的代码(从项目经理处获取git账号密码)
      3 阅读鉴权的业务逻辑
      4 了解权限系统(要权限系统的文档,了解代码的上下文)
      5 动手编码解决问题
      6 部署,功能测试

部署后台管理系统

构建前端

总共有四个前端,这里先部署pc端

pc管理端是需要将前端开发的vue进行编译,发布成html,然后通过nginx进行访问,这个过程已经在Jenkins中配置,执行点击发布即可。

Jenkins地址:NameBright - Coming Soon

找到前端开发的vue进行构建

实际执行的底层命令

将打包后的html等静态文件拷贝到虚拟机指定目录下:

接下来的部分日志展示了通过 SSH 执行的部署操作:

说明:

1.SSH 脚本:
rm -rf /itcast/admin-web/*:这是一个 Linux 命令,用于删除目标目录 /itcast/admin-web/ 中的所有文件和子目录。rm -rf 强制删除所有内容而不提示确认。
cp /usr/local/src/jenkins/workspace/project-slwl-admin-vue/dist/* /itcast/admin-web/ -R:这是另一个 Linux 命令,用于将构建后的文件从 Jenkins 工作空间中的 dist 目录复制到目标目录 /itcast/admin-web/。-R 选项表示递归复制,包括所有子目录和文件。

2.SSH 执行:
[SSH] executing...:表示开始通过 SSH 执行上述脚本。
[SSH] completed:表示脚本执行完成。
[SSH] exit-status: 0:表示脚本执行成功。exit-status: 0 是一个标准的退出状态码,表示操作成功完成。

查看nginx配置文件内容

nginx所在目录:/usr/local/src/nginx/conf

反向代理

在在浏览器中输入以下域名admin.sl-express.com,nginx反向代理转发到构建的vue前端中

这里的验证码需要启动后台的微服务提供

构建后端微服务

所以需要在Jenkins中,依次启动这几个服务,类似如下构建(需要找到对应的构建任务进行构建,使用默认参数进行构建):

说明:

  • 停止并删除旧的 Docker 容器和镜像(如果存在)。
  • 使用 Dockerfile 构建一个新的 Docker 镜像。
  • 启动新构建的 Docker 容器。

可以在dockers或者nacos中查看具体的微服务(例如网关gateway有没有启动)

部署完成后,即可在前端查看验证码

测试发现bug

拉取网关代码

步骤:

  • 在本地创建 sl-express 文件夹后用idea打开,该目录存放项目课程期间所有的代码(企业内只需要拉取一个微服务进行修改,自己学习则拉取全部,每拉取一个点file新建一个project)

配置环境

(1)jdk配置为11

电脑切换不同的JDK版本使用

( 2)maven地址配置

 注:这里我们新建一个项目专属的maven,更改地址为新建maven地址

git中拉取代码

注:需要启动虚拟机,才可以使用服务列表的服务

在git中搜索找到gateway分支,拉取master分支

点击cancel(this会挤掉之前的项目,new会新开一个窗口)

将导入项目识别为maven项目

这里idea报错Could not find artifact com.sl-express:sl-express-parent:pom:1.4 in central (https://repo.maven.apache.org/maven2),重新查看maven私服配置发现未配置maven-conf-setting.xml文件(复制上文更改好的setting.xml文件)。

检查后发现本地仓库已经拉取到相关依赖,但idea仍然报错。

问题转变为maven本地仓库有包但idea识别不到

找到方法:删除对应依赖目录下的_remote.repositories文件(未解决问题)

IDEA maven项目中刷新依赖的两种方法

后删除maven仓库文件,重新加载项目使其重新下载依赖

解决报错Could not find artifact com.sl-express:sl-express-parent:pom:1.4 in central

注意:重新加载前看自己的maven配置是否正确

仍有报错Plugin 'org.springframework.boot:spring-boot-maven-plugin:' not found错误

判断原因:清理缓存后,再次打开项目会重新对仓库中已有的文件构造index,所以找到了仓库中的文件进行构建索引

如果仍有报错,则添加版本号。(对应仓库下有版本号文件夹时)

说明:该工程会依赖 sl-express-parent,此maven依赖是通过私服拉取到的。

如果想在之后导出项目中全部代码,则在git中将parent,common也拉取下来。

权限管家

说明

在神领物流项目中,快递员、司机、管理人员都是在权限管家中进行管理的,所以他们的登录都是需要对接权限管家完成的。

具体权限管家的介绍说明参见:权限管家使用说明 (注:虚拟机中已安装,无需再部署)

测试用户登录及校验token

前面已经了解了权限管家,接下来我们将基于权限管家在sl-express-gateway中进行测试用户的登录以及对于token的校验。

说明

对接权限管家需要引入依赖(该依赖已经导入,并且在parent中指定了版本号):

<dependency>
    <groupId>com.itheima.em.auth</groupId>
    <artifactId>itcast-auth-spring-boot-starter</artifactId>
</dependency>

该依赖已经上传到maven中央仓库,可以直接下载,地址:https://mvnrepository.com/artifact/com.itheima.em.auth/itcast-auth-spring-boot-starter

nacos ip设置为192.168.150.1原因

nacos,网关,支付等服务都在jenkins中跑,在虚拟机中 。如果nacos不注册为192.168.150.1,泽本地会随机注册一个ip地址(如10.11.121.11)导致与网关不在同一网段下,访问不到运费服务。

此时若想访问运费服务则需将该服务发到虚拟机中启动,这样就不能在本地debug。

需要变化的配置在nacos中(根据服务名称在nacos中寻找)如下文配置文件中的

role: manager: ${role.manager} courier: ${role.courier} driver: ${role.driver}

使用*sl-express-gateway*进行模糊查找,点击编辑
#sl-express-gateway.properties如下:
#权限系统的配置
authority.host = 192.168.150.101
authority.port = 8764
authority.timeout = 10000 #超时时间
#应用id
authority.applicationId = 981194468570960001

#角色id(有四种角色可以进行管理)
role.manager = 986227712144197857,989278284569131905,996045142395786081,996045927523359809
#快递员角色
role.courier = 989559057641637825
#司机角色
role.driver = 989559028277315009

#RSA公钥
sl.jwt.public-key = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC6of/EqnM2008gRpFAJJd3iGF5o6P6SuJOcKq4iJQ+62EF4WKGIGQunJjPwVNQFqDuT7ko9bRFZNnMba9A5GrFELtAK7tzO9l19JgFcCBQoU3J6ehPCCunRKz52qJuzS0yiJp0dbB2i6cb7mSCftwZavmcpzhsBaOGQd23AnAmQIDAQAB

其中applicationId、角色id都是需要在权限系统中找到。

角色id需要在数据库表中查询,表为:itcast_auth.itcast_auth_role

测试

测试用例在AuthTemplateTest中:

    @Test
    public void testLogin() {
        //登录
        Result<LoginDTO> result = this.authTemplate.opsForLogin()
                .token("shenlingadmin", "123456");

        String token = result.getData().getToken().getToken();
        System.out.println("token为:" + token);

        UserDTO user = result.getData().getUser();
        System.out.println("user信息:" + user);

        //查询角色
        Result<List<Long>> resultRole = AuthTemplateFactory.get(token).opsForRole()
                .findRoleByUserId(user.getId());
        System.out.println(resultRole);
    }

token校验测试:

    @Test
    public void checkToken() {
        String token = "xxx.xx.xxx"; //上面方法中生成的token
        AuthUserInfoDTO authUserInfo = this.tokenCheckService.parserToken(token);
        System.out.println(authUserInfo);

        System.out.println(JSONUtil.toJsonStr(authUserInfo));
    }

说明:权限管家生成的token采用的是RSA非对称加密方式,项目中配置的公钥一定要与权限系统中使用的公钥一致,否则会出现无法校验token的情况。

即项目中的公钥从虚拟机的权限管家中获取

阅读鉴权代码

流程

首先需要明确的一点是四个终端都是通过sl-express-gateway进行验证与鉴权的,下面以管理员请求流程为例,其他的流程类似。

不同终端的鉴权实现(自定义过滤器)

不同终端进入Gateway的请求路径不同,且对token的校验和鉴权逻辑不同,故通过在网关中对不同终端创建不同的过滤器来实现。 请求路径如下:

快递员端:/courier/**
用户端:/customer/**
司机端:/driver/**
管理端:/manager/**

其中,配置文件中自定义过滤器配置了4个(对应四个过滤器类)

本地配置文件中的路由配置项

其中自定义过滤器CourierToken会自动匹配对应的类CourierTokenGatewayFilterFactory

原理:当Spring Boot应用启动时,Spring Cloud Gateway会:

1:扫描所有Spring Bean:Spring Boot会扫描所有使用@Component、@Service、@Repository、@Controller等注解标记的类,并将它们注册为Spring Bean。
2:加载过滤器工厂:Spring Cloud Gateway会查找所有继承自AbstractGatewayFilterFactory的Bean,并将它们作为过滤器工厂加载。
3:匹配配置文件中的过滤器:根据配置文件中的过滤器名称(如CourierToken),Spring Cloud Gateway会自动查找并应用相应的过滤器工厂类实例。

具体的业务逻辑,在自定义TokenGatewayFilter类中的filter()方法中完成。执行流程是先校验token的有效性,再进行鉴权。

bug发现

由于auth()方法直接返回true,导致所有角色都能通过校验,也就是所有角色的用户都能登录到后台管理系统,这里就是bug原因的根本所在。

auth()方法逻辑(根据不同角色判断是否有权限)
    1:获取AuthTemplate对象
    2:查询登录用户对应角色
通过authTemplate对象的 opsForRole() 方法获取与角色操作相关接口,并调用findRoleByUserId 方法查询指定用户的角色ID。
authUserInfoDTO.getUserId() 获取当前用户的ID,作为查询角色的参数。
getData() 方法用于从 Result 对象中提取实际的数据(即用户角色ID列表)
    3:与配置的访问角色取交集
CollUtil.intersection 是 Hutool 工具包中的方法,用于计算两个集合的交集。
@Value("${role.manager}"): 这个注解告诉Spring框架,要从配置文件中读取以 role.manager 开头的配置项,并将其的值注入到 managerRoleIds 变量中。

在配置文件中配置好了nacos,在sl-express-gateway.properties中寻找

#sl-express-gateway.properties如下:

#权限系统的配置
authority.host = 192.168.150.101
authority.port = 8764
authority.timeout = 10000 #超时时间
#应用id
authority.applicationId = 981194468570960001

#角色id(有四种角色可以进行管理)
role.manager = 986227712144197857,989278284569131905,996045142395786081,996045927523359809
#快递员角色
role.courier = 989559057641637825
#司机角色
role.driver = 989559028277315009

#RSA公钥
sl.jwt.public-key = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDC6of/EqnM2008gRpFAJJd3iGF5o6P6SuJOcKq4iJQ+62EF4WKGIGQunJjPwVNQFqDuT7ko9bRFZNnMba9A5GrFELtAK7tzO9l19JgFcCBQoU3J6ehPCCunRKz52qJuzS0yiJp0dbB2i6cb7mSCftwZavmcpzhsBaOGQd23AnAmQIDAQAB

    4:判断是否有交集从而判断是否有权限

测试

将本地springboot的Gateway服务启动起来,访问swagger测试  即可看到【管理后台微服务接口文档】

出现上述错误,在jenkins中构建该项目即可。

随便测试个接口,会发现响应401

测试登录接口,需要先获取验证码再进行登录:key的值后续在登录时还要输入

以创建的zhangsan账号登录,密码123456

设置全局参数Authorization及其值(上文token的值),刷新页面生效

构建sl-express-ms-base 服务的实例,才能使用行政机构的服务。

使用管理员的账号测试,主要测试鉴权服务

部署服务

部署

首先将本地开发的代码进行git提交

项目的发布,我们采用Jenkins持续集成的方式,在提供的虚拟机中已经部署好了Jenkins,我们只需要进行简单的操作即可完成部署。
第一步,浏览器打开:sl-express.com  (账号:root/123)
第二步,按照如下数字标识进行操作

选择默认参数:

第三步在jenkins中进行构建项目

注:需要把本地的gateway项目停止,否则nacos中会注册两个网关服务(会报错)

测试上传的服务

停止本地服务后,在jenkinbs中构建project-slwl-admin-vue服务

在admin.sl-express.com登录进行测试

测试成功,快递员zhangsan权限不足,不能登录

注:本地服务只能用swagger进行测试,上传后用jenkins启动gateway后,前端才能显示验证码

作业

day1课后练习-CSDN博客

注:只有用户端不需要鉴权,登录成功就返回true。且check只返回用户id

注:司机端和用户端均为小程序,后续开发后进行测试

作业1:司机端

package com.sl.gateway.filter;

import cn.hutool.core.collection.CollUtil;
import com.itheima.auth.factory.AuthTemplateFactory;
import com.itheima.auth.sdk.AuthTemplate;
import com.itheima.auth.sdk.dto.AuthUserInfoDTO;
import com.itheima.auth.sdk.service.TokenCheckService;
import com.sl.gateway.config.MyConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;

/**
 * 司机端token拦截处理
 */
@Component
@Slf4j
public class DriverTokenGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> implements AuthFilter {

    @Resource
    private MyConfig myConfig;

    @Resource
    private TokenCheckService tokenCheckService;

    //怎么使用@Value?@Value(${role.Driver})报错
    @Value("${role.Driver}")
    List<Long> roleDriverIds;


    @Override
    public GatewayFilter apply(Object config) {

        return new TokenGatewayFilter(this.myConfig, this);
    }

    @Override
    public AuthUserInfoDTO check(String token) {
        //校验token
        return this.tokenCheckService.parserToken(token);
    }

    @Override
    //通过用户信息自带的角色id和在配置文件查出来的有权限的角色id做交集,看有无权限
    public Boolean auth(String token, AuthUserInfoDTO authUserInfoDTO, String path) {

        //通过AuthTemplateFactory的.get(token)方法构造AuthTemplate对象,用来获取用户角色信息
        AuthTemplate authTemplate = AuthTemplateFactory.get(token);
        //此处传来的??AuthUserInfoDTO信息哪里得到,TokenGateway和DriverTokenGateway的调用顺序
        Long userId = authUserInfoDTO.getUserId();
        //获取userId对应的角色信息
          //.findRoleByUserId(userId)方法返回的时Result<List<Long>>对象,要用.getData获得数据
        List<Long> roleIds = authTemplate.opsForRole().findRoleByUserId(userId).getData();
        //使用工具CollUtil.intersection测试是否有交集
        Collection<Long> intersection = CollUtil.intersection(roleIds, roleDriverIds);
        return intersection.isEmpty();

    }
}

作业2 :用户端

package com.sl.gateway.filter;

import cn.hutool.jwt.JWTUtil;
import com.itheima.auth.sdk.dto.AuthUserInfoDTO;
import com.sl.gateway.config.MyConfig;
import com.sl.gateway.properties.JwtProperties;
import com.sl.transport.common.constant.Constants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * 用户端token拦截处理
 */
@Slf4j
@Component
public class CustomerTokenGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> implements AuthFilter {

    @Resource
    private MyConfig myConfig;

    @Resource
    private JwtProperties jwtProperties;

    @Override
    public GatewayFilter apply(Object config) {
        return new TokenGatewayFilter(this.myConfig, this);
    }

    @Override
    public AuthUserInfoDTO check(String token) {
        // 普通用户的token没有权力对接权限系统,需要自定实现
          //即无法使用AuthTemplate对象,AuthUserInfoDTO,TokenService对象中的方法
          //自定义解析Token即可

        // 得到DefaultJwtParser
        Map<String, Object> claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(jwtProperties.getPublicKey().getBytes(StandardCharsets.UTF_8))
                // 使用 parseClaimsJws 方法解析JWT令牌,并提取其中的声明(claims)。
                .parseClaimsJws(token).getBody();

        //AuthUserInfoDTO.bulider(),不可使用builder方法,AuthUserInfoDTO为拉取的依赖,未注入Bean容器
        AuthUserInfoDTO authUserInfoDTO = new AuthUserInfoDTO();
        //这里只需封装userId信息即可
        Long userId = Long.valueOf(claims.get("userId").toString());
        authUserInfoDTO.setUserId(userId);
        return authUserInfoDTO;
    }

    @Override
    public Boolean auth(String token, AuthUserInfoDTO authUserInfoDTO, String path) {
        //普通用户不需要校验角色
          //所以只需要token解析进行判断即check
        return true;
    }

    @Override
    public String tokenHeaderName() {
        return Constants.GATEWAY.ACCESS_TOKEN;
    }
}


修改:

 public AuthUserInfoDTO check(String token) {
        // 普通用户的token没有权力对接权限系统,需要自定实现
          //即无法使用AuthTemplate对象,AuthUserInfoDTO,TokenService对象中的方法
          //自定义解析Token即可
        try {
//        基于JwtUtils解析token获取Claims内容
            Map<String, Object> claims = JwtUtils.checkToken(token,jwtProperties.getPublicKey());
            //这里只需封装userId信息即可
            Long userId = Long.valueOf(claims.get("userId").toString());
            //AuthUserInfoDTO.bulider(),不可使用builder方法,AuthUserInfoDTO为拉取的依赖,未注入Bean容器
            AuthUserInfoDTO authUserInfoDTO = new AuthUserInfoDTO();
            authUserInfoDTO.setUserId(userId);
            return authUserInfoDTO;
        }catch (Exception e) {
            log.error(">>>>>>>>>>>>>>>>>> 解析用户登录token失败 >>>>>>>>>>>>>>>>");
            return null;
        }
    }

作业3: tokengateway整体逻辑

(1)获取请求路径看是否在白名单中

(2) 从请求头header中获取Token(微服务想通过网关访问则需携带token的头信息),有权限放行,无权限报错

(4)调用authFilter.check(token)方法校验Token,捕获异常并记录日志,如果Token无效,返回401未授权状态。

(5)调用authFilter.auth(token, authUserInfoDTO, path)方法进行权限验证,捕获异常并记录日志,如果鉴权失败,返回400错误请求状态。

(6)增加参数,向下游微服务传递参数(向下游微服务传递用户信息和Token)用以访问其他微服务

(7)如果校验和鉴权通过则放行,请求传递给过滤器链中的下一个过滤器。

了解代码具体怎么写的后再回头看看整体流程

面试问题

总结:项目公司介绍,鉴权流程,用到哪些技术

技术:VMare虚拟机,jenkins部署服务,gogs(git)提交代码,nacos存储可变的配置(在本地配置文件中配置),maven配置私服(从私服拉取代码逻辑),权限管家的auth鉴权check解析令牌服务,柯knf4j(swagger)测试代码,

面试官问:

  • 能介绍一下你做的这个神领物流项目么? (背景 分为几个端 用到哪些技术)
  • 你们开发部门的组成是怎样的?
  • 你们的项目权限是如何管理的呢?
  • 你们公司项目是如何部署的呢?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值