手动搭建gateway,项目集成gateway实现Token效果

目录

  • 背景
  • 步骤
    • 1、首先创建springboot项目
    • 2、引入依赖
    • 3、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)
    • 4、相关类
    • 我们在服务中进行的白名单中接口的操作如下
  • 测试
    • 存:
    • 拿:
  • 总结

背景

现在想要进行token校验,故引入gateway服务。
首先阅读官网,知道概念:Gateway官网详解

步骤

1、首先创建springboot项目

看整体的目录结构
在这里插入图片描述

2、引入依赖

 <dependencies>
        <!--gateway的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--nacos注册与发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--nacos配置中心来做配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <dependency>
            <groupId>com.tfjybj</groupId>
            <artifactId>fewCode-common-redis</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>


    </dependencies>

3、配置文件!!!!!(超级重要!!!根据自己的需要进行配置)

server:
  port: 8088
  servlet:
    context-path: /api

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: login_route
          uri: lb://fewCode-provider-login
          #uri: http://localhost:8001/
          predicates:
            - Path=/login/**,/Curriculum/**,/classes/**
          filters:
            - RewritePath=/(?<segment>.*),/$\{segment}
leyou:
  filter:  #需要进行过滤的白名单
    allowPaths:
      - /login/checkLogin
      - /login/user
      - /login/userLogin
      - /upload/file
    tokenExpire: 1440

这份配置是用于Spring Cloud Gateway的配置文件,用于构建API网关。让我来解释一下每个部分的含义:

服务器配置:

服务器监听端口为8088。
Servlet的上下文路径设置为"/api",意味着所有API的端点都将具有这个前缀。
Spring Cloud Gateway配置:

服务发现定位器:启用,这意味着网关将使用服务发现来查找后端服务的路由。在动态的微服务环境中非常有用。
路由:路由配置指定了网关如何将传入的请求路由到后端服务。在这里,定义了一个路由:
id: login_route - 这是该路由的唯一标识符。
uri: lb://fewCode-provider-login - 后端服务的URI,用于处理具有负载均衡器(lb)的请求。服务名为"fewCode-provider-login",网关将使用服务发现来定位该服务。
predicates: 定义了应用此路由的条件。在这里,该路由将用于以"/login/“、”/Curriculum/“或”/classes/"开头的路径的请求。
filters: 对请求应用的过滤器,在将其转发到后端服务之前。在这里,使用了一个RewritePath过滤器,用于删除路径末尾的斜杠。
自定义配置:

leyou:这似乎是一些与"leyou"相关的自定义配置,用于特定的过滤逻辑。
filter:这是一个允许白名单路径的配置。
allowPaths:列出的路径将无需任何额外过滤而被允许。这些路径的请求不会受到任何额外的检查。
tokenExpire:设置令牌过期的时间限制(以分钟为单位),这里设置为1440分钟(24小时)。
总体而言,这份配置将Spring Cloud Gateway配置成将传入的请求路由到名为"fewCode-provider-login"的后端服务,前提是请求路径以"/login/“、”/Curriculum/“或”/classes/"开头。同时,它提供了一个白名单,允许无需任何额外过滤的路径。

4、相关类

package com.tfjybj.gateway.config;


import com.tfjybj.gateway.filter.AuthorizeFilter;
import com.tfjybj.gateway.filter.RenewFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class FilterConfig {

    @Bean
    public AuthorizeFilter authGatewayFilter() {
        //配置拦截器参数
        //order:序号,设置拦截器执行顺序,AuthGatewayFilter为1
        return new AuthorizeFilter(0);
    }

    @Bean
    public RenewFilter renewFilter(){
        // 续约Token
        return new RenewFilter(5);
    }



}

package com.tfjybj.gateway.config;

import com.tfjybj.gateway.util.FilterProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 请求白名单
 *

 */
@Component
public class IgnorePath {

    @Autowired
    private FilterProperties filterProperties;

    public boolean isAllowPath(String path) {
        //遍历白名单
        for (String allowPath : filterProperties.getAllowPaths()) {
            //判断是否允许
            if(path.startsWith(allowPath)){
                return true;
            }
        }
        return  false;
    }

}

package com.tfjybj.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class LapCorsConfiguration {
    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1、配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

package com.tfjybj.gateway.filter;

import com.alibaba.fastjson.JSON;

import com.tfjybj.gateway.config.IgnorePath;
import com.tfjybj.gateway.result.ResultMsgEnum;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;


public class AuthorizeFilter implements GlobalFilter, Ordered {

    private static final Logger log = LogManager.getLogger();

    @Autowired
    private StringRedisTemplate redisTemplate;


    @Autowired
    private IgnorePath ignorePath;

    private final int order;

    public AuthorizeFilter(int order) {
        this.order = order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        //获取请求的url路径
        String path = request.getURI().getPath();
        boolean flag=ignorePath.isAllowPath(path);
        if (flag) {
            log.info("请求在白名单中,metaverse.filter: {}",path);
            return chain.filter(exchange);
        } else {

            //获取token值
            String authorization = headers.getFirst("Authorization");
            log.info("Authorization值{}", authorization);
            authorization = authorization.split("Bearer ")[1];
            //判断redis中是否有token
            Boolean aBoolean = redisTemplate.hasKey("fewCode:userinfo:" + authorization);
            if (aBoolean){
                return chain.filter(exchange);
            }else {
                //声明变量
                ServerHttpResponse response = exchange.getResponse();
                HashMap map = new HashMap();
                String resp;
                DataBuffer bodyDataBuffer;

                //设置响应头
                response.setStatusCode(HttpStatus.FORBIDDEN);
                map.put("code", "403");
                map.put("message", ResultMsgEnum.AUTH_FAILED.getMsg());
                resp = JSON.toJSONString(map);
                bodyDataBuffer = response.bufferFactory().wrap(resp.getBytes());
                response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
                return response.writeWith(Mono.just(bodyDataBuffer));

            }


        }
    }


    @Override
    public int getOrder() {
        return this.order;
    }

}

package com.tfjybj.gateway.filter;

import com.tfjybj.gateway.config.IgnorePath;
import com.tfjybj.gateway.util.FilterProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.concurrent.TimeUnit;

/**
 * token续约的逻辑
 *

 */
public class RenewFilter implements GlobalFilter, Ordered {


    private static final Logger log = LogManager.getLogger();

    private final int order;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public RenewFilter(int order) {
        this.order = order;
    }

    @Autowired
    private IgnorePath ignorePath;

    @Autowired
    private FilterProperties filterProperties;

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();

        //获取请求的url路径
        String path = request.getURI().getPath();
        boolean flag=ignorePath.isAllowPath(path);
        if (flag) {
            log.info("请求在白名单中,metaverse.filter: {}", path);
            return chain.filter(exchange);
        }
        //token值
        String authorization = headers.getFirst("Authorization");
        authorization = authorization.split("Bearer ")[1];
        log.info("Authorization值{}", authorization);


        //TOKEN续活


        //解析TOKEN


        //根据uuid,延长用户信息
        String uuid = authorization;
        String key = "fewCode:userinfo:" + uuid;
        stringRedisTemplate.expire(key, filterProperties.getTokenExpire(), TimeUnit.MINUTES);


        return chain.filter(exchange);

    }



}

package com.tfjybj.gateway.result;

public enum ResultMsgEnum {

    FIND_SUCCESS("查询成功!"),
    FIND_FAIL("查询失败!"),

    UPDATE_SUCCESS("更新成功"),
    UPDATE_FAIL("更新失败"),

    DELETE_SUCCESS("删除成功"),
    DELETE_FAIL("删除失败"),

    SEND_SUCCESS("发送成功"),
    SEND_FAIL("发送失败"),

    EXECUTE_SUCCESS("执行成功!"),
    EXECUTE_FAIL("执行失败!"),

    AUTH_FAILED("权限认证失败"),
    AUTH_SUCCESS("权限认证成功");

    private final String msg;

    ResultMsgEnum(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}

package com.tfjybj.gateway.util;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.util.List;


@Component
@RefreshScope
@ConfigurationProperties(prefix = "leyou.filter")
public class FilterProperties {
    public void setAllowPaths(List<String> allowPaths) {
        this.allowPaths = allowPaths;
    }

    public List<String> getAllowPaths() {
        return allowPaths;
    }

    private List<String> allowPaths;

    /**
     * token过期时间
     */
    private Integer tokenExpire;

    public Integer getTokenExpire() {
        return tokenExpire;
    }

    public void setTokenExpire(Integer tokenExpire) {
        this.tokenExpire = tokenExpire;
    }
}

package com.tfjybj.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
//@EnableDiscoveryClient

public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }

}

我们在服务中进行的白名单中接口的操作如下

(主要是根据传过来的信息生成token,然后以键值对的形式存入redis中,方便后续通过redis根据token拿人的信息):

RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private Actor actorInfo;

    @ApiOperation("学生登录验证")
    @RequestMapping(value="checkLogin",method= RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public Actor checkLoginInfo(@RequestBody Actor actor){
        return actorInfo.notifyStudentCheckLoginInfo(actor);
    }
}
public Actor notifyStudentCheckLoginInfo(Actor student){
        Actor actor;
        for (Actor studentInfo:allStudent){
            actor=studentInfo.checkLoginInfo(student);
            if (!ObjectUtils.isEmpty(actor)){
                //生成UUID
                String uuid = CreateUUID.createUUID();
                //存入redis
                saveRedis(uuid, actor);
                //生成token,封装到请求头
                putHeader(uuid);
                return actor;
            }
        }
        return null;
    }
public class CreateUUID {
    public CreateUUID() {
    }

    public static String createUUID() {
        String preUuid = UUID.randomUUID().toString();
        String newUUID = preUuid.replace("-", "");
        return newUUID;
    }
}
   private void saveRedis(String uuid, Object userInfo) {
        //拼接key,user信息序列化,存入redis,过期时间在nacos中设置
        String key = "fewCode:userinfo:" + uuid;
        String userJson = JSONObject.toJSONString(userInfo);
        redisTemplate.opsForValue().set(key, userJson);
        redisTemplate.expire(key, 1440, TimeUnit.MINUTES);
    }
   private void putHeader(String token) {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = sra.getResponse();
        response.setHeader("Authorization", token);
    }

测试

存:

在这里插入图片描述

在这里插入图片描述
将断点打到这里,可以观察到我们要请求的服务IP+端口号还有url地址,如下
相当手动访问

在这里插入图片描述

调通后存入redis中如下

在这里插入图片描述

拿:

然后拿着根据token获取信息

在这里插入图片描述

在这里插入图片描述

package com.tfjybj.login.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Service
public class AnalysisTokenService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 获取当前登陆人id
     *
     * @return
     */
    public String getUserId() {
        JSONObject userData = getUserData();
        return userData.get("id").toString();
    }

    /**
     * 获取用户code
     *
     * @return
     */
    public String getUserAccount() {
        JSONObject userData = getUserData();
        return userData.get("account").toString();
    }

    /**
     * 获取当前登陆人name
     *
     * @return
     */
    public String getUserName() {
        JSONObject userData = getUserData();
        return userData.get("name").toString();

    }

    public JSONObject getUserData() {
        //从请求头获取token
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String token = sra.getRequest().getHeader("Authorization");
        token = token.split("Bearer ")[1];
        //解析token
        JSONObject userJson = this.analysisToken(token);
        return userJson;
    }


    public JSONObject analysisToken  (String token){

        //解析token
        String key= "fewCode:userinfo:"+token;
        String userInfoStr = redisTemplate.opsForValue().get(key);
        JSONObject userJson = JSONObject.parseObject(userInfoStr);
//        String data = jsonObject.get("data").toString();
//        JSONObject userJson = JSONObject.parseObject(data);
        return userJson;

    }

}

总结

1、搞懂gateway是干嘛的
2、知道配置文件中各个参数是什么

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/48874.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于 FFmpeg 的跨平台视频播放器简明教程(七):使用多线程解码视频和音频

系列文章目录 基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;一&#xff09;&#xff1a;FFMPEG Conan 环境集成基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;二&#xff09;&#xff1a;基础知识和解封装&#xff08;demux&#xff09;基于 FFmpeg 的跨平台视频…

生产环境Session解决方案、Session服务器之Redis

目录 一、服务器配置 二、安装nginx 三、安装配置Tomcat&#xff1a; 四、配置session Session服务器之Redis Redis与Memcached的区别 安装部署redis 一、服务器配置 IP地址 主机名 软件包列表 192.168.100.131 huyang1 nginx 192.168.100.133 huyang3 JDK Tomca…

[STL]list使用介绍

[STL]list使用 注&#xff1a;本文测试环境是visual studio2019。 文章目录 [STL]list使用1. list介绍2. 构造函数3. 迭代器相关函数begin函数和end函数rbegin函数和rend函数 4. 容量相关函数empty函数size函数 5. 数据修改函数push_back函数和pop_back函数push_front函数和pop…

位图和布隆过滤器+哈希切分思想

文章目录 一.位图(bitset)底层实现: 二.布隆过滤器(bloomFilter)底层实现: 三.哈希切分思想 一.位图(bitset) 位图是一种以一个比特位为数据记录单元的哈希表 ,以无符号整数为key值,采用直接定址法(不存在哈希冲突的问题),其哈希映射函数为 f ( k e y ) k e y ( k e y 的存在…

快速排序qsort讲解

hello大家好&#xff0c;我是c语言boom家宝&#xff0c;今天为大家分享的博客内容是qsort快速排序&#xff0c;简称快排的一个知识点的讲解。 在讲到快排之前&#xff0c;允许博主先提一嘴冒泡排序。大家在c语言的学习过程中&#xff0c;冒泡排序是必不可少会学习到的一个思想&…

Hudi数据湖技术引领大数据新风口(三)解决spark模块依赖冲突

文章目录 解决spark模块依赖冲突2.2.6 执行编译命令2.2.7 编译成功 下一章 核心概念后记 解决spark模块依赖冲突 修改了Hive版本为3.1.2&#xff0c;其携带的jetty是0.9.3&#xff0c;hudi本身用的0.9.4&#xff0c;存在依赖冲突。 1&#xff09;修改hudi-spark-bundle的pom文…

kafka集群

目录 broker ZooKeeper consumer group&#xff08;消费者组&#xff09; 分区&#xff08;Partitions&#xff09; 副本&#xff08;Replicas&#xff09; 主题&#xff08;Topic&#xff09; 偏移量&#xff08;offset&#xff09; broker 一个kafka进程就是一个broker…

备战秋招 | 笔试强训17

目录 一、选择题 二、编程题 三、选择题题解 四、编程题题解 一、选择题 1、假设A为抽象类&#xff0c;下列声明&#xff08;&#xff09;是正确的 A. int fun(A); B. A Obj; C. A fun(int); D. A *p; 2、虚函数可不可以重载为内联&#xff1f; A. 可以 B. 不可以 C. 语法…

uni-app踩坑记

打包h5如何配置域名&#xff1a; 在manifest.json中配置域名 配置完成后无论是测试环境还是正式环境都带上/mobile/&#xff0c;否则会报错404 如何引入调试工具erada: 在默认的index.html中直接引入erada&#xff0c;页面样式会整个错乱&#xff0c;解决方案就是引入官方…

低代码开发平台源码

什么是低代码开发平台&#xff1f; 低代码来源于英文“Low Code&#xff0c;它意指一种快速开发的方式&#xff0c;使用最少的代码、以最快的速度来交付应用程序。通俗的来说&#xff0c;就是所需代码数量低&#xff0c;开发人员门槛低&#xff0c;操作难度低。一般采用简单的图…

学习笔记|大模型优质Prompt开发与应用课(二)|第二节:超高产文本生成机,传媒营销人必备神器

文章目录 01 文字写作技能的革新&#xff0c;各行各业新机遇四大类常见文字工作新闻记者的一天新闻记者的一天–写策划prompt 新闻记者的一天–排采访prompt生成结果prompt生成结果 大模型加持&#xff0c;文字写作我们如何提效营销创作营销创作-使用预置法为不同平台生成文案p…

产品开发八大模块交流︱奇瑞新能源汽车产品开发院院长荣升格

奇瑞新能源汽车股份有限公司研发中心/产品开发院院长荣升格先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;产品开发八大模块交流。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; VUCA时代…

从源码分析Handler面试问题

Handler 老生常谈的问题了&#xff0c;非常建议看一下Handler 的源码。刚入行的时候&#xff0c;大佬们就说 阅读源码 是进步很快的方式。 Handler的基本原理 Handler 的 重要组成部分 Message 消息MessageQueue 消息队列Lopper 负责处理MessageQueue中的消息 消息是如何添加…

用WhatsApp开拓和跟进客户,需要注意这些雷点

我们很多新手小白在利用WhatsApp开拓和维护客户的时候&#xff0c;总是容易犯一些错误&#xff0c;踩到雷点&#xff0c;这不利于客户对企业的印象&#xff0c;不利于增长&#xff0c;下面我们来说一些需要注意的点&#xff1a; 1、专业正确的用语 不管外贸人是跟进哪个国家…

29.Git版本控制工具

1.Git简介 Git是一开源的分布式版本控制系统&#xff0c;提供了存储代码、管理版本历史、分支和合并等功能。 版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理&#xff0c;是软件配置管理的核心思想之一。它的主要目的是跟踪和记录软件开发过程…

双重for循环优化

项目中有段代码逻辑是个双重for循环&#xff0c;发现数据量大的时候&#xff0c;直接导致数据接口响应超时&#xff0c;这里记录下不断优化的过程&#xff0c;算是抛砖引玉吧~ Talk is cheap,show me your code&#xff01; 双重for循环优化 1、数据准备2、原始双重for循环3、…

如何利用Requestly提升前端开发与测试的效率

痛点 B站最牛的Python接口自动化测试进阶教程合集&#xff08;真实企业项目实战&#xff09; 前端测试 在进行前端页面开发或者测试的时候&#xff0c;我们会遇到这一类场景&#xff1a; 在开发阶段&#xff0c;前端想通过调用真实的接口返回响应在开发或者生产阶段需要验证前…

Vue 中通用的 css 列表入场动画效果

css 代码 .gradientAnimation {animation-name: gradient;animation-duration: 0.85s;animation-fill-mode: forwards;opacity: 0; }/* 不带前缀的放到最后 */ keyframes gradient {0% {opacity: 0;transform: translate(-100px, 0px);}100% {opacity: 1;transform: translate…

算法38:反转链表

一、需求 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1] 示例3&#xff…

MSFCS互相联动

MSF&CS互相联动 1. 前言2. CS联动MSF2.1. 案例测试2.1.1. CS设置联动监听器2.1.2. CS设置联动MSF会话2.1.3. MSF设置监听 3. MSF联动CS3.1. 案例测试3.1.1. MSF生成木马3.1.2. 设置监听3.1.3. CS设置监听3.1.4. MSF转移会话3.1.5. 查看上线 4. 其它 1. 前言 在日常渗透测试…