9

ASP.NETCore编程实现基本认证

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA%3D%3D&%3Bmid=2654077279&%3Bidx=4&%3Bsn=bac10e340b36392b3039a21f226775b9
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

RFrQv2M.gif

HTTP基本认证

在HTTP中,HTTP基本认证(Basic Authentication)是一种允许浏览器或其他客户端程序使用(用户名,口令)请求资源的身份验证方式,不要求cookie,session identifier、login page等标记或载体。   

  • 所有浏览器据支持HTTP基本认证协议

  • 基本身证原理不保证传输凭证的安全性,仅被based64编码,并没有encrypted或者hashed,一般部署在互信的内网,在公网上应用BA协议通常与https结合。

BA标准协议

BA协议的实施主要依靠约定的请求头/响应头, 典型的浏览器和服务器的BA认证流程:

① 浏览器请求应用了BA的网站,服务端响应一个401认证失败响应码,并写入 WWW-Authenticate 响应头,指示服务端支持BA协议。

HTTP/1.1 401 Unauthorized

WWW-Authenticate: Basic realm="our site"

或在初次请求时发送正确 Authorization 标头,从而避免被质询

② 客户端以based64(用户名:口令) 作为 Authorization 标头值,重新发送请求:

Authorization : Basic userid:password

nm22EvN.png!web

认证的范围与realm相关,准确的realm由服务端定义,因为服务端可能有多个不同的realm.

> 浏览器客户端,对于 WWW- Authenticate 响应头弹出了口令输入窗。

BA编程实践

ASP.NET Core网站利用FileServerMiddleware将部分路径映射到文件资源,对该资源访问路径应用Http基本认证。

服务端实现BA认证

①  实现基本认证Handler:认证、无口令质询、质询失败逻辑

# ......

namespace EqidManager.Services

{

public static class BasicAuthenticationScheme

{

public const string DefaultScheme = "Basic";

}

public class BasicAuthenticationOption:AuthenticationSchemeOptions

{

public string Realm { get; set; }

public string UserName { get; set; }

public string UserPwd { get; set; }

}


public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOption>

{

private readonly BasicAuthenticationOption authOptions;

public BasicAuthenticationHandler(

IOptionsMonitor<BasicAuthenticationOption> options,

ILoggerFactory logger,

UrlEncoder encoder,

ISystemClock clock)

: base(options, logger, encoder, clock)

{

authOptions = options.CurrentValue;

}


/// <summary>

/// 认证逻辑

/// </summary>

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()

{

if (!Request.Headers.ContainsKey("Authorization"))

return AuthenticateResult.Fail("Missing Authorization Header");

string username, password;

try

{

var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);

var credentialBytes = Convert.FromBase64String(authHeader.Parameter);

var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');

username = credentials[0];

password = credentials[1];

var isValidUser= IsAuthorized(username,password);

if(isValidUser== false)

return AuthenticateResult.Fail("Invalid username or password");

}

catch

return AuthenticateResult.Fail("Invalid Authorization Header");

var claims = new[] {

new Claim(ClaimTypes.NameIdentifier,username),

new Claim(ClaimTypes.Name,username),

};

var identity = new ClaimsIdentity(claims, Scheme.Name);

var principal = new ClaimsPrincipal(identity);

var ticket = new AuthenticationTicket(principal, Scheme.Name);

return await Task.FromResult(AuthenticateResult.Success(ticket));

}


/// <summary>

/// 质询

/// </summary>

protected override async Task HandleChallengeAsync(AuthenticationProperties properties)

{

Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{Options.Realm}\"";

await base.HandleChallengeAsync(properties);

}


/// <summary>

/// 认证失败

/// </summary>

protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)

{

await base.HandleForbiddenAsync(properties);

}

private bool IsAuthorized(string username, string password)

{

return username.Equals(authOptions.UserName, StringComparison.InvariantCultureIgnoreCase)

&& password.Equals(authOptions.UserPwd);

}

}

}

②  实现BasicAuthenticationMiddleware: 要求对HttpContext应用BA Scheme。

// HTTP基本认证Middleware

public static class BasicAuthentication

{

public static void UseBasicAuthentication(this IApplicationBuilder app)

{

app.UseMiddleware<BasicAuthenticationMiddleware>();

}

}

public class BasicAuthenticationMiddleware

{

private readonly RequestDelegate _next;

private readonly ILogger _logger;

public BasicAuthenticationMiddleware(RequestDelegate next, ILoggerFactory LoggerFactory)

{

_next = next;

_logger = LoggerFactory.CreateLogger<BasicAuthenticationMiddleware>();

}

public async Task Invoke(HttpContext httpContext, IAuthenticationService authenticationService)

{

var authenticated = await authenticationService.AuthenticateAsync(httpContext, BasicAuthenticationScheme.DefaultScheme);

_logger.LogInformation("Access Status:" + authenticated.Succeeded);

if (!authenticated.Succeeded)

{

await authenticationService.ChallengeAsync(httpContext, BasicAuthenticationScheme.DefaultScheme, new AuthenticationProperties { });

return;

}

await _next(httpContext);

}

}

ASP. N ET  Core 添加BA Scheme , 为 证资源路径 用BA 中间件,注意这里使用UseWhen插入中间件。

services.AddAuthentication(BasicAuthenticationScheme.DefaultScheme)

.AddScheme<BasicAuthenticationOption, BasicAuthenticationHandler>(BasicAuthenticationScheme.DefaultScheme,null);

app.UseWhen(

predicate:x => x.Request.Path.StartsWithSegments(new PathString(_protectedResourceOption.Path)),

configuration:appBuilder => { appBuilder.UseBasicAuthentication();}

);

现可在浏览器测试:

UBzURvy.gif

进一步思考?

以上是浏览器在 BA协议中的行为 :可尝试 程序自动向服务端发起BA请求,需要的同学看博客园源码。

That's All .  BA认证是常见的基础认证协议,文章期待以清晰的方式传递协议原理和编程实现,要的同学阅读原文。

nIn6jeF.jpg


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK