如何编写一个Fiddler扩展

如何编写一个Fiffler扩展

需求

起因1

因为开发需要,需要使用test环境的网页直连我的开发电脑进行调试.

解决方案1

直接使用FiddlerAutoResponder功能实现.

起因2

调试过程中发现,将请求转发到我的开发电脑后,服务无法获取用户信息.
经追踪发现,前端所有请求是通过网关传递到后方的微服务中的,包含用户信息的Cookie经过网关转换为另一个请求头.此请求头中的信息才是微服务识别的用户信息.
同时,这也导致了使用AutoResponder无法正确传递用户信息,本地服务无法获取用户信息.

解决方案2

经过调研,决定使用FiddlerExtend(扩展),完成如下功能

  • 在指定请求前,插入额外请求获取用户信息,并将其以指定格式配装到原有请求中
  • 指定请求通过正则对url进行匹配
  • 可以指定上述的额外请求的url
  • 可以动态开关此功能
  • 请求转发功能还是由AutoResponder完成
  • 有日志功能,可以捕获错误

前置准备

安装最新版Fiddler

  • (略)

安装Visual Studio 2019

  • 下载Visual Studio Installer
  • 启动Visual Studio Installer
  • 在工作负载中选选择 .NET桌面开发选项
  • 调整安装文件夹,进行安装

安装 .NET Framework 4.7.2

  • (略)

创建项目

  1. 打开Visual Studio 2019

  2. 创建新项目

  3. 使用模板类库(.NET Framework) 作为项目模板

    注意,这里可能会有多种类库类型.如类库(.NET Core)类库(.NET Standard).注意选择正确的模板

  4. 下一步

  5. 输入项目名,选择项目位置

  6. 选择将解决方案与项目放在同一目录中(非必要)

  7. 选择框架 -> .NET Framework 4.7.2

  8. 添加Fiddler引用

    1. 右键点击项目引用
    2. 选择添加引用
    3. 选择左侧菜单中的浏览
    4. 选择右下侧的浏览按钮
    5. 选择<fiddler_home>\Fiddler.exe
    6. 确定

编写代码

打开Class1.cs输入如下代码

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using Fiddler;

[assembly: Fiddler.RequiredVersion("2.3.5.0")]

/**
 * 进行用户信息转换
 */
public class SsoAutoTamper : IAutoTamper
{
    /**
     * sso服务默认url
     */
    private String default_sso_url = "<url>";
    /**
     * url正则输入框
     */
    private TextBox regularBox;
    /**
     * sso服务url输入框
     */
    private TextBox getSsoUrlBox;
    /**
     * 启动复选框
     */
    private CheckBox setupBox;
    /**
     * 日志输出框
     */
    private TextBox logBox;
    /**
     * 标题宽度
     */
    private int labelWidth = 110;
    /**
     * 标题高度
     */
    private int labelHeight = 25;
    /**
     * 输入框宽度
     */
    private int inputBoxWidth = 120;
    /**
     * 输入框高度
     */
    private int inputBoxHeight = 25;
    /**
     * OnLoad方法执行完成标志
     */
    private bool hasOnLoad;

    public SsoAutoTamper()
    {
    }

    /**
     * 加载回调,可以用于UI声明
     */
    public void OnLoad() 
    {
        // 构造table页
        TabPage tabPage = new TabPage("ssoUserTools");
        FiddlerApplication.UI.tabsViews.TabPages.Add(tabPage);

        // 启用按钮
        tabPage.Controls.Add(new Label
        {
            Text = "是否启用: ",
            Width = labelWidth,
            Height = labelHeight,
            TextAlign = ContentAlignment.MiddleLeft
        });
        tabPage.Controls.Add(setupBox = new CheckBox
        {
            Location = new Point(labelWidth, 0),
        });
        // 注册事件
        setupBox.CheckedChanged += CheckBoxChanged;

        // 正则输入框label
        tabPage.Controls.Add(new Label
        {
            Text = "目标url正则表达式: ",
            Width = labelWidth,
            Height = labelHeight,
            TextAlign = ContentAlignment.MiddleLeft,
            Location = new Point(0, labelHeight)
        });
        // 正则输入框
        tabPage.Controls.Add(regularBox = new TextBox
        {
            Width = inputBoxWidth,
            Height = inputBoxHeight,
            Location = new Point(labelWidth, labelHeight),
            Text = "<default pattern>"
        });
        // 获取用户信息url输入框label
        tabPage.Controls.Add(new Label
        {
            Text = "获取用户信息url: ",
            Width = labelWidth,
            Height = labelHeight,
            TextAlign = ContentAlignment.MiddleLeft,
            Location = new Point(0, labelHeight * 2)
        });
        tabPage.Controls.Add(getSsoUrlBox = new TextBox
        {
            Width = inputBoxWidth,
            Height = inputBoxHeight,
            Location = new Point(labelWidth, labelHeight*2),
            Text = default_sso_url
        });
        // 日志
        tabPage.Controls.Add(logBox = new TextBox
        {
            Location = new Point(0, labelHeight * 3),
            Width = 300,
            Height = 200,
            Multiline = true
        });

        hasOnLoad = true;
    }
    public void OnBeforeUnload() { }

    public void AutoTamperRequestAfter(Session oSession) { }
    public void AutoTamperResponseBefore(Session oSession) { }
    public void AutoTamperResponseAfter(Session oSession) { }
    public void OnBeforeReturningError(Session oSession) { }

    public void AutoTamperRequestBefore(Session oSession)
    {
        try
        {
            // OnLoad未完成,直接返回
            if(!hasOnLoad)
            {
                return;
            }
            // 未启用直接返回
            if(!setupBox.Checked)
            {
                return;
            }
            // 正则不匹配直接返回
            if (!Regex.IsMatch(oSession.fullUrl, regularBox.Text))
            {
                return;
            }

            Log("匹配路径->" + oSession.fullUrl);

            // 构造请求客户端
            HttpClientHandler httpClientHandler = new HttpClientHandler();
            httpClientHandler.UseCookies = false;
            HttpClient httpClient = new HttpClient(httpClientHandler);
            HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, getSsoUrlBox.Text);
            // 复制所有头信息
            IEnumerator<HTTPHeaderItem> ie = oSession.oRequest.headers.GetEnumerator();
            while (ie.MoveNext())
            {
                HTTPHeaderItem hi = ie.Current;
                // 跳过Content相关头
                if(hi.Name.StartsWith("Content-"))
                {
                    continue;
                }
                httpRequestMessage.Headers.Add(hi.Name, hi.Value);
            }
            // 进行请求
            Task<HttpResponseMessage> response = httpClient.SendAsync(httpRequestMessage);
            HttpContent httpContent = response.Result.Content;
            Task<string> result = httpContent.ReadAsStringAsync();
            // 向原有请求头中添加用户信息
            oSession.oRequest.headers.Add("actual-user", result.Result);
        }
        catch(Exception e)
        {
            //logBox.AppendText(e.Message);
            logBox.AppendText(e.StackTrace);
            logBox.AppendText(e.Source);
        }
        return;
    }

    /**
     * 启动复选框回调
     */
    void CheckBoxChanged (Object sender, EventArgs eventArgs)
    {
        if (setupBox.Checked)
        {
            Log("开启sso转换功能");
        }
        else
        {
            Log("关闭sso转换功能");
        }
    }

    /**
     *日志方法
     */
    void Log(String text)
    {
        logBox.AppendText("\r\n");
        logBox.AppendText(text);
    }
}

编译

  1. 生成 -> 生成解决方案

  2. 可以看到如下日志

    已启动生成…
    1>------ 已启动生成: 项目: <project_name>, 配置: Debug Any CPU ------
    1>  <project_name> -> <project_path>\<project_name>\bin\Debug\<project_name>.dll
    ========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
    

加载扩展

  1. 关闭Fiddler
  2. 复制上述日志中的 <project_name>.dllC:\Users\lixiaoxi_v\Documents\Fiddler2\Scripts\
  3. 打开Fiddler
  4. 可以在Fiddler中看到声明的ssoUserTools标签,这个就是需要功能了~

PS

  • 本人是Java开发,上述所有功能都是我一边翻Fiddler文档,一边翻C#API文档拼凑出来的.所以内容可能会有疏漏与错误,如果有的话,还请指正.
  • 代码部分为了保密,我直接在markdown编辑器中对一些代码与注释进行了修改,参考时请注意.

参考

Fiddler Extend
HttpClient
C#的正则表达式用法

狗头

posted @ 2021-03-01 11:32  li_xiaoxi  阅读(254)  评论(0编辑  收藏  举报