You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ASP.NET Core 6 Web API中启用Digest身份认证?

.NET 服务端 Digest 认证实现示例

针对你的虚拟设备工具API需求,以下是实用的.NET Framework(适配你的.NET 4.5客户端)服务端Digest认证实现,基于ASP.NET Web API 2:

自定义Digest认证Handler

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

public class DigestAuthenticationHandler : DelegatingHandler
{
    // 模拟用户存储,实际可替换为数据库/安全配置
    private readonly string _validUsername = "testuser";
    private readonly string _validPassword = "testpass";
    private readonly string _realm = "VirtualDeviceAPI";
    private string _nonce;

    public DigestAuthenticationHandler()
    {
        // 生成初始nonce,可定期刷新提升安全性
        _nonce = GenerateNonce();
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        AuthenticationHeaderValue authHeader = request.Headers.Authorization;

        // 检查是否提供Digest认证头
        if (authHeader == null || !authHeader.Scheme.Equals("Digest", StringComparison.OrdinalIgnoreCase))
        {
            return CreateUnauthorizedResponse();
        }

        // 解析认证参数并验证
        var authParams = ParseDigestParameters(authHeader.Parameter);
        if (!ValidateDigestCredentials(authParams, request.Method.Method))
        {
            return CreateUnauthorizedResponse();
        }

        // 认证通过,继续处理请求
        return await base.SendAsync(request, cancellationToken);
    }

    private HttpResponseMessage CreateUnauthorizedResponse()
    {
        // 刷新nonce,防止重放攻击
        _nonce = GenerateNonce();
        
        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(
            "Digest",
            $"realm=\"{_realm}\",nonce=\"{_nonce}\",qop=\"auth\""));
        response.Content = new StringContent("需要Digest认证");
        return response;
    }

    private bool ValidateDigestCredentials(DigestParameters parameters, string httpMethod)
    {
        // 验证用户名合法性
        if (parameters.Username != _validUsername)
            return false;

        // 计算HA1: MD5(用户名:领域:密码)
        string ha1 = CalculateMd5Hash($"{parameters.Username}:{_realm}:{_validPassword}");
        // 计算HA2: MD5(请求方法:请求URI)
        string ha2 = CalculateMd5Hash($"{httpMethod}:{parameters.Uri}");
        // 计算预期响应值: MD5(HA1:nonce:nc:cnonce:qop:HA2)
        string expectedResponse = CalculateMd5Hash($"{ha1}:{_nonce}:{parameters.Nc}:{parameters.Cnonce}:{parameters.Qop}:{ha2}");

        // 比对客户端响应值和计算值
        return parameters.Response.Equals(expectedResponse, StringComparison.OrdinalIgnoreCase);
    }

    private DigestParameters ParseDigestParameters(string parameterString)
    {
        var parameters = new DigestParameters();
        var parts = parameterString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

        foreach (var part in parts)
        {
            var keyValue = part.Split(new[] { '=' }, 2);
            if (keyValue.Length != 2) continue;

            string key = keyValue[0].Trim();
            string value = keyValue[1].Trim().Trim('\"');

            switch (key.ToLower())
            {
                case "username":
                    parameters.Username = value;
                    break;
                case "realm":
                    parameters.Realm = value;
                    break;
                case "nonce":
                    parameters.Nonce = value;
                    break;
                case "uri":
                    parameters.Uri = value;
                    break;
                case "response":
                    parameters.Response = value;
                    break;
                case "qop":
                    parameters.Qop = value;
                    break;
                case "nc":
                    parameters.Nc = value;
                    break;
                case "cnonce":
                    parameters.Cnonce = value;
                    break;
            }
        }

        return parameters;
    }

    private string CalculateMd5Hash(string input)
    {
        using (MD5 md5 = MD5.Create())
        {
            byte[] inputBytes = Encoding.ASCII.GetBytes(input);
            byte[] hashBytes = md5.ComputeHash(inputBytes);
            StringBuilder sb = new StringBuilder();
            foreach (byte b in hashBytes)
            {
                sb.Append(b.ToString("x2"));
            }
            return sb.ToString();
        }
    }

    private string GenerateNonce()
    {
        // 使用GUID生成随机nonce,可加入时间戳增强防重放能力
        return Guid.NewGuid().ToString("N");
    }

    private class DigestParameters
    {
        public string Username { get; set; }
        public string Realm { get; set; }
        public string Nonce { get; set; }
        public string Uri { get; set; }
        public string Response { get; set; }
        public string Qop { get; set; }
        public string Nc { get; set; }
        public string Cnonce { get; set; }
    }
}

注册Handler到Web API

在WebApiConfig.cs中添加Handler注册代码:

public static void Register(HttpConfiguration config)
{
    // 注册Digest认证Handler
    config.MessageHandlers.Add(new DigestAuthenticationHandler());

    // 常规Web API路由配置
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

客户端适配说明

你提供的.NET 4.5客户端代码使用NetworkCredential配合HttpClientHandler,会自动处理服务端的Digest挑战-响应流程,无需修改现有客户端代码即可与上述服务端实现对接:

public static HttpClient Client(Device device, int timeoutInSecond)
{
    var handler = new HttpClientHandler()
           {
                    Credentials = new NetworkCredential(device.Credentials.UserName,device.Credentials.Password)            
           };
    
    var client = new HttpClient(handler) { BaseAddress = new Uri(device.Url) }; 
    client.Timeout = TimeSpan.FromSeconds(timeoutInSecond);

    return client;
}

Postman配置说明

截图中显示在Postman的认证设置里选择了Digest类型,填写用户名和密码后,Postman会自动识别服务端返回的WWW-Authenticate头,生成符合规范的Authorization请求头,完成认证流程。

注意事项

  • 你提到Digest认证并非最安全的实现,生产环境请务必配合HTTPS使用,避免认证参数被明文窃取。
  • 示例中的用户信息为硬编码,实际开发中请替换为数据库或加密配置存储。
  • 可优化nonce生成逻辑,加入时间戳和签名,进一步防范重放攻击。

内容的提问来源于stack exchange,提问作者greektreat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 08:23:12