1、Http BasicAuth 概述
Http BasicAuth 即 Http 基本认证,其最简单的实质是,服务器允许客户端携带账号密码/加密认证请求头来访问受保护资源,例如:
curl http://username:password@localhost/api/users
实现访问受保护资源- Http请求头中,加入Authorization, 值为 Basic
base64str
实现访问受保护资源
基本的交互流程如下图所示
2、项目搭建
根据以下步骤,创建一个基本的NetCore项目(基于NetCore3.1版本)
根据上面三个图所示,我们即可创建好一个最基本的NetCore项目
3. BasicAuth配置
3.1 自定义AuthenticationHandler
通过自定义AuthenticationHandler
实现BasicAuth认证,实现代码如下:
using ApiBasicAuth.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
namespace ApiBasicAuth.Security
{
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
{
Response.Headers.Add("WWW-Authenticate", @"Basic realm='Secure Area'");
return AuthenticateResult.Fail("Missing Authorization Header");
}
User user = null;
try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
var username = credentials[0];
var password = credentials[1];
if (username.Equals("admin") && password.Equals("password"))
{
user = new User { Id=1, Username = "admin", Birthday = DateTime.Now };
}
}
catch
{
// Base64字符串解码失败
return AuthenticateResult.Fail("Invalid Authorization Header");
}
if (user == null)
return AuthenticateResult.Fail("Invalid Username or Password");
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
}
其中有几点值得比较注意:
Response.Headers.Add("WWW-Authenticate", @"Basic realm='Secure Area'");
在认证失败时,返回这个请求头,则是告诉浏览器,要求弹出认证信息输入窗口(浏览器自身功能),若没返回此请求头,则浏览器不会弹出自带账号/密码输入窗口,直接显示401if (username.Equals("admin") && password.Equals("password"))
在示例代码中,直接固定只支持admin账号,在实际中,因该是根据诸如UserService
等服务层,来判断用户账号信息var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username), };
在示例中,默认只是给当前用户设置了两个基本属性,在实际中,我们可以根据用户的角色/权限等信息,注入对应的Claim,如:
var roles = new List<string>(); foreach (var item in roles) { claims.Append(new Claim(ClaimTypes.Role, item)); };
3.2 修改NetCore认证配置
在public void ConfigureServices(IServiceCollection services)
中,增加如下配置
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
在public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
中,增加如下配置:
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
至此,项目的认证服务配置,搭建完成
4、认证功能验证
通过上面的章节,我们已经配置好认证服务,下面则是开始构建Controller,来校验认证是否生效。如下图,我们便携一个最基本的Controller
namespace ApiBasicAuth.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class UserController : ControllerBase
{
[HttpGet]
[AllowAnonymous]
public IActionResult NoSecurity() {
return Ok("NoSecurity");
}
[HttpGet]
[Authorize]
public IActionResult Security()
{
return Ok("Security");
}
}
}
其中
/api/User/Security
需要认证后才能访问/api/User/NoSecuirty
不需要认证就能访问
我们开始进行校验:
- 访问
/api/User/Security
不携带认证信息
请求返回401,表明未认证,则代表我们配置的认证服务已生效。
- 访问
/api/User/Security
携带认证信息
可以看到,返回Security,表明已经认证通过
- 访问
/api/User/NoSecurity
不携带认证信息
可以看到,对于无需认证的接口,不携带认证信息也能获取内容。
5、结语
至此,BasicAuth 认证服务就配置验证完成,其实我们可以通过这个例子,可以衍生推导出如何实现JWT认证服务,基本原理一致,只是需要稍微修改BasicAuthenticationHandler
配置即可,后续这边也会编写NetCore实现自定义JWT认证服务相关文章。
欢迎关注公众号,获取最新文章信息