如何编写一个Fiffler扩展
需求
起因1
因为开发需要,需要使用test环境的网页直连我的开发电脑进行调试.
解决方案1
直接使用Fiddler的AutoResponder功能实现.
起因2
调试过程中发现,将请求转发到我的开发电脑后,服务无法获取用户信息.
经追踪发现,前端所有请求是通过网关传递到后方的微服务中的,包含用户信息的Cookie经过网关转换为另一个请求头.此请求头中的信息才是微服务识别的用户信息.
同时,这也导致了使用AutoResponder无法正确传递用户信息,本地服务无法获取用户信息.
解决方案2
经过调研,决定使用Fiddler的Extend(扩展),完成如下功能
- 在指定请求前,插入额外请求获取用户信息,并将其以指定格式配装到原有请求中
- 指定请求通过正则对url进行匹配
- 可以指定上述的额外请求的url
- 可以动态开关此功能
- 请求转发功能还是由AutoResponder完成
- 有日志功能,可以捕获错误
前置准备
安装最新版Fiddler
- (略)
安装Visual Studio 2019
- 下载Visual Studio Installer
- 启动Visual Studio Installer
- 在工作负载中选选择 .NET桌面开发选项
- 调整安装文件夹,进行安装
安装 .NET Framework 4.7.2
- (略)
创建项目
-
打开Visual Studio 2019
-
创建新项目
-
使用模板类库(.NET Framework) 作为项目模板
注意,这里可能会有多种类库类型.如类库(.NET Core) 和 类库(.NET Standard).注意选择正确的模板
-
下一步
-
输入项目名,选择项目位置
-
选择将解决方案与项目放在同一目录中(非必要)
-
选择框架 -> .NET Framework 4.7.2
-
添加Fiddler引用
- 右键点击项目引用
- 选择添加引用
- 选择左侧菜单中的浏览
- 选择右下侧的浏览按钮
- 选择
<fiddler_home>Fiddler.exe
- 确定
编写代码
打开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("
");
logBox.AppendText(text);
}
}
编译
-
生成 -> 生成解决方案
-
可以看到如下日志
已启动生成… 1>------ 已启动生成: 项目: <project_name>, 配置: Debug Any CPU ------ 1> <project_name> -> <project_path><project_name>inDebug<project_name>.dll ========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
加载扩展
- 关闭Fiddler
- 复制上述日志中的 <project_name>.dll 到 C:Userslixiaoxi_vDocumentsFiddler2Scripts 中
- 打开Fiddler
- 可以在Fiddler中看到声明的ssoUserTools标签,这个就是需要功能了~
PS
- 本人是Java开发,上述所有功能都是我一边翻Fiddler文档,一边翻C#API文档拼凑出来的.所以内容可能会有疏漏与错误,如果有的话,还请指正.
- 代码部分为了保密,我直接在markdown编辑器中对一些代码与注释进行了修改,参考时请注意.
参考
Fiddler Extend
HttpClient
C#的正则表达式用法