asp.net core3.1 實戰開發(授權,鑑權封裝詳解)

2b勿擾發表於2020-01-10

使用方式

#region 設定自己的schema的handler 
            services.AddAuthenticationCore(options => options.AddScheme<MyHandler>("myScheme", "demo myScheme"));
            #endregion

            #region 支援 policy 認證授權的服務  

            // 指定通過策略驗證的策略列
            services.AddSingleton<IAuthorizationHandler, AdvancedRequirement>();

            services.AddAuthorization(options =>
            {
                //AdvancedRequirement可以理解為一個別名
                options.AddPolicy("AdvancedRequirement", policy =>
                {
                    policy.AddRequirements(new NameAuthorizationRequirement("1"));
                });
            }).AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie(options =>
            {
                options.LoginPath = new PathString("/Fourth/Login");
                options.ClaimsIssuer = "Cookie";
            });

            #endregion

            #region  Schame 驗證

            services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;// "Richard";//  
            })
            .AddCookie(options =>
            {
                options.LoginPath = new PathString("/Fourth/Login");// 這裡指定如果驗證不通過就跳轉到這個頁面中去
                options.ClaimsIssuer = "Cookie";
            });
            #endregion
#region 認證授權基本模型
 登入
app.Map("/login", builder => builder.Use(next =>
{
    return async (context) =>
    {
        var claimIdentity = new ClaimsIdentity();  // 可以把這個ClaimsIdentity理解成一個身份證
        claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "XT")); // 使用者資訊的載體
        // Scheme 可以理解為 跟身份證對應的一個標識
        await context.SignInAsync("myScheme", new ClaimsPrincipal(claimIdentity));
        await context.Response.WriteAsync($"Hello, ASP.NET Core! {context.User.Identity.Name} Login");
    };
}));
// 退出
app.Map("/logout", builder => builder.Use(next =>
{
    return async (context) =>
    {
        await context.SignOutAsync("myScheme");
    };
}));

app.Use(next => //認證   認識身份證
{
    return async (context) =>
    {
        var result = await context.AuthenticateAsync("myScheme");
        if (result?.Principal != null)
        {
            context.User = result.Principal;
            await next(context);
        }
        else
        {
            await context.ChallengeAsync("myScheme");
        }
    };
});
// 授權 身份證有什麼許可權
app.Use(async (context, next) =>
{
    var user = context.User;
    if (user.Identity.Name.Equals("XT"))//user?.Identity?.IsAuthenticated這裡沒有授權檢測 只檢查下名稱
    {
        await next();
    }
    else
    {
        await context.ForbidAsync("myScheme");
    }
    //if (user?.Identity?.IsAuthenticated ?? false)
    //{
    //    if (user.Identity.Name != "jim") await context.ForbidAsync("eScheme");
    //    else await next();
    //}
    //else
    //{
    //    await context.ChallengeAsync("eScheme");
    //}
});

// 訪問受保護資源
app.Map("/resource", builder => builder.Run(async (context) =>
{
    await context.Response.WriteAsync($"Hello, ASP.NET Core! {context.User.Identity.Name}");
}));

app.Run(async (HttpContext context) =>
{
    await context.Response.WriteAsync("Hello World,success!");
});
#endregion

封裝

/// <summary>
    /// 自定義的handler
    /// 通常會提供一個統一的認證中心,負責證書的頒發及銷燬(登入和登出),而其它服務只用來驗證證書,並用不到SingIn/SingOut。
    /// </summary>
    public class MyHandler : IAuthenticationHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler
    {
        public AuthenticationScheme Scheme { get; private set; }
        protected HttpContext Context { get; private set; }

        public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
        {
            Scheme = scheme;
            Context = context;
            return Task.CompletedTask;
        }

        /// <summary>
        /// 認證
        /// </summary>
        /// <returns></returns>
        public async Task<AuthenticateResult> AuthenticateAsync()
        {
            var cookie = Context.Request.Cookies["myCookie"];
            if (string.IsNullOrEmpty(cookie))
            {
               return  AuthenticateResult.NoResult();
            }
            return AuthenticateResult.Success(this.Deserialize(cookie));
        }

        /// <summary>
        /// 沒有登入 要求 登入 
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        public Task ChallengeAsync(AuthenticationProperties properties)
        {
            Context.Response.Redirect("/login");
            return Task.CompletedTask;
        }

        /// <summary>
        /// 沒許可權
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        public Task ForbidAsync(AuthenticationProperties properties)
        {
            Context.Response.StatusCode = 403;
            return Task.CompletedTask;
        }

        /// <summary>
        /// 登入
        /// </summary>
        /// <param name="user"></param>
        /// <param name="properties"></param>
        /// <returns></returns>
        public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
        {
            var ticket = new AuthenticationTicket(user, properties, Scheme.Name);
            Context.Response.Cookies.Append("myCookie", this.Serialize(ticket));
            return Task.CompletedTask;
        }

        /// <summary>
        /// 退出
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        public Task SignOutAsync(AuthenticationProperties properties)
        {
            Context.Response.Cookies.Delete("myCookie");
            return Task.CompletedTask;
        }
        private AuthenticationTicket Deserialize(string content)
        {
            byte[] byteTicket = System.Text.Encoding.Default.GetBytes(content);
            return TicketSerializer.Default.Deserialize(byteTicket);
        }

        private string Serialize(AuthenticationTicket ticket)
        {

            //需要引入  Microsoft.AspNetCore.Authentication

            byte[] byteTicket = TicketSerializer.Default.Serialize(ticket);
            return Encoding.Default.GetString(byteTicket);
        }
    }

    public class TicketDataFormat : SecureDataFormat<AuthenticationTicket>// IDataSerializer<AuthenticationTicket>//
    {
        public TicketDataFormat(IDataProtector protector)
            : base(TicketSerializer.Default, protector)
        {
        }
    }
/// <summary>
    /// Policy 的策略 或者是規則
    /// </summary>
    public class AdvancedRequirement : AuthorizationHandler<NameAuthorizationRequirement>, IAuthorizationRequirement
    { 
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NameAuthorizationRequirement requirement)
        {
            // 這裡可以把使用者資訊獲取到以後通過資料庫進行驗證
            // 這裡就可以做一個規則驗證
            // 也可以通過配置檔案來驗證
            if (context.User != null && context.User.HasClaim(c => c.Type == ClaimTypes.Sid))
            {
                string sid = context.User.FindFirst(c => c.Type == ClaimTypes.Sid).Value;
                if (!sid.Equals(requirement.RequiredName))
                {
                    context.Succeed(requirement);
                }
            }

            return Task.CompletedTask;
        }
    }
public class RoleAuthorizationRequirement : AuthorizationHandler<RoleAuthorizationRequirement>, IAuthorizationRequirement
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleAuthorizationRequirement requirement)
        {
            throw new NotImplementedException();
        }
    }

相關文章