前言

HTTP Authentication 是很古老的东西. 已经很少地方会用到了. 但还是给我遇上了.

在做 Google Ads Offline Conversion 时, 它提供了 2 种方式让我 upload offline conversion.

第一种是顺风水的 best practice, 通过 Google Ads API. (OAuth + C# dll)

第二种是通过 Google Ads UI 设置一个 server task, 让它每天访问一个 HTTP 地址. 这个地址 provide 一个 .csv file, file 里面自然就是 offline conversion 需要的资料.

为了安全, 这个 HTTP request 就用上了 HTTP Authentication.

当然这个安全级别是蛮低的. but is ok, 毕竟 offline conversion 不会涉及太多敏感信息. 如果想更安全, 那就用 API 咯.

我只是纯粹想体验一下古老技术所以才玩玩的.

参考

一文读懂HTTP Basic身份认证

Implementing Basic Authentication in ASP.NET Core Minimal API

Implementing Basic Authentication in ASP.NET 6

How to Add Basic Authentication to an ASP.NET Core Application: OAuth Security - Part 1

YouTube – Basic Authentication in DOT NET Core Web API using VS Code | .NET CORE 6.0 Tutorial

HTTP Authentication 介绍

有些资源需要被保护, 最简单的方法就是搞 username password. 所以 HTTP 就拟定了一个简单的 username password 潜规则, 让客户端和服务端用一个很简单的方式去实现简单的资源保护.

流程

它的流程是这样的.

1. 游览器请求一个被保护的资料

2. 服务端验证, 并返回 401 status 再加一个 header

header key: WWW-Authenticate
header value: basic realm="product"
basic 是这个 Authentication 的代号, 让游览器知道, 资源被保护, 可以使用 basic HTTP Authentication 方式访问.
realm 下面会解释
 
3. 游览器会 popup username and password

4. 用户输入 username password 后, 游览器会再次发请求, 并且加上一个 header

header key: Authorization

header value: base64(username + ":" + password)

username password 会通过 base64 encode.

5. 服务器验证 username password 通过就 response 200

realm 是什么?

游览器支持记入多个 username password. 所以资源可以分组进行保护. 分组就是通过 realm 完成的

realm="a group name"

比如

/products (realm="product")

/models (realm="product")

/orders (realm="order")

当游览访问 products 后, 就会把 username password 记入起来, 并且记入它是 product group 的 username password

当访问 models 的时候, 游览器会直接附上 username password, 直接验证通过

当访问 orders 的时候, 游览器依然会直接附上 username password, 但这次显然是不能通过的, 因为资源组不同, username password 也不同嘛.

验证失败之后, 游览器获得 401 和 basic realm="order", 然后 popup > fill in username password > valid response > 最后把这个 username password 记入起来 under order group.

这时游览器就有了 2 pair username password, 一个负责 product group, 一个负责 order group 的资源访问.

ASP.NET Core Simple Test

dotnet new webapi -o TestHttpAuthentication

Controller

using System.Text;
using Microsoft.AspNetCore.Mvc; namespace TestHttpAuthentication.Controllers; [ApiController]
public class TestControllerController : ControllerBase
{
[HttpGet("products")]
public ActionResult<List<string>> GetProducts()
{
// 游览器没有提供 username password header Authorization, 直接返回 401
if (!Request.Headers.Any(e => e.Key == "Authorization"))
{
Response.Headers.Add("WWW-Authenticate", "basic realm=\"product\"");
return Unauthorized();
} // 从游览器的 header Authorization 拆解出 username password
var base64 = Request.Headers.Authorization.ToString().Split(" ")[1];
var base64Bytes = Convert.FromBase64String(base64);
var usernameAndPassword = Encoding.UTF8.GetString(base64Bytes);
var username = usernameAndPassword.Split(":")[0];
var password = usernameAndPassword.Split(":")[1];
// 通过就返回 200
if (username == "keatkeat" && password == "password")
{
return new List<string> { "Product 1", "Product 2", "Product 3" };
}
// 失败就返回 401
else
{
Response.Headers.Add("WWW-Authenticate", "basic realm=\"product\"");
return Unauthorized();
}
}
}

效果

ASP.NET Core 封装 HTTP Authentication

ASP.NET Core 没有 build-in 的 Authentication 机制, 可能是因为很少人用又容易实现, 所以它就不做了. 那我们自己做一个呗.

参考

Implementing Basic Authentication in ASP.NET Core Minimal API

Working with custom authentication schemes in ASP.NET Core 6.0 Web API

Program.cs

namespace TestHttpAuthentication;

public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddAuthentication()
.AddScheme<BasicAuthenticationSchemeOptions, BasicAuthenticationHandler>("GoogleAdsBasicAuthentication", option =>
{
option.SetRealm("Google Ads");
option.AddUser("keatkeat", "password");
});
builder.Services.AddAuthorization(); var app = builder.Build(); if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
} app.UseHttpsRedirection(); app.UseAuthentication();
app.UseAuthorization(); app.MapControllers(); app.Run();
}
}

主要是添加了这几个

在 Identity – Without Identity Framework 有提过如何使用底层的 ASP.NET Core Authentication, 但那个依然是用了 build-in 的 CookieScheme.

而这里, 我们是创建了一个全新的 Scheme.

TestController.cs

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; namespace TestHttpAuthentication.Controllers; [ApiController]
public class TestControllerController : ControllerBase
{
[HttpGet("products")]
[Authorize(AuthenticationSchemes = "GoogleAdsBasicAuthentication")]
public ActionResult<List<string>> GetProducts()
{
return Ok(new List<string> { "Product 1", "Product 2", "Product 3" });
} [HttpGet("models")]
public ActionResult<List<string>> GetModels()
{
return Ok(new List<string> { "Model 1", "Model 2", "Model 3" });
}
}

BasicAuthentication.cs

using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options; namespace TestHttpAuthentication; public class BasicAuthenticationSchemeOptions : AuthenticationSchemeOptions
{
public string Realm { get; set; } = "";
public void SetRealm(string realm)
{
Realm = realm;
} public List<User> Users = new(); public void AddUser(string username, string password)
{
Users.Add(new User
{
Username = username,
Password = password,
});
} public bool ValidUser(string username, string password)
{
return Users.Any(e => e.Username == username && e.Password == password);
} public class User
{
public string Username { get; set; } = "";
public string Password { get; set; } = "";
}
} public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationSchemeOptions>
{
public BasicAuthenticationHandler(
IOptionsMonitor<BasicAuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock
) : base(options, logger, encoder, clock)
{
} protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authorization = Request.Headers.Authorization.ToString();
if (!authorization.StartsWith("Basic ")) return Fail(); var base64 = authorization.Split(" ")[1];
var base64Bytes = Convert.FromBase64String(base64);
var usernameAndPassword = Encoding.UTF8.GetString(base64Bytes);
var username = usernameAndPassword.Split(":")[0];
var password = usernameAndPassword.Split(":")[1]; if (Options.ValidUser(username, password))
{
var claims = new List<Claim> { new Claim("name", username) }; // 不一定要放 name claims
var identity = new ClaimsIdentity(claims, authenticationType: "Basic"); // 一定要 set authenticationType to Basic
var claimsPrincipal = new ClaimsPrincipal(identity);
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, Scheme.Name)));
}
return Fail(); Task<AuthenticateResult> Fail()
{
Response.StatusCode = 401;
Response.Headers.Add("WWW-Authenticate", $"basic realm=\"{Options.Realm}\"");
return Task.FromResult(AuthenticateResult.Fail("Authentication failed"));
}
}
}

蛮简单的, 一个 Options 配一个 Handler.

Handler 里面负责从 Request 获取信息, 然后验证通过与否.

Identity – HTTP Authentication的更多相关文章

  1. ASP.NET Core 身份认证 (Identity、Authentication)

    Authentication和Authorization 每每说到身份验证.认证的时候,总不免说提及一下这2个词.他们的看起来非常的相似,但实际上他们是不一样的. Authentication想要说明 ...

  2. Asp.net core 学习笔记 ( Identity 之 Authentication )

    和从前的 identity 区别不是很大. 从 2.1 开始 vs 模板的 identity 都被封装了起来, 你几乎看不到任何一行代码, 需要向下面这样打开它, 才能做修改. 说一下比较常用的配置 ...

  3. [SharePoint]SharePoint Claim base Authentication的一个比较好的介绍

    User identity in AD DS is based on a user account. For successful authentication, the user provides ...

  4. 全新的membership框架Asp.net Identity(1)——.Net membership的历史

    在Asp.net上,微软的membershop框架经历了Asp.net membership到Asp.net simple membership,再到现在的Asp.net Identity. 每一次改 ...

  5. MVC MemeberShip vs. Asp.net Identity

    参考 从Membership 到 .NET4.5 之 ASP.NET Identity Extending Identity Accounts and Implementing Role-Based ...

  6. 框架Asp.net Identity

    框架Asp.net Identity 在Asp.net上,微软的membershop框架经历了Asp.net membership到Asp.net simple membership,再到现在的Asp ...

  7. [转]The NTLM Authentication Protocol and Security Support Provider

    本文转自:http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication The NTLM Authentication Proto ...

  8. 全新的membership框架Asp.net Identity

    在Asp.net上,微软的membershop框架经历了Asp.net membership到Asp.net simple membership,再到现在的Asp.net Identity. 每一次改 ...

  9. MVC6 (ASP.NET5) 认证 (Asp.net identity) cookie模式 自定义认证

    1.Startup类的Configure方法中, app.UseIdentity(); 改为 app.UseCookieAuthentication(options => { options.A ...

  10. OpenStack Identity API v3 extensions (CURRENT)

    Table Of Contents Identity API v3 extensions (CURRENT) OS-ENDPOINT-POLICY API Associate policy and e ...

随机推荐

  1. 屏幕分辨率基础概念PX,PT,DP,DPR,DPI说明

    屏幕分辨率基础概念说明 缩写 全称 说明 PX Device Pixels 设备像素,指设备的物理像素 PX CSS Pixels CSS像素,指CSS样式代码中使用的逻辑像素 DOT Dot 点,屏 ...

  2. API引用在Element UI (Vue 2)和Element Plus (Vue 3)中的不同

    API 变动 样式类名变化: 一些组件的样式类名有所变动,可能需要更新你的自定义样式. 事件名和属性名变化: 某些组件的事件名和属性名发生了变化,需要检查 Element Plus 文档 以了解详细信 ...

  3. .NET开源、简单、实用的数据库文档生成工具

    前言 今天大姚给大家分享一款.NET开源(MIT License).免费.简单.实用的数据库文档(字典)生成工具,该工具支持CHM.Word.Excel.PDF.Html.XML.Markdown等多 ...

  4. 关于使用c++制作蓝牙连接,Windows版本

    1 #define _CRT_SECURE_NO_WARNINGS 2 #pragma warning(disable : 4995) 3 #include <iostream> 4 #i ...

  5. Cloudflare教程:如何注册账户、购买域名、开启免费CDN服务?

    Cloudflare介绍 什么是Cloudflare Cloudflare是一家总部位于旧金山的美国跨国科技企业,以向客户提供基于反向代理的内容分发网络(CDN)及分布式域名解析服务为主要业务. 目前 ...

  6. 句子成分&分类 被动

    句子成分&分类 简单句的两个主要句子成分,分别是主语 + 谓语 谓语的核心是谓语动词 谓语 不等于 谓语动词 主语 谓语 [The rabbit] [ate a carrot] ate 为谓语 ...

  7. 【Android】构建Android12项目报错

    报错信息: Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Man ...

  8. 【Lodop】01 Lodop手册阅读上手

    官方网站: http://www.c-lodop.com/index.html 版本:6.2.2.6 一.概述 Lodop是一款用于WEB打印开发的专业WEB打印控件 控件发布包有3个系统文件组成,主 ...

  9. 【Mybatis-Plus】01 快速上手

    [官网快速上手地址] https://mp.baomidou.com/guide/quick-start.html#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B7%A5%E7%A8 ...

  10. 哈哈哈,我就说未来要研发无人的AI潜艇嘛 —— 说啥来啥 —— AI驱动的无人潜艇

    相关: 沉默5个月后,美国对华发出挑战书,万没想到,中方打法早就变了