首页 > 其他分享 >01-项目概述

01-项目概述

时间:2024-04-17 20:37:02浏览次数:24  
标签:01 项目 express token html 概述 proxy sl com

1. 产品说明

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

项目产品主要有 4 端产品:

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

2. 系统&技术架构

2.1 系统架构

2.2 技术架构

3. 功能演示

  • 用户在【用户端】下单后,生成订单
  • 系统会根据订单生成【取件任务】,快递员上门取件后成功后生成【运单】
  • 用户对订单进行支付,会产生【交易单】
  • 快件开始运输,会经历起始营业部、分拣中心、转运中心、分拣中心、终点营业部之间的转运运输,在此期间会有多个【运输单】
  • 到达终点网点后,系统会生成【派件任务】,快递员进行派件作业
  • 最后,用户将进行签收或拒收操作

4. 开发环境

微服务间调用关系:

4.1 软件环境

为了模拟企业中的开发环境,所以我们需要通过 VMware 导入 Linux 虚拟机,该虚拟机中已经安装了课程中所需要的各种环境,包括 Git、Maven私服、MySQL、RabbitMQ 等。只要按照文档导入虚拟机即可,其他软件无需自己安装,都已经安装了,并且开机自启。

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

# 增加如下内容并保存退出
vim ~/.bashrc
>>> alias dps='docker ps --format "table{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}"'
# 生效
source ~/.bashrc

4.2 服务列表

在本机 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

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

名称 地址 用户名/密码 端口
git http://git.sl-express.com/ sl/sl123 10880
maven http://maven.sl-express.com/nexus/ admin/admin123 8081
jenkins http://jenkins.sl-express.com/ root/123 8090
权限管家 http://auth.sl-express.com/api/authority/static/index.html admin/123456 8764
RabbitMQ http://rabbitmq.sl-express.com/ sl/sl321 15672
MySQL - root/123 3306
nacos http://nacos.sl-express.com/nacos/ nacos/nacos 8848
neo4j http://neo4j.sl-express.com/browser/ neo4j/neo4j123 7474
xxl-job http://xxl-job.sl-express.com/xxl-job-admin admin/123456 28080
EagleMap http://eaglemap.sl-express.com/ eagle/eagle 8484
seata http://seata.sl-express.com/ seata/seata 7091
Gateway http://api.sl-express.com/ - 9527
admin http://admin.sl-express.com/ - 80
skywalking http://skywalking.sl-express.com/ - 48080

查看虚机的 Nginx 配置:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
	
    server {
        listen       80;
        server_name  git.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

	location / {
            client_max_body_size  1024m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:10880;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

    }

    server {
        listen       80;
        server_name  maven.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location / { 
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:8081;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  jenkins.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:8090;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  auth.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:8764;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }	

    server {
        listen       80;
        server_name  rabbitmq.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:15672;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  nacos.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:8848;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  neo4j.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:7474;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  xxl-job.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:28080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  eaglemap.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:8484;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }


    server {
        listen       80;
        server_name  seata.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:7091;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  skywalking.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        
        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:48080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    server {
        listen       80;
        server_name  api.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location / {
            client_max_body_size  300m;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
            proxy_pass http://127.0.0.1:9527;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

   server {
        listen       80;
        server_name  admin.sl-express.com;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location / {
            root   /itcast/admin-web;
            index  index.html index.htm;
        }
    }

}

4.3 配置 MVN 私服

在本地的 Maven(建议版本为 3.6.x)配置中配置上述的私服,配置文件参考如下:

<?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>D:\develop\MavenRepository</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>

5. 开发任务

5.1 任务描述

后台管理系统只允许管理员登录,非管理员(司机或快递员)是没有权限登录的,现在的情况是,任何角色的人都能登录到后台管理系统,应该是当非管理员登录时需要提示没有权限。

这个可以算是一个 bug 修复的工作。接下来,你需要思考下,该如何解决这个问题。

解决步骤:

  • 先确定鉴权工作是在哪里完成的 => 通过前面的系统架构,可以得知是在网关中完成的
  • 拉取到网关的代码
  • 阅读鉴权的业务逻辑
  • 了解权限系统
  • 动手编码解决问题
  • 部署,功能测试

5.2 错误复现

(1)部署后台管理系统

后台管理系统的部署是使用 101 机器的 Jenkins 部署的,部署完成后,就可以看到登录页面。

地址:http://admin.sl-express.com/

可以看到,页面是可以正常看到,但没有获取到验证码,是因为验证码的获取是需要后端服务支撑的,目前并没有启动后端服务。

(2)部署后端服务

后端服务需要启动如下几个服务:

目前,只启动了 itcast-auth-server,其他均未启动:

所以需要在 Jenkins 中,依次启动这几个服务,类似如下构建:

启动完成:

在 Nacos 中已经完成了服务的注册:

刷新后台管理系统页面,即可成功看到验证码,说明所需要的服务已经启动完成了。

使用默认账号,shenlingadmin/123456 即可完成登录:

使用非管理员账号进行测试,例如:gzsj/123456 (司机账号) 或 hdkdy001/123456 (快递员账号) 进行测试,发现依然是可以登录的。

至此,问题就重现了。

5.3 拉取代码

(1)在本地创建 sl-express 文件夹,该目录存放项目课程期间所有的代码。

(2)登录 Git 服务,找到 sl-express-gateway 工程,拷贝地址,在 idea 中拉取代码(注意存储路径)

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

(3)拉取后设置 JDK 版本为 11

编译 gateway 服务报错!

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project xxxxx: Fatal error compiling: 无效的目标发行版: 11 -> [Help 1]

可是 IDEA 中已经设置了 JDK11,并且也在 pom.xml 中添加:

<properties>
  <java.version>11</java.version>
  <maven.compiler.source>11</maven.compiler.source> 
  <maven.compiler.target>11</maven.compiler.target>
</properties>

也有建议修改 Maven 的 setting.xml 文件,向文件中添加以下内容:

<profile>
    <id>jdk-11</id>
    <activation>
        <activeByDefault>true</activeByDefault>
        <jdk>11</jdk>
    </activation>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
    </properties>
</profile>

逐一尝试后并未解决前述错误,于是重新检查一遍当前 Maven 关联的 JDK:

D:\WorkSpace\me\sl-express\sl-express-gateway>mvn -v
Apache Maven 3.6.1 (d66c9c0b3152b2e69ee9bac180bb8fcc8e6af555; 2019-04-05T03:00:29+08:00)
Maven home: D:\develop\Maven3.6.1\bin\..
Java version: 1.8.0_231, vendor: Oracle Corporation, runtime: D:\develop\Java\jdk1.8.0_231\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

至此发现错误原因,原来执行 java -version 默认使用系统环境变量 JAVA_HOME,而 Maven 优先使用了 Windows 的用户变量 JAVA_HOME(这里之前忘记修改,仍然指向 java8 路径),修改用户变量 JAVA_HOME 指向 jdk11 或删除该用户变量 JAVA_HOME(如无它用)后即可。

修改环境变量后,重新打开 IDEA:

D:\WorkSpace\me\sl-express\sl-express-gateway>mvn -v
Apache Maven 3.6.1 (d66c9c0b3152b2e69ee9bac180bb8fcc8e6af555; 2019-04-05T03:00:29+08:00)
Maven home: D:\develop\Maven3.6.1\bin\..
Java version: 11.0.22, vendor: Oracle Corporation, runtime: D:\develop\Java\jdk11
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

此时即可正常编译。

5.4 权限管家

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

http://auth.sl-express.com/api/authority/static/index.html

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

对接权限管家需要引入依赖:

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

该依赖已经导入,并且在 parent 中指定了版本号。还已经上传到 Maven 中央仓库,可以直接下载。

https://mvnrepository.com/artifact/com.itheima.em.auth/itcast-auth-spring-boot-starter

5.5 解读配置

在 bootstrap-local.yml 中配置了 Nacos,一些参数存放到了 Nacos 中,这些参数一般都是不同环境不一样配置的。

server:
  port: 9527
  tomcat:
    uri-encoding: UTF-8
    threads:
      max: 1000
      min-spare: 30
spring:
  cloud:
    nacos:
      username: nacos
      password: nacos
      server-addr: 192.168.150.101:8848
      discovery:
        namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
        ip: 192.168.150.1
      config:
        namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origin-patterns: "*"
            allowed-headers: "*"
            allow-credentials: true
            allowed-methods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
      discovery:
        locator:
          enabled: true #表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
      routes:
        - id: sl-express-ms-web-courier
          uri: lb://sl-express-ms-web-courier
          predicates:
            - Path=/courier/**
          filters:
            - StripPrefix=1
            - CourierToken
            - AddRequestHeader=X-Request-From, sl-express-gateway
        - id: sl-express-ms-web-customer
          uri: lb://sl-express-ms-web-customer
          predicates:
            - Path=/customer/**
          filters:
            - StripPrefix=1
            - CustomerToken
            - AddRequestHeader=X-Request-From, sl-express-gateway
        - id: sl-express-ms-web-driver
          uri: lb://sl-express-ms-web-driver
          predicates:
            - Path=/driver/**
          filters:
            - StripPrefix=1
            - DriverToken
            - AddRequestHeader=X-Request-From, sl-express-gateway
        - id: sl-express-ms-web-manager
          uri: lb://sl-express-ms-web-manager
          predicates:
            - Path=/manager/**
          filters:
            - StripPrefix=1
            - ManagerToken
            - AddRequestHeader=X-Request-From, sl-express-gateway
        - id: sl-express-ms-trade
          uri: lb://sl-express-ms-trade
          predicates:
            - Path=/trade/notify/**
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-Request-From, sl-express-gateway
itcast:
  authority:
    host: ${authority.host} #authority服务地址,根据实际情况更改
    port: ${authority.port} #authority服务端口
    timeout: ${authority.timeout} #http请求的超时时间
    public-key-file: auth/pub.key
    applicationId: ${authority.applicationId}

#角色id
role:
  manager: ${role.manager}
  courier: ${role.courier}
  driver: ${role.driver}

sl:
  noAuthPaths:
    - /courier/login/account
    - /courier/swagger-ui.html
    - /courier/webjars/
    - /courier/swagger-resources
    - /courier/v2/api-docs
    - /courier/doc.html
    - /customer/user/login
    - /customer/swagger-ui.html
    - /customer/webjars/
    - /customer/swagger-resources
    - /customer/v2/api-docs
    - /customer/doc.html
    - /driver/login/account
    - /driver/swagger-ui.html
    - /driver/webjars/
    - /driver/swagger-resources
    - /driver/v2/api-docs
    - /driver/doc.html
    - /manager/login
    - /manager/webjars/
    - /manager/swagger-resources
    - /manager/v2/api-docs
    - /manager/doc.html
    - /manager/captcha
  jwt:
    user-secret-key: ${sl.jwt.user-secret-key}

查看 Nacos 中 gateway 相关配置:

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

测试用例在 AuthTemplateTest 中:

@SpringBootTest(properties = "spring.main.web-application-type = reactive")
public class AuthTemplateTest {

    @Resource
    private AuthTemplate authTemplate;
    @Resource
    private TokenCheckService tokenCheckService;
    @Autowired
    private AuthorityProperties authorityProperties;

    @Test
    public void testLogin() {
        // 登录
        Result<LoginDTO> result = this.authTemplate.opsForLogin().token("liujiaqi", "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);
    }

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

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

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

5.6 阅读代码

a. 整体流程

以管理员请求流程为例,其他的流程类似。

b. AuthFilter

该接口定义了 2 个方法,分别是 check() 、 auth() 方法,前者用户校验 token,后者用户鉴权,执行流程是先校验后鉴权。

package com.sl.gateway.filter;

import com.itheima.auth.sdk.dto.AuthUserInfoDTO;

/**
 * 鉴权业务的回调,具体逻辑由 GatewayFilterFactory 具体完成。
 */
public interface AuthFilter {

    /**
     * 校验token
     *
     * @param token 请求中的token
     * @return token中携带的数据
     */
    AuthUserInfoDTO check(String token);

    /**
     * 鉴权
     *
     * @param token        请求中的token
     * @param authUserInfo token中携带的数据
     * @param path         当前请求的路径
     * @return 是否通过
     */
    Boolean auth(String token, AuthUserInfoDTO authUserInfo, String path);
}

c. TokenGatewayFilter

该过滤器是整个校验/ 鉴权流程的具体实现,由于存在不同的终端,导致具体的校验和鉴权逻辑不一样,所以具体处理要由 AuthFilter 实现类完成。

在向下游服务转发请求时,会携带 2 个头信息,分别是 userInfo 和 token,也就是会将用户信息和 token 传递下去。

@Slf4j
public class TokenGatewayFilter implements GatewayFilter, Ordered {

    private MyConfig myConfig;
    private AuthFilter authFilter;

    public TokenGatewayFilter(MyConfig myConfig, AuthFilter authFilter) {
        this.myConfig = myConfig;
        this.authFilter = authFilter;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().toString();
        // 1. 无需校验,直接放行
        if (StrUtil.startWithAny(path, myConfig.getNoAuthPaths())) {
            return chain.filter(exchange);
        }

        // 2. 获取header的参数
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StrUtil.isEmpty(token)) {
            // 没有权限
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        // 3. 校验token 
        AuthUserInfoDTO authUserInfoDTO = null;
        try{
            // *********************************************
            authUserInfoDTO = this.authFilter.check(token);
            // *********************************************
        }catch (Exception e){
            log.error("权限校验失败,e:",e);
        }
        if (ObjectUtil.isEmpty(authUserInfoDTO)) {
            // token失效/伪造
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        // 4. 鉴权
        Boolean result = false;
        try {
            // *********************************************
            result = this.authFilter.auth(token, authUserInfoDTO, path);
            // *********************************************
        }catch (Exception e){
            log.error("鉴权失败,e:",e);
        }
        if (!result) {
            // 没有权限
            exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
            return exchange.getResponse().setComplete();
        }

        // 5. 增加参数
        exchange.getRequest().mutate().header("userInfo", JSONUtil.toJsonStr(authUserInfoDTO));
        exchange.getRequest().mutate().header("token", token);

        // 6. 校验通过放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }
}

d. GatewayFilterFactory

过滤器工厂默认命名规则必须按照「名称 + GatewayFilterFactory」,如 StripPrefixGatewayFilterFactory 的过滤器名称为 StripPrefix。

/**
 * 后台管理员token拦截处理 - 此为TokenGatewayFilter的工厂,并实现了AuthFilter规范(即关于Manger该如何check+auth)
 */
@Component
public class ManagerTokenGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> implements AuthFilter {

    @Resource
    private MyConfig myConfig;
    
    @Resource
    private TokenCheckService tokenCheckService;

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

    @Override
    public AuthUserInfoDTO check(String token) {
        try {
            // 权限管家API-校验token
            return tokenCheckService.parserToken(token);
        } catch (AuthSdkException e) {
            // TODO 校验失败
        }
        return null;
    }

    @Override
    public Boolean auth(String token, AuthUserInfoDTO authUserInfoDTO, String path) {
        return true;
    }
}

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

e. 管理员校验实现

想让管理员角色的用户通过,而非管理员角色不能通过,这里就需要对 auth() 进行实现了,现在的实现是都返回 true,那当然是所有的都通过了。

@Component
public class ManagerTokenGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> implements AuthFilter {

    @Resource
    private MyConfig myConfig;
    @Resource
    private TokenCheckService tokenCheckService;
    
    // --- 获取Nacos配置文件中的司机角色id ---
    @Value("${role.manager}")
    private List<Long> managerRoles;


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

    @Override
    public AuthUserInfoDTO check(String token) {
        try {
            // 校验token
            return tokenCheckService.parserToken(token);
        } catch (AuthSdkException e) {
            // TODO 校验失败
        }
        return null;
    }

    @Override
    public Boolean auth(String token, AuthUserInfoDTO authUserInfoDTO, String path) {
        // 获取AuthTemplate对象
        AuthTemplate authTemplate = AuthTemplateFactory.get(token);
        // 查询登陆用户对应角色ids
        Result<List<Long>> roleByUserId = authTemplate.opsForRole().findRoleByUserId(authUserInfoDTO.getUserId());
        List<Long> roleIds = roleByUserId.getData();
        // 和配置的访问角色 取交集
        Collection<Long> intersection = CollUtil.intersection(roleIds, managerRoles);
        // 判断是否有交集即可判断出是否有权限
        return CollUtil.isNotEmpty(intersection);

    }
}

f. 测试&部署

测试分两种方法,分别是接口测试和功能测试,我们首先进行接口测试,然后再进行功能测试。

测试步骤:

  • 首先,测试管理员的登录,获取到 token
  • 接着测试管理员请求接口资源(期望结果:正常获取到数据)
  • 更换成司机用户进行登录,并且测试请求接口资源(期望结果:响应 400,没有权限)

(1)将本地 Gateway 服务启动起来,访问 http://127.0.0.1:9527/manager/doc.html 即可看到【管理后台微服务接口文档】

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

(3)测试登录接口,需要先获取验证码再进行登录

(4)登录成功

(5)获取到 token

(6)设置请求头 Authorization

(7)进行功能测试

(8)更换成司机账户测试

会发现,更换成司机账户后会响应400,符合我们的预期。测试无误后,可以将代码提交到 Git 中。

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

标签:01,项目,express,token,html,概述,proxy,sl,com
From: https://www.cnblogs.com/liujiaqi1101/p/18141689

相关文章

  • JavaFx项目打包成exe,并集成Jre,使Java项目在任意机器运行
    1.关键点:通过springboot打包插件,将项目依赖都打到一个jar包内。以下是pom配置文件:<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artif......
  • 如何使用KPI评估研发项目
    研发项目对于创新至关重要,但同时也面临不确定性、风险和复杂性。那么该如何衡量和评估它们的表现和影响呢?在本文中,我们将讨论一些关键绩效指标(KPI),这些指标可以帮助您评估您的研发项目的价值和进展。为什么要为研发项目使用KPI?通过使用KPI,即一些可量化的指标,可以有效地衡量和......
  • P3978 [TJOI2015] 概率论 题解
    题意:求一棵\(n\)个节点的有根二叉树的叶子节点的期望个数。设\(f_n\)表示\(n\)个点的二叉树个数,\(g_n\)表示\(n\)个点的所有二叉树的叶子节点数之和。显然\(f_n\)为\(\text{Catalan}\)数,考虑如何求\(g_n\)。一个结论是:\(g_n=f_{n-1}\timesn\)。证明:对于每一......
  • SpringBoot项目 file not present 终局!
    在写一个文件上传接口时,从一个老项目里copy出来了一个接口,死活报错filenotpresent,参考如下步骤排查确保请求的httpheader里面的文件字段名和接口定义一致如果使用postman,则确定key和接口保持一致在保证一切都是对的情况下,检查下项目是否配置了CommonsMultipartResolv......
  • 前端项目安装node-sass依赖问题解决
    前端项目安装依赖node-sass问题解决记录:(项目中node版本14.16.0node-sass版本4.14.1)问题1:pnpnrunall:install后报错MSBUILD:errorMSB3428:解决方法:需要安装npminstall--globalwindows-build-tools1.1、npm全局安装windows-build-tools1.1安装过程中可能会出现......
  • 关于项目中的一些总结和思考(一)
    以前,在某一个项目中担任后端负责人的角色,后端开发大概10人左右,主要负责总体架构设计、工作任务分配和总体进度跟踪把控。做得好的部分:任务的分配能够安排合适的人,项目中关键路径上安排技术能力强,工作态度靠谱的人,某些特别的技术点安排对这块比较熟悉的人并且提前做好技术预研和......
  • uniCloud云函数概述---云对象
    云对象是普通云函数的升级版,功能和云函数是一样的。它在大多数场景下替代了普通云函数。云对象是对象化的云函数,比如一个文章云对象,它可以包括文章的创建,文章的删除,文章的编辑等功能。(一句话描述云对象:等同于PHP后端部份) 一、创建云对象打开项目,找到uniCloud/cloudfuncti......
  • 企业IT部门在集成类项目中扮演的角色
    随着企业信息化建设的不断深入,集成类项目成为企业发展的重要支撑。这类项目涉及场景多、系统杂、实施牵扯广、周期长,对技术和管理的要求极高。企业IT部门(信息化部门)作为企业的技术核心和纽带,对业务需求、技术选型、项目实施等方面具有深入的理解和丰富的经验,其最重要的角色即是参......
  • Azkaban - [01] 概述
    简单的任务调度使用crontab、复杂的任务调度使用oozie、azkaban等开发调度系统。 一、为什么学习Azkaban  一个完整的数据分析系统通常都是由大量任务单元(shell脚本、java程序、MapReduce程序、Hive脚本等)组成。各任务单元之间存在先后及前后依赖关系,为了组织起这样的复杂......
  • MBR40100PT-ASEMI肖特基二极管MBR40100PT
    编辑:llMBR40100PT-ASEMI肖特基二极管MBR40100PT型号:MBR40100PT品牌:ASEMI封装:TO-247最大平均正向电流(IF):40A最大循环峰值反向电压(VRRM):100V最大正向电压(VF):0.88V工作温度:-40°C~170°C反向恢复时间:5ns芯片个数:2芯片尺寸:130mil引脚数量:3正向浪涌电流(IFMS):400A包装方式:50/......