ASP.NET Core 中的身份认证及鉴权

目录

知识准备

理解身份认证

身份认证(Authentication )

 鉴权(Authorization)

 IAuthorizeData

 Identity

认证功能配置

ConfigureServices

 AuthenticationScheme

Identity项目示例

创建项目

 配置认证与鉴权

配置服务

 启用中间件

 运行测试

总结

Identity

添加Identity功能

配置数据库连接

IdentityHostingStartup

配置RazorPage及静态文件

Startup.cs完整内容

生成数据库

生成迁移代码

功能测试

自定义UI


知识准备

ASP.NET Core 使用了不同传统Web程序的实现方式,在读本文之前,需要先了解ASP.NET Core的运作方式。主要是将传统Web中的模块(IHttpModule)和处理器(IHttpHandler)合并成了中间件(Middleware),以下ASP.NET Core 中间件的官方解释,以及如何进行代码迁移,不了解的小伙伴可以点开学习一下,这里不再重复。
ASP.NET Core Middleware | Microsoft Docs

Migrate HTTP handlers and modules to ASP.NET Core middleware | Microsoft Docs

Identity使用的RazorPage的实现,有关RazorPage请参考:

Introduction to Razor Pages in ASP.NET Core | Microsoft Docs


理解身份认证

ASP.NET Core 自带的安全管理分为两部分,一个是身份认证(Authentication ),另一个是鉴权(Authorization)。身份认证(Authentication )用于用户的登录登出,鉴权(Authorization)用于权限控制。这两个都是以相应的中间件(Middleware)来表示的。

身份认证(Authentication )

对于用户登录ASP.NET Core提供了多种认证方式,不过没找到具体的文档说明,这里参照源码总结了一下,大致分成四大类:

  1. 表单认证
  2. JWT认证
  3. 远程第三方认证
  4. 使用系统认证

这四类认证方式都实现了IAuthenticationHandler接口,但实际使用中并不直接在代码中操作这些具体的实现,可能是这个原因,微软的文档中并没有具体提及,AuthenticationHandler类结构图如下:

 鉴权(Authorization)

同样鉴权也有四种实现,分别是:

  1. 基于条件断言的,比如级别达到多少的可以访问。
  2. 防匿名的,比如某些功能需要登录才能使用。
  3. 基于用户的,比如某些指定的用户才可以使用。
  4. 基于角色的,比如某一类型的用户。

 IAuthorizeData

IAuthorizeData表明了具体资源的权限特征,实际项目中使用AuthorizeAttribute来表示。在鉴权的时候主要用到两个属性:Policy和Roles,这两个属性确定最终使用哪个鉴权方式。比如两个属性都不指定就是防匿名方式,指定Policy就是条件断言的方式。

比如在类申明中指定角色:

[Authorize(Roles = "Administrator")]
public class AdministrationController : Controller
{
}

 Identity

Identity是基于表单认证的一套用户管理组件,包括用户登录(含第三方登录)、用户管理的定义和操作接口,以及相应的用户界面。Identity通常使用EntityFramkeworkCore作为持久层,将用户数据保存到关系数据库中。


认证功能配置

ConfigureServices

虽然认证中间件(AuthenticationMiddleware)是ASP.NET Core 内置的,但因为有多种认证方式,所以在应用启动时要作相应的配置。比如表单验证时的登录页面、登录出链接、保存凭据的Cookie名等。好在ASP.NET Core 给每种认证方式都提供了相应的扩展方法,可以轻松地进行配置。比如添加表单验证,登录页为/Login

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options=> {
                options.LoginPath = "/Login";
            });
            services.AddControllersWithViews();
        }

 AuthenticationScheme

AuthenticationScheme是个字符串,跟每一种验证方式一一对应。不过,AuthenticationScheme跟Middleware一样,都没有使用接口进行约束,而是都遵守一同一个约定,每个验证组件中提供一个AuthenticationScheme 的常量,比如提供:

   public static class FacebookDefaults
    {
        /// <summary>
        /// The default scheme for Facebook authentication. The value is <c>Facebook</c>.
        /// </summary>
        public const string AuthenticationScheme = "Facebook";

        /// <summary>
        /// The default display name for Facebook authentication. Defaults to <c>Facebook</c>.
        /// </summary>
        public static readonly string DisplayName = "Facebook";

        /// <summary>
        /// The default endpoint used to perform Facebook authentication.
        /// </summary>
        /// <remarks>
        /// For more details about this endpoint, see https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login.
        /// </remarks>
        public static readonly string AuthorizationEndpoint = "https://www.facebook.com/v8.0/dialog/oauth";

        /// <summary>
        /// The OAuth endpoint used to retrieve access tokens.
        /// </summary>
        public static readonly string TokenEndpoint = "https://graph.facebook.com/v8.0/oauth/access_token";

        /// <summary>
        /// The Facebook Graph API endpoint that is used to gather additional user information.
        /// </summary>
        public static readonly string UserInformationEndpoint = "https://graph.facebook.com/v8.0/me";
    }

 AuthenticationScheme通常是由相应的验证组件提供的扩展方法中自动调用的,但在多认证系统方式的系统中,需要指定通过AuthenticationScheme指定默认的认证系统。比如前边的ConfigService方法中,指定表单认证为默认认证方式。


Identity项目示例

创建项目

为了便于理解,我们从最简单的项目开始。我们在Visual Studio里创建一个空白Web项目,运行环境选择3.1,如图所示:

接下来,我们增加两条EndPoint,一条用于模拟登录,另一条模拟管理:

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });

//新增两条终结点

                endpoints.MapGet("/Account/Login", async context =>
                {
                    await context.Response.WriteAsync("Login");
                });
                
                endpoints.MapGet("/Manage", async context =>
                {
                    await context.Response.WriteAsync("Manage");
                });
            });

 配置认证与鉴权

配置服务

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
            services.AddAuthorization();
        }

 启用中间件

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //……

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                //……
                //使用RequireAuthorization()方法,对manage请求启用默认鉴权
                endpoints.MapGet("/Manage", async context =>
                {
                    await context.Response.WriteAsync("Manage");
                }).RequireAuthorization();
            });
        }

 运行测试

等Hello World出现后,在浏览器地址栏里输入:https://localhost:44362/manane,就会跳到模拟的登录页:

总结

此时我们已经让 ASP.NET Core 身份认证及鉴权生效,并开始工作。当然这里并没有实现登录等功能,如果要实现完整功能还需要写很多代码,不过这并不是我们目的,到此只要知道怎么工作的就行了。


Identity

添加Identity功能

前边我们演示了一个最简单的认证鉴权功能。接下来我们通过增加Identity来实现完整的功能,为了省事,我们使用Visual Studio来添加:

 弹出框中选择标识,点击添加:

 添加一个下文和一个用户类,点击添加:

 添加成功后,会自动添加相关依赖包,及需要的项目文件:

配置数据库连接

默认使用的Sql Server的驱动,且自动更新了配置,我们将连接字符串改成下边的样子:

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

IdentityHostingStartup

加完标识后会自动生成一个IdentityHostingStartup.cs的文件,里边配置了数据库上下文及Identity,并通过assembly标注附加到启动项:

 [assembly: HostingStartup(typeof(WebApplication2.Areas.Identity.IdentityHostingStartup))]
namespace WebApplication2.Areas.Identity
{
    public class IdentityHostingStartup : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices((context, services) => {
                services.AddDbContext<WebApplication2Context>(options =>
                    options.UseSqlServer(
                        context.Configuration.GetConnectionString("WebApplication2ContextConnection")));

                services.AddDefaultIdentity<WebApplication2User>(options => options.SignIn.RequireConfirmedAccount = true)
                    .AddEntityFrameworkStores<WebApplication2Context>();
            });
        }
    }
}

配置RazorPage及静态文件

Startup里添加RazorPage中间件:

            services.AddRazorPages();

 使用静态文件:

app.UseStaticFiles();

添加映射:

endpoints.MapRazorPages();

Startup.cs完整内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using WebApplication2.Areas.Identity.Data;
using WebApplication2.Data;

namespace WebApplication2
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });

                endpoints.MapGet("/Account/Login", async context =>
                {
                    await context.Response.WriteAsync("Login");
                });

                endpoints.MapGet("/Manage", async context =>
                {
                    await context.Response.WriteAsync("Manage");
                }).RequireAuthorization();

                endpoints.MapRazorPages();
            });
        }
    }
}

生成数据库

到目前为止程序能运行了,只是还不能登录,因为数据库还没创建。下边开始一步步添加数据库、

生成迁移代码

打开软件包控制台,输入以下命令:

Add-Migration Identity_Init

生成数据库:

Update-Database

功能测试

为了方便起见,先关闭密码策略

    public class IdentityHostingStartup : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices((context, services) =>
            {
                services.AddDbContext<WebApplication2Context>(options =>
                    options.UseSqlServer(
                        context.Configuration.GetConnectionString("WebApplication2ContextConnection")));

                services.AddDefaultIdentity<WebApplication2User>(options =>
                {
                    options.SignIn.RequireConfirmedAccount = false;
                    options.Password.RequireDigit = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireUppercase = false;
                    options.Password.RequiredLength = 6;
 
                    
                })
                    .AddEntityFrameworkStores<WebApplication2Context>();
            });
        }
    }

编译,在VS中起动,打开下边的链接,注册一个账号:

https://localhost:44359/Identity/Account/Register

因为没有限制,注册完就可以使用了。打开下边的链接,就可以修改自己的信息了。

https://localhost:44359/identity/account/manage

自定义UI

由于自带的UI并没有提供语言包,所以如果换成中文,需要使用自己的UI,我们可以重新添加标识,生成所需要的页面,在添加界面上勾选替代所有文件。

添加完成后,这些文件就全部在解决方案里了,我们尝试改其中的一个文件,比如Identity/Account/Manage/Index.cshtml.cs,给UserName属性加上Display属性,修改Input中的PhoneNumber的Display:

然后编译运行,查看效果:

至此Identity功能就算完成了。

  • 2
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值