asp.net自定义认证(1)

Program.cs

主要看AddAuthentication,AddScheme,UseAuthentication,UseAuthorization
using CustomAuthentication.AuthenticationHandlers;

var builder = WebApplication.CreateBuilder(args);


builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();


//AddAuthentication的参数为默认方案名,该方案任何情况下都会执行(如果明确指定方案的话,代码会执行,但不影响最终认证结果)
//SecretAuthenticationOptions(自定义)继承自AuthenticationSchemeOptions,可自定义一些选项
//SecretAuthenticationHandler(自定义)继承自AuthenticationHandler<SecretAuthenticationOptions>,实现具体认证过程
//AddScheme的第一个参数为自定义方案名,第二个参数可配置SecretAuthenticationOptions
builder.Services.AddAuthentication(SecretAuthenticationOptions.Scheme)
    .AddScheme<SecretAuthenticationOptions, SecretAuthenticationHandler>(SecretAuthenticationOptions.Scheme, options => {} );
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//认证中间件(认证在授权前)
app.UseAuthentication();
//授权中间件
app.UseAuthorization();

app.MapControllers();

app.Run();

AuthenticationHandler<TOptions>

  public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler
    where TOptions : AuthenticationSchemeOptions, new()
{
    //省略了一些方法...


    //具体认证过程(子类实现)
    protected abstract Task<AuthenticateResult> HandleAuthenticateAsync();

    //若Options.ForwardChallenge没有指定挑战方案
    //则在认证失败的情况下执行该方法,以下是默认实现
    protected virtual Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        this.Response.StatusCode = 401;
        return Task.CompletedTask;
    }

    public async Task ChallengeAsync(AuthenticationProperties? properties)
    {
        string scheme = this.ResolveTarget(this.Options.ForwardChallenge);
        if (scheme != null)
        {
            await this.Context.ChallengeAsync(scheme, properties);
        }
        else
        {
            if (properties == null)
                properties = new AuthenticationProperties();
            await this.HandleChallengeAsync(properties);
            this.Logger.AuthenticationSchemeChallenged(this.Scheme.Name);
        }
    }

    //若Options.ForwardForbid没有指定授权失败方案
    //则在认证成功但授权失败的情况下执行该方法,以下是默认实现
    protected virtual Task HandleForbiddenAsync(AuthenticationProperties properties)
    {
        this.Response.StatusCode = 403;
        return Task.CompletedTask;
    }

    public async Task ForbidAsync(AuthenticationProperties? properties)
    {
        string scheme = this.ResolveTarget(this.Options.ForwardForbid);
        if (scheme != null)
        {
            await this.Context.ForbidAsync(scheme, properties);
        }
        else
        {
            if (properties == null)
                properties = new AuthenticationProperties();
            await this.HandleForbiddenAsync(properties);
            this.Logger.AuthenticationSchemeForbidden(this.Scheme.Name);
        }
    }
}

自定义认证处理器

ClaimsPrincipal :人(User)
ClaimsIdentity : 人拥有的证件,例如身份证(人可以拥有多张证件)
Claims : 证件上的信息,例如姓名,出生日期
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;

namespace CustomAuthentication.AuthenticationHandlers;

//自定义选项
public class SecretAuthenticationOptions : AuthenticationSchemeOptions
{
    //自定义方案名
    public const string Scheme = "Secret";
}

//自定义角色
public static class SecretRoles
{
    //小红的朋友
    public const string XiaoHongFriend = "XiaoHongFriend";

    //小红的爱慕者
    public const string XiaoHongAdorer = "XiaoHongAdorer";
}

public class SecretAuthenticationHandler : AuthenticationHandler<SecretAuthenticationOptions>
{
    public SecretAuthenticationHandler(IOptionsMonitor<SecretAuthenticationOptions> options, ILoggerFactory logger,
        UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    private Task<AuthenticateResult> createSingleIdentityAuthenticationTicket(IEnumerable<Claim> claims)
    {
        //创建主体
        var claimsPrincipal = new ClaimsPrincipal();
        //创建证件
        var claimsIdentity = new ClaimsIdentity();
        //为证件添加信息
        claimsIdentity.AddClaims(claims);
        //为主体添加证件
        claimsPrincipal.AddIdentity(claimsIdentity);
        //返回认证票据
        return Task.FromResult(
            AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, SecretAuthenticationOptions.Scheme)));
    }


    //小红的朋友
    private string[] xiaohongFriends = { "小红", "小芳", "小樱" };
    //小红的爱慕者
    private string[] xiaohongAdorers = { "小明", "小李", "小华" };

    //具体认证过程
    //若该方案是默认方案,无论任何时候(action存不存在,有没有标注Authorize特性),该方法都会执行(如果明确指定方案的话,代码会执行,但不影响最终认证结果)
    //若不是默认方案,则仅在标注了Authorize特性,并指定了该方案的情况下,该方法才会执行
    //若认证成功,会将返回值中的claimsPrincipal赋值给HttpContext.User,后续在action中可以访问
    //ClaimTypes.Role为预定义的Claim类型
    //ClaimTypes.Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
    protected override Task<AuthenticateResult> HandleAuthenticateAsync() => Request.Query["name"] switch
    {
        //认证成功,角色设置为小红的朋友,name为自定义的额外信息
        { Count: 1 } s when xiaohongFriends.Contains(s.ToString()) => createSingleIdentityAuthenticationTicket(new[]
        {
            new Claim(ClaimTypes.Role, SecretRoles.XiaoHongFriend),
            new Claim("name", s.ToString())
        }),
        //认证成功,角色设置为小红的爱慕者,name为自定义的额外信息
        { Count: 1 } s when xiaohongAdorers.Contains(s.ToString()) => createSingleIdentityAuthenticationTicket(new[]
        {
            new Claim(ClaimTypes.Role, SecretRoles.XiaoHongAdorer),
            new Claim("name", s.ToString())
        }),
        //认证失败
        _ => Task.FromResult(AuthenticateResult.NoResult())
    };
}

控制器代码

using CustomAuthentication.AuthenticationHandlers;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace CustomAuthentication.Controllers;

[Route("[controller]/[action]")]
public class SecretSpaceController : ControllerBase
{
    //若未指定AuthenticationSchemes,则仅有默认方案会执行(如果明确指定方案的话,代码会执行,但不影响最终认证结果)
    //AuthenticationSchemes可指定多个方案,用逗号分割,从左到右执行所有认证方案,若全部认证失败则失败,否则认证成功,并且合并所有结果返回(默认方案总是第一个执行,但不影响认证结果)
    //挑战方案和授权失败方案由最后一个方案提供
    //这里使用的是基于角色的访问控制(RBAC),限制了只有小红的朋友才能访问该action
    [Authorize(AuthenticationSchemes = SecretAuthenticationOptions.Scheme, Roles = SecretRoles.XiaoHongFriend)]
    [HttpGet]
    public string Secret1()
    {
        //User为ClaimsPrincipal类型,认证成功后会为其赋值(没有认证该值也不是null,是个空对象)
        //从中寻找类型为name的Claim信息
        return $"{HttpContext.User.FindFirst("name")?.Value ?? "无名"}知道了小明喜欢小红";
    }
}

实验结果

  • 认证失败(默认401响应)

认证失败

  • 认证成功但授权失败(默认403响应)

认证成功但授权失败

  • 授权成功

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值