antdesignpro 重定向到登录页面_如何在ASP.NET Core应用中实现认证、登录和注销?一个实例告诉你

在安全领域,认证和授权是两个重要的主题。认证是安全体系的第一道屏障,是守护整个应用或者服务的第一道大门。当访问者请求进入的时候,认证体系通过验证对方的提供凭证确定其真实身份。认证体系只有在证实了访问者的真实身份的情况下才会允许其进入。ASP.NET Core提供了多种认证方式,它们的实现都基于相同的认证模型。本篇文章提供了一个极简的实例让读者体验如何在ASP.NET Core应用中实现认证、登录和注销。

一、认证票据

认证是一个旨在确定请求访问者真实身份的过程,与认证相关的还有其他两个基本操作——登录与注销。要真正理解认证、登录与注销这3个核心操作的本质,就需要对ASP.NET Core采用的基于“票据”的认证机制有基本的了解。ASP.NET Core应用的认证实现在一个名为AuthenticationMiddleware的中间件中,该中间件在处理分发给它的请求时会按照指定的认证方案(Authentication Scheme)从请求中提取能够验证用户真实身份的数据,我们一般将该数据称为安全令牌(Security Token)。ASP.NET Core应用下的安全令牌被称为认证票据(Authentication Ticket),所以ASP.NET Core应用采用基于票据的认证方式。

AuthenticationMiddleware中间件实现的整个认证流程涉及下图所示的3种针对认证票据的操作,即认证票据的颁发、检验和撤销。我们将这3个操作所涉及的3种角色称为票据颁发者(Ticket Issuer)、验证者(Authenticator)和撤销者(Ticket Revoker),在大部分场景下这3种角色由同一个主体来扮演。

60043b7dbe0d4779a046e1461481730f

颁发认证票据的过程就是登录(Sign In)操作。一般来说,用户试图通过登录应用以获取认证票据的时候需要提供可用来证明自身身份的用户凭证(User Credential),最常见的用户凭证类型是“用户名 + 密码”。认证方在确定对方真实身份之后,会颁发一个认证票据,该票据携带着与该用户相关的身份、权限及其他相关的信息。

一旦拥有了由认证方颁发的认证票据,我们就可以按照双方协商的方式(如通过Cookie或者报头)在请求中携带该认证票据,并以此票据声明的身份执行目标操作或者访问目标资源。认证票据一般都具有时效性,一旦过期将变得无效。我们有的时候甚至希望在过期之前就让认证票据无效,以免别人使用它冒用自己的身份与应用进行交互,这就是注销(Sign Out)操作。

ASP.NET Core应用的认证系统旨在构建一个标准的模型来完成针对请求的认证以及与之相关的登录和注销操作。接下来我们就通过一个简单的实例来演示如何在一个ASP.NET Core应用中实现认证、登录和注销的功能。

二、基于Cookie的认证

我们会采用ASP.NET Core提供的基于Cookie的认证方案。顾名思义,该认证方案采用Cookie来携带认证票据。为了使读者对基于认证的编程模式有深刻的理解,我们演示的这个应用将从一个空白的ASP.NET Core应用开始搭建。

我们即将创建的这个ASP.NET Core应用主要处理3种类型的请求。应用的主页需要登录之后才能访问,所以针对主页的匿名请求会被重定向到登录页面。在登录页面输入正确的用户名和密码之后,应用会自动重定向到应用主页,该页面会显示当前认证用户名并提供注销的链接。我们按照如下所示的方式利用路由来处理这3种类型的请求,其中登录和注销采用的是默认路径“Account/Login”与“Account/Logout”。

public class Program{    public static void Main()    {        Host.CreateDefaultBuilder()            .ConfigureWebHostDefaults(builder => builder                .ConfigureServices(svcs => svcs.AddRouting())                 .Configure(app => app                    .UseRouting()                    .UseEndpoints(endpoints =>{                        endpoints.Map(pattern: "/", RenderHomePageAsync);                        endpoints.Map("Account/Login", SignInAsync);                        endpoints.Map("Account/Logout", SignOutAsync);                        })))                               .Build()            .Run();    }    public static async Task RenderHomePageAsync(HttpContext context)    {        throw new NotImplementedException();    }    public static async Task SignInAsync(HttpContext context)    {        throw new NotImplementedException();    }    public static async Task SignOutAsync(HttpContext context)    {        throw new NotImplementedException();    }}

三、应用主页

如下面的代码片段所示,我们调用IApplicationBuilder接口的UseAuthentication扩展方法就是为了注册用来实现认证的AuthenticationMiddleware中间件。该中间件的依赖服务是通过调用IServiceCollection接口的AddAuthentication扩展方法注册的。在注册这些基础服务时,我们还设置了默认采用的认证方案,静态类型CookieAuthenticationDefaults的AuthenticationScheme属性返回的就是Cookie认证方案的默认方案名称。

public class Program{    public static void Main()    {        Host.CreateDefaultBuilder()            .ConfigureWebHostDefaults(builder => builder                .ConfigureServices(svcs => svcs                    .AddRouting()                    .AddAuthentication(options => options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme)                        .AddCookie())                .Configure(app => app                    .UseAuthentication()                    .UseRouting()                    .UseEndpoints(endpoints =>{                        endpoints.Map(pattern: "/", RenderHomePageAsync);                        endpoints.Map("Account/Login", SignInAsync);                        endpoints.Map("Account/Logout", SignOutAsync);                        })))                               .Build()            .Run();    }}

ASP.NET Core提供了一个极具扩展性的认证模型,我们可以利用它支持多种认证方案,针对认证方案的注册是通过AddAuthentication方法返回的一个AuthenticationBuilder对象来实现的。在上面提供的代码片段中,我们调用AuthenticationBuilder对象的AddCookie扩展方法完成了针对Cookie认证方案的注册。

演示实例的主页是通过如下所示的RenderHomePageAsync方法来呈现的。由于我们要求浏览主页必须是经过认证的用户,所以该方法会利用HttpContext上下文的User属性返回的ClaimsPrincipal对象判断当前请求是否经过认证。对于经过认证的请求,我们会响应一个简单的HTML文档,并在其中显示用户名和一个注销链接。

public class Program{    ...    public static async Task RenderHomePageAsync(HttpContext context)    {        if (context?.User?.Identity?.IsAuthenticated == true)        {            await context.Response.WriteAsync(                @"                    Index                    " +                        $"

Welcome {context.User.Identity.Name}

" + @"Sign Out "); } else { await context.ChallengeAsync(); } }}

对于匿名请求,我们希望应用能够自动重定向到登录路径。从如上所示的代码片段可以看出,我们仅仅调用当前HttpContext上下文的ChallengeAsync扩展方法就完成了针对登录路径的重定向。前面提及,注册的登录和注销路径是基于Cookie的认证方案采用的默认路径,所以调用ChallengeAsync方法时根本不需要指定重定向路径。下图所示就是作为应用的主页在浏览器上呈现的效果。

d929c01800fd4a6e8b7f2a5702ab12de

四、登录

登录与注销分别实现在SignInAsync方法和SignOutAsync方法中,我们采用的是针对“用户名 + 密码”的登录方式,所以可以利用静态字段_accounts来存储应用注册的账号。在静态构造函数中,我们添加密码均为“password”的3个账号(Foo、Bar和Baz)。

public class Program{    private static Dictionary _accounts;    static Program()    {        _accounts = new Dictionary(StringComparer.OrdinalIgnoreCase);        _accounts.Add("Foo", "password");        _accounts.Add("Bar", "password");        _accounts.Add("Baz", "password");    }}

如下所示的代码片段是用于处理登录请求的SignInAsync方法的定义,而RenderLoginPageAsync方法用来呈现登录页面。如下面的代码片段所示,对于GET请求,SignInAsync方法会直接调用RenderLoginPageAsync方法来呈现登录界面。对于POST请求,我们会从提交的表单中提取用户名和密码,并对其实施验证。如果提供的用户名与密码一致,我们会根据用户名创建一个代表身份的GenericIdentity对象,并利用它创建一个代表登录用户的ClaimsPrincipal对象,RenderHomePageAsync方法正是利用该对象来检验当前用户是否经过认证的。有了ClaimsPrincipal对象,我们只需要将它作为参数调用HttpContext上下文的SignInAsync扩展方法即可完成登录,该方法最终会自动重定向到初始方法的路径,也就是我们的主页。

public class Program{    public static async Task SignInAsync(HttpContext context)    {        if (string.Compare(context.Request.Method, "GET") == 0)        {                         await RenderLoginPageAsync(context, null, null, null);        }        else        {            var userName = context.Request.Form["username"];            var password = context.Request.Form["password"];            if (_accounts.TryGetValue(userName, out var pwd) && pwd == password)            {                var identity = new GenericIdentity(userName, "Passord");                var principal = new ClaimsPrincipal(identity);                await context.SignInAsync(principal);            }            else            {                await RenderLoginPageAsync(context, userName, password, "Invalid user name or password!");            }        }    }    private static Task RenderLoginPageAsync(HttpContext context, string userName,  string password, string errorMessage)    {        context.Response.ContentType = "text/html";        return context.Response.WriteAsync(            @"                Login" +                            $"" +                            $" " +                            @"" +                    $"

{errorMessage}

" + @" "); }}

如果用户提供的用户名与密码不匹配,我们还是会调用RenderLoginPageAsync方法来呈现登录页面,该页面会以下图所示的形式保留用户的输入并显示错误消息。图19-3还反映了一个细节,调用HttpContext上下文的ChallengeAsync方法会将当前路径(主页路径“/”,经过编码后为“%2F”)存储在一个名为ReturnUrl的查询字符串中,SignInAsync方法正是利用它实现对初始路径的重定向的。

五、注销

既然登录可以通过调用当前HttpContext上下文的SignInAsync扩展方法来完成,那么注销操作对应的自然就是SignOutAsync扩展方法。如下面的代码片段所示,我们定义在Program中的SignOutAsync扩展方法正是调用这个方法来注销当前登录状态的。我们在完成注销之后将应用重定向到主页。

public class Program{        ...    public static async Task SignOutAsync(HttpContext context)    {        await context.SignOutAsync();        context.Response.Redirect("/");       }}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,你需要打开 Visual Studio 并创建一个新的 ASP.NET Core Web 应用程序项目。在创建项目时,你可以选择 Web 应用程序模板和 ASP.NET Core 版本。 接下来,你需要添加一个登录页面。你可以在项目创建一个新的视图文件夹,然后在其创建一个名为 Login.cshtml 的视图文件。 在 Login.cshtml 文件,你可以使用 HTML 和 Razor 语法来创建一个基本的登录表单。例如,以下是一个简单的示例: ```html @page @model LoginModel <form method="post"> <div> <label for="username">Username:</label> <input type="text" id="username" name="username" /> </div> <div> <label for="password">Password:</label> <input type="password" id="password" name="password" /> </div> <div> <button type="submit">Login</button> </div> </form> ``` 在上面的示例,我们创建了一个包含用户名和密码字段的表单,并添加了一个提交按钮。 接下来,你需要创建一个 LoginModel 类来处理表单提交。你可以在项目创建一个名为 Login.cshtml.cs 的代码文件,并在其定义 LoginModel 类。以下是一个简单的示例: ```csharp public class LoginModel : PageModel { [BindProperty] public string Username { get; set; } [BindProperty] public string Password { get; set; } public void OnPost() { if (Username == "admin" && Password == "password") { // 登录成功,重定向到主 Response.Redirect("/"); } else { // 登录失败,显示错误消息 ModelState.AddModelError("", "Invalid username or password."); } } } ``` 在上面的示例,我们定义了一个 LoginModel 类,并添加了两个属性来表示用户名和密码。在 OnPost 方法,我们检查用户名和密码是否正确,如果正确,我们重定向到主,否则我们添加一个错误消息到 ModelState 对象,让它显示在面上。 最后,你需要在 Startup.cs 文件配置路由,以便用户可以访问登录页面。以下是一个简单的示例: ```csharp public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } ``` 在上面的示例,我们使用 MapControllerRoute 方法来配置路由,以便用户可以访问名为 Login 的控制器和 Login 方法。例如,如果你的项目在本地运行,你可以访问 http://localhost:5000/Login 来访问登录页面。 这是一个基本的 ASP.NET Core Web 应用程序登录页面的创建过程。你可以根据你的需求来添加更多的功能和验证步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值