方法/工具 | 发布时间 | 所属框架 | 命名空间/依赖项 | 编码标准 | 空格处理 | 严格性 | 适用场景 | 现代项目支持(.NET 6+) |
---|---|---|---|---|---|---|---|---|
HttpUtility.UrlEncode | 2002 | .NET Framework 1.0+ | System.Web(需引用 DLL) | x-www-form-urlencoded | + | 宽松 | 传统 ASP.NET WebForms | ❌ |
Server.UrlEncode | 2002 | .NET Framework 1.0+ | System.Web(ASP.NET 页面内) | x-www-form-urlencoded | + | 宽松 | ASP.NET WebForms 页面内编码 | ❌ |
Uri.EscapeDataString | 2005 | .NET Framework 2.0+ | System(核心库) | RFC 3986 | %20 | 严格 | 构造 URI 组件(路径、查询参数) | ✔️ |
WebUtility.UrlEncode | 2012 | .NET Framework 4.5+ | System.Net(跨平台) | x-www-form-urlencoded | + | 宽松 | 非 Web 环境或兼容旧代码 | ✔️ |
UrlEncoder.Default.Encode | 2016 | .NET Core 1.0+ | System.Text.Encodings.Web | RFC 3986 | %20 | 严格 | 现代应用,严格 URI 编码 | ✔️ |
关键选择原则
兼容旧代码 → HttpUtility.UrlEncode 或 WebUtility.UrlEncode。
严格 URI 规范 → Uri.EscapeDataString 或 UrlEncoder。
ASP.NET Core → UrlEncoder。
非 Web 或跨平台 → 弃用 System.Web,选择 System.Net 或 System.Text.Encodings.Web 中的方法。

打开“任务计划程序”(taskschd.msc)
点击右侧“创建任务”
填写“名称”
“安全选项”根据实际情况设置
如果选择“不管用户是否登录都要运行”,则启动成功后不会显示窗口(任务管理器中可见进程)
如果选择“只在用户登录时运行”启动成功后会显示窗口,但系统重启后需要进入系统才能运行此计划
“触发器”新建,勾选“重复任务间隔”选最短,“持续时间”无限期,并取消“任务的执行时间超过此值则停止执行”
“操作”新建,启动程序,浏览程序或脚本
“设置”请勿启动新实例(只判断它启动的实例,不判断手动打开的或开机启动的实例),其它选项按需设置
设置完成

打开 .csproj 项目文件,在 <PropertyGroup> 标签内添加:
<Version>
1.0.$([System.Math]::Floor($([System.DateTime]::Now.Subtract($([System.DateTime]::Parse('2000-01-01T00:00:00Z'))).TotalDays))).$([MSBuild]::Divide($([System.Math]::Floor($([System.DateTime]::Now.TimeOfDay.TotalSeconds))), 2))
</Version>
最终生成的版本号示例: 1.0.9238.28518
其中,Major 与 Minor 是固定的,Build 是2000年1月1日至今的天数,Revision 是今天的秒数 / 2 所得的值。(为了防止数值超过 65535)
程序中获取版本号:
var version = Assembly.GetExecutingAssembly().GetName().Version!; // 当前类库
var version = Assembly.GetEntryAssembly()?.GetName().Version!; // 入口项目
从版本号获取发布时间:
DateTime versionTime = new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2);

在解决方案资源管理器中找到 Properties/AssemblyInfo.cs 文件。该文件存放程序集版本信息。
修改版本号格式
将以下代码片段中的 AssemblyVersion 改为使用星号通配符(建议保留主版本和次版本号):
[assembly: AssemblyVersion("1.0.*")] // 自动生成构建号和修订号 // [assembly: AssemblyFileVersion("1.0.0.0")] // 注释或删除此行
关闭确定性构建
用文本编辑器打开 .csproj 项目文件,在 <PropertyGroup> 标签内添加:
<Deterministic>false</Deterministic>
此设置允许 MSBuild 生成动态版本号。
最终生成的版本号示例: 1.0.9238.28518
其中,Major 与 Minor 是固定的,Build 是2000年1月1日至今的天数,Revision 是今天的秒数 / 2 所得的值。(为了防止数值超过 65535)
程序中获取版本号:
var version = Assembly.GetExecutingAssembly().GetName().Version;
从版本号获取发布时间:
DateTime versionTime = new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2);
查看 .NET Core / .NET 5+ 实现自动版本号的方法

本文介绍 Token 认证和 HMAC 认证两种方式。
一、Token 接口认证方式
原理:
客户端使用账号密码等信息登录,服务器验证通过后生成一个 Token 发送给客户端。客户端在后续的请求中携带这个 Token,服务器通过验证 Token 来确认用户的身份和权限。
应用场景:移动应用、Web 应用(特别是 SPA)
优点:
无状态性,即服务器不需要存储用户的会话信息。
易于实现跨域认证。
缺点:
Token 可能被窃取,应使用 HTTPS、不暴露在 URL 中、使用 HttpOnly 的 Cookie、对 Session ID 进行验证、设置合理过期时间、对 Token 进行加密等措施加强防范。
二、HMAC 接口认证方式
原理:
客户端将消息M与密钥K连接起来,通过哈希函数计算得到 HMAC 值,发送给服务器。服务器收到请求后,使用相同的密钥和请求参数重新计算 HMAC 值,如果与客户端发送的签名一致,即是合法请求。
优点:
安全性较高,攻击者很难伪造 HMAC 值,截获并篡改数据也无法通过服务端验证。
计算效率较高,哈希函数(如 MD5、SHA-1、SHA-256 等)计算效率比较高。
缺点:
密钥的更新和管理比较麻烦。
扩展:
在消息体中添加时间戳以防止重放攻击。
加密隐私数据:可以使用对称加密算法(如 AES)或非对称加密算法(如 RSA、ECC 等)对部分隐私数据进行加密。非对称算法虽然更安全,但速度较慢,如需加密大量数据,可以考虑使用对称加密算法进行加密,然后使用非对称加密算法对对称密钥进行加密。

前几天实现了在 nginx 中使用 lua 实现远程鉴权,今天想试试在 IIS 中能不能实现相同的功能。查询资料发现需要使用 URL 重写和 HTTP 请求模块,没有深究。干脆使用 ASP.NET 中间件来实现吧。
在 StratUp.cs 的 Configure 方法中,或 Program.cs 文件中添加以下代码:
// 远程鉴权
app.Use(async (context, next) =>
{
var ip = context.Connection.RemoteIpAddress!.ToString();
var ua = context.Request.Headers.UserAgent.ToString();
var host = context.Request.Host.Host;
var uri = new Uri(context.Request.GetDisplayUrl()).PathAndQuery;
var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(1); // 设置超时时间
try
{
var requestUrl = "https://鉴权地址/";
var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUrl);
requestMessage.Headers.Add("X-Real-IP", ip);
requestMessage.Headers.Add("User-Agent", ua);
requestMessage.Headers.Add("X-Forwarded-Host", host);
requestMessage.Headers.Add("X-Forwarded-Uri", uri);
// 发送请求
var response = await client.SendAsync(requestMessage);
// 检查响应状态码
if (response.StatusCode == HttpStatusCode.Forbidden)
{
// 如果返回403,则拒绝访问
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
await context.Response.WriteAsync("Access Denied");
}
else
{
// 如果返回其他状态码,则继续执行管道中的下一个中间件
await next();
}
}
catch (TaskCanceledException ex) when (ex.CancellationToken.IsCancellationRequested)
{
// 如果请求超时(任务被取消),则继续执行管道中的下一个中间件
await next();
}
catch
{
// 如果遇到错误,则继续执行管道中的下一个中间件
await next();
}
});
代码很简单,使用 HttpClient 发送请求,若返回 403 则拒绝访问,其它情况继续执行业务逻辑,超时或报错的情况按需修改即可。
若鉴权接口在私网中,建议将鉴权接口域名和私网 IP 添加到 hosts 文件中。

http {
...
lua_shared_dict cpu_cache 1m; # 定义共享字典用于缓存 CPU 使用率
access_by_lua_block {
local cpu_cache = ngx.shared.cpu_cache
local cache_time = cpu_cache:get("cache_time") or 0
local current_time = ngx.now()
if current_time - cache_time > 5 then -- 每 5 秒更新一次
local cpu_info = io.popen("top -bn1 | grep 'Cpu(s)'"):read("*all")
local cpu_usage = cpu_info:match("(%d+%.%d+) id") -- 获取空闲 CPU 百分比
local cpu_used = 100 - tonumber(cpu_usage) -- 计算使用率
cpu_cache:set("cpu_usage", cpu_used)
cpu_cache:set("cache_time", current_time)
end
local cpu_usage = cpu_cache:get("cpu_usage")
ngx.log(ngx.INFO, "CPU 使用率: " .. cpu_usage .. "%")
-- 将 cpu_usage 发送到远程鉴权接口,可根据服务器压力来决定是否拒绝一些不重要的请求
}
}
代码解析:
共享字典:使用 lua_shared_dict 定义一个共享字典 cpu_cache,用于存储 CPU 使用率和缓存时间。
获取 CPU 使用率:在 access_by_lua_block 中,检查缓存时间,如果超过 5 秒,则重新获取 CPU 使用率,并更新共享字典。
记录日志:使用 ngx.log 将 CPU 使用率记录到 Nginx 日志中。
注意事项:
确保 Nginx 配置中已经加载了 Lua 模块(如 ngx_http_lua_module)。
根据实际需求调整缓存时间,以平衡性能和数据的实时性。
尝试过使用 ifconfig 或 ip 命令获取网卡流量,在宝塔面板中失败了,怀疑是权限问题,有空再研究。临时方案是鉴权接口定时调用宝塔面板 API 或阿里云控制台 API 来获取 ECS 的 CPU 和带宽使用率。

今天早上同事反映论坛某管理账号无法登录,于是我尝试用创始人账号登录,也不行,第一反应就是中招了。
于是进阿里云控制台,发现云安全中心有许多安全警告,类型是网站后门,幸好 nginx 中设置了仅部分文件可执行 PHP,这些后门文件无法被执行。
尝试在 config_global_default.php 文件中添加创始人,但账号必须是副站长等管理账号才能成为创始人。
于是借用一个小号,从表 pre_ucenter_members 中将这个小号的 password 和 salt 复制到创始人账号中,这样创始人账号就可以用这个小号的密码登录了。
进入论坛后台,在 工具-运行记录-系统记录-后台访问 中查看入侵时间段的记录(操作、时间、IP 等),可搜索。
发现基本上在操作模板管理和专题管理。
对比时间,发现进入后台操作在先,上传后门在后。
查询 web 访问日志,通过访问文件路径或 IP 查询,在进入论坛后台之间,他进入了 UCenter 的后台,但是再往前就没有记录了。
因此基本可以确定:
黑客从 UCenter 修改了某管理员账号的密码(可能是利用漏洞),然后登录论坛后台修改了创始人的密码(可能也是用 UCenter 改的),通过模板管理和专题管理功能的上传功能上传了后门文件。
索性他没有对数据进行破坏性处理,也没有挂马,只是发现后门文件无法执行就放弃了。

一年前记录过浏览器上如何下载小鹅通的课程视频,现在发现那个插件已经不能用了,所以重新整理了一下,下载过程比上次简单多了。
插件还是那只可爱的小黄猫
GitHub 地址:https://github.com/xifangczy/cat-catch
浏览器扩展安装地址:Chrome / Edge / Firefox
与之前相比,功能改进了不少:
“从头捕获”,不需要手动拖动进度条,生怕没有录制完全。但是实际使用还是会丢失一部分开头,希望下个版本改进;
“使用 ffmpeg 合并”可以自动合并捕获的视频和音频,下载得到的是一个完整的视频文件;
“自动跳转到缓冲尾”可以不间断加载、节省录制时间,对下载非直播画面非常有用;
可以设置播放速度,点击扩展图标,在“其他页面 / 媒体控制”中可以设置。但是有了“自动跳转到缓冲尾”,我觉得设置播放速度这个功能可有可无。
开始录制:
在浏览器上安装扩展后,打开需要录制视频的页面,点击缓存捕获:
然后在面板上点击“从头捕获”:
插件就自动开始从头播放并记录缓存了。
建议勾选“完成捕获自动下载”、“使用 ffmpeg 合并”。
如果是录制“非直播”,建议勾选“自动跳转到缓冲尾”。
另外两个选项“始终从头捕获”、“清理多余头部数据”我没有深入研究,有兴趣的自己研究下。
播放完成后,会自动打开一个“猫抓 cat-catch”的网页,自动使用 ffmpeg 进行转码与合并。
完成后自动下载该文件。
提示:如果要继续捕获其它视频,请先关闭那个“猫抓 cat-catch”网页,否则捕获完成后无法下载。

解析域名(非网站域名)、修改实例名称、主机名
设置阿里云(重要)
远程连接进入 ECS(若解析未生效可以先用 IP)(若新服默认使用 22 端口,可在阿里云控制台登录系统,或先在安全组临时放行 22 端口)
修复系统漏洞
将磁盘挂载到目录(fdisk、df 命令参考:https://xoyozo.net/Blog/Details/SSH)
安装宝塔面板(本文以宝塔面板方案为例,选择任何你喜欢的环境部署方案都行)。可以在阿里云控制台ECS实例页安装扩展程序
临时放行宝塔面板端口,进入宝塔面板(http方式),或用命令更改宝塔面板端口
配置面板 SSH、添加新的安全端口、面板设置
更改 SSH 默认端口(参:https://xoyozo.net/Blog/Details/change-default-port)
安装 nginx、PHP 等
配置 PHP 扩展(Redis、sqlsrv(注意选择兼容的版本)、memcached 及端口)
创建网站,配置网站(路径、伪静态等)
迁移网站文件(参:https://xoyozo.net/Blog/Details/SSH)
仔细对比新旧网站的配置文件(特别是 .php 的访问权限,参:https://xoyozo.net/Blog/Details/nginx-location-if)
设置写入目录(使用 rsync 同步的文件会同步用户和权限)
解析域名(先改 hosts 测试网站功能)
更改内网其它 ECS 上的 hosts
关闭原 ECS(能马上发现问题,不然等运行一段时间才发现问题就麻烦点)
设置 FTP
迁移“计划任务”
所有网站和软件的配置文件都要使用 WinMerge 进行对比
移除“宝塔面板-安全”和“阿里云-ECS-安全组”中不用的端口
再次检查阿里云设置
私网中若有 ECS 的 hosts 中域名直接绑定到私网 IP 的,做相应更改
其它:ERP添加到期提醒、WAF增加ECS、备份工具增加ECS
更多文章:
