ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程

简介: 原文:ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 登录登出 上一章节我们总算完善了注册的功能,而且也添加了一个用户,现在,我们是时候继续完善登录登出功能了。

原文:ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程

ASP.NET Core 登录登出

上一章节我们总算完善了注册的功能,而且也添加了一个用户,现在,我们是时候继续完善登录登出功能了。

本章节应该是我们这个序列的最后一章节,因为本章节接收,我们大概的基础的 ASP.NET Core 知识都讲解了一遍。虽然很肤浅,但总算日常使用的知识点都有所涉及

本章节,我们将学习登录和注销功能。 与登录相比,注销相当简单。

我们首先重新布局下视图,在首页中去掉登录和注册链接,而改成在 _Layout.cshtml 布局视图中添加这些链接,如果用户已经登录,则隐藏登录和注册,而显示用户名和注销按钮

我们首先来看看之前的 _Layout.cshtml 模板的内容

<!DOCTYPE html>
<html>
<head> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> </head> <body> <div>@DateTime.Now</div> <div> @RenderBody() </div> </body> </html> 

删除 @DateTime.Now 然后改成如下内容

@using System.Security.Claims
<!DOCTYPE html>
<html>
<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <style> body { margin: 10px auto; text-align: center } table { margin: 0 auto; width: 90% } table, th, td { border: 1px solid #eee; border-collapse: collapse; border-spacing: 0; padding: 5px; text-align: center } .txt-left { text-align: left; } </style> </head> <body> <div> @if ( User.Identity.IsAuthenticated ) { <div>@User.Identity.Name</div> <form method="post" asp-controller="Account" asp-action="Logout"> <input type="submit" value="登出" /> </form> } else { <a asp-controller="Account" asp-action="Login">登录</a> <a asp-controller="Account" asp-action="Signup">注册</a> } </div> <div>@RenderBody()</div> </body> </html> 

上面这段代码,又多了很多新面孔

  1. 为了判断用户是否已经登录,我们必须使用 User.Identity.IsAuthenticated 来判断,如果返回 true 则说明已经登录,否则表示未登录

  2. 为了显示当前登录的用户名,我们使用了 User.Identity.Name 表达式

  3. 为了在当前视图中使用 User 对象,我们必须引入命名空间 System.Security.Claims

  4. 同时,我们也把 Index.cshtml 中的样式移到了当前视图

接着修改 Index.cshtml ,去掉注册链接,去掉样式,修改完成后代码显示如下

@model HomePageViewModel
@{
    ViewBag.Title = "职工列表";
}
<h1>职工列表</h1> <table> <tr> <td>ID</td> <td>姓名</td> <td class="txt-left">操作</td> </tr> @foreach (var employee in Model.Employees) { <tr> <td>@employee.ID</td> <td>@employee.Name</td> <td class="txt-left"><a asp-action="Detail" asp-route-Id="@employee.ID">详情</a> &nbsp; <a asp-controller="Home" asp-action="Edit" asp-route-id="@employee.ID">编辑</a></td> </tr> } </table> <br/> <p><a asp-action="Create" >新增员工</a></p> 

保存所有代码,刷新浏览器,访问首页,显示如下

因为之前注册的时候我们已经登录过了,所以这时候就会显示登录状态

注销功能

如果点击 登出 则会报错,原因是我们还没实现该功能呢

修改 AccountController 类添加动作方法 Logout

[HttpPost] 
public async Task<IActionResult> Logout() { await _signManager.SignOutAsync(); return RedirectToAction("Index", "Home"); } 

这个注销操作也是有很多的名堂可讲了

  1. 因为在 _Layout.cshtml 的登出操作使用的是 HTTP POST 方法。所以这里添加了 [HttpPost] 特性

  2. 注销操作需要与 Identity 框架进行交互,告诉 Identity 框架注销当前用户。这个操作通过调用 _signManager.SignOutAsync 方法来完成,因为这是一个异步方法,所以需要使用 await 来修饰等待

  3. 因为使用了异步方法,所以整个方法也是异步的,那么返回结果也就需要是异步结果,使用 async Task<IActionResult> 来修饰

  4. Identity 注销完成后,当前用户就变成了匿名用户,而匿名用户能访问的只有首页,所以就跳回了首页

保存所有代码,重启应用程序,点击 登出 显示如下

登录功能

  1. 登出成功后,我们可以继续点击 登录 链接来登录,因为还没有实现该功能,所以会出现 404 错误

  2. 从前面可知,登录页面的网址为 /Account/Login,为了实现登录功能,我们需要添加一对动作方法 Login ,一个用于响应 HTTP GET 请求显示登录表单,另一个用户响应 HTTP POST 请求完成登录操作

    [HttpGet] 
    public IActionResult Login() {  
       return View(); 
    }
    
    [HttpPost] 
    public IActionResult Login() {  
       return View(); 
    }
    
  3. 为了能够记住显示登录之前的页面,我们需要保存来路,这一点,ASP.NET Core MVC 早就已经想好了,只要给我们的方法添加上 returnUrl 即可

    [HttpGet] 
    public IActionResult Login(string returnUrl = "" ) {  
       return View(); 
    }
    
    [HttpPost] 
    public IActionResult Login(string returnUrl = "") {  
       return View(); 
    }
    
  4. 因为注册的时候只输入了用户名和密码,所以登录的时候也就只有用户名和密码了,所以我们需要创建一个模型,用来接收登录提交的用户数据

    public class LoginViewModel { 
       public string Username { get; set; }
    
       [DataType(DataType.Password)] 
       public string Password { get; set; }
    
       [Display(Name ="Remember Me")] 
       public bool RememberMe { get; set; } 
       public string ReturnUrl { get; set; }
    
    }
    

    RegisterViewModel 不同的是,少了 ConfirmPassword 而多了一个 RememberMeReturnUrl 用户保存是否记住密码选项和登录来路

    其实这里有多了一个新的特性,就是 Display ,这个特性用于填充登录表单的 <label> 的名字

  5. 接下来我们就可以修改显示表单的 Login 方法将 LoginViewModel 实例传给视图

    [HttpGet] 
    public IActionResult Login(string returnUrl = "") { 
       var model = new LoginViewModel { ReturnUrl = returnUrl }; 
       return View(model); 
    }
    
  6. 然后在 Views/Account 目录下新建一个 Login.cshtml 登录表单视图,内容如下

    @model LoginViewModel 
    @{ 
       ViewBag.Title = "Login"; 
    }  
    <h2>登录</h2>
    
    <form method="post" asp-controller="Account" asp-action="Login" asp-route-returnurl = "@Model.ReturnUrl"> <div asp-validation-summary="ModelOnly"></div> <div> <label asp-for="Username"></label> <input asp-for="Username" /> <span asp-validation-for="Username"></span> </div> <div> <label asp-for="Password"></label> <input asp-for="Password" /> <span asp-validation-for="Password"></span> </div> <div> <label asp-for="RememberMe"></label> <input asp-for="RememberMe" /> <span asp-validation-for="RememberMe"></span> </div> <input type = "submit" value="登录" /> </form> 

    代码很普通,很多知识我们之前已经接触过了,不过唯一的不同的就是 asp-route-returnurl = "@Model.ReturnUrl"

    asp-route-returnurl 属性会把当前值附在提交的 URL 后面,键名为 returnurl,值为指定的值

  7. 接下来我们实现登录动作,因为 LoginViewModel 已经包含了 ReturnUrl ,所以我们可以把 returnurl 参数删掉

    [HttpPost] 
    public async Task<IActionResult> Login(LoginViewModel model) { 
       if (ModelState.IsValid) { 
          var result = await _signManager.PasswordSignInAsync(model.Username,
             model.Password, model.RememberMe,false);
    
          if (result.Succeeded) { 
             if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl)) {
                return Redirect(model.ReturnUrl);
             } else { 
                return RedirectToAction("Index", "Home"); 
             } 
          } 
       }
    
        if ( string.IsNullOrEmpty(model.ReturnUrl) )
        {
            model.ReturnUrl = "";
        }
    
       ModelState.AddModelError("","Invalid login attempt"); 
       return View(model); 
    }
    
    1. 因为 LoginViewModel 已经包含了 ReturnUrl ,所以我们可以把 returnurl 参数删掉

    2. 检查 ModelState 是否有效。如果它有效,则通过调用 SignInManager 上的 PasswordSignInAsync 来登录用户

      该方法接受四个参数,前三个分别为用户名、密码、和是否记住我三个参数

    3. 如果验证成功且成功登录,则重定向到 returnurl

    4. 如果失败,则显示失败信息给用户

修改完成后,AccountController.cs 的完整代码如下

using System;
using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using System.ComponentModel.DataAnnotations; using HelloWorld.Models; namespace HelloWorld.Controllers { public class AccountController : Controller { private SignInManager<User> _signManager; private UserManager<User> _userManager; public AccountController(UserManager<User> userManager, SignInManager<User> signManager) { _userManager = userManager; _signManager = signManager; }  [HttpGet] public ViewResult Signup() { return View(); }  [HttpPost] public async Task<IActionResult> Signup(RegisterViewModel model) { if (ModelState.IsValid) { var user = new User { UserName = model.Username }; var result = await _userManager.CreateAsync(user, model.Password); if (result.Succeeded) { await _signManager.SignInAsync(user, false); return RedirectToAction("Index", "Home"); } else { foreach (var error in result.Errors) { ModelState.AddModelError("", error.Description); } } } return View(); }  [HttpPost] public async Task<IActionResult> Logout() { await _signManager.SignOutAsync(); return RedirectToAction("Index", "Home"); }  [HttpGet] public IActionResult Login(string returnUrl = "") { var model = new LoginViewModel { ReturnUrl = returnUrl }; return View(model); }  [HttpPost] public async Task<IActionResult> Login(LoginViewModel model) { if (ModelState.IsValid) { var result = await _signManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, false); if (result.Succeeded) { if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl)) { return Redirect(model.ReturnUrl); } else { return RedirectToAction("Index", "Home"); } } } ModelState.AddModelError("", "Invalid login attempt"); if ( string.IsNullOrEmpty(model.ReturnUrl) ) { model.ReturnUrl = ""; } return View(model); } } public class RegisterViewModel {  [Required, MaxLength(64)] public string Username { get; set; }  [Required, DataType(DataType.Password)] public string Password { get; set; }  [DataType(DataType.Password), Compare(nameof(Password))] public string ConfirmPassword { get; set; } } public class LoginViewModel { public string Username { get; set; }  [DataType(DataType.Password)] public string Password { get; set; }  [Display(Name = "Remember Me")] public bool RememberMe { get; set; } public string ReturnUrl { get; set; } } } 

重启我们的应用程序,刷新我们的浏览器,访问首页,显示如下

点击 登录 并输入正确的用户名和密码

然后点击 登录 按钮,成功登录后会跳回首页

我们继续点击 登出 ,登出成功后会跳回首页

此时点击 李白 的详情,则会跳转到登录页面,并且 returnurl 会被复制为 /Home/Detail/1

然后点击 登录 按钮,成功登录后会跳转到李白的详情页

目录
相关文章
|
4月前
|
开发框架 前端开发 JavaScript
盘点72个ASP.NET Core源码Net爱好者不容错过
盘点72个ASP.NET Core源码Net爱好者不容错过
75 0
|
4月前
|
开发框架 .NET
ASP.NET Core NET7 增加session的方法
ASP.NET Core NET7 增加session的方法
38 0
|
13天前
|
Go 开发者
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第24天】Go语言的`net/http`包在HTTP客户端编程中扮演重要角色,但使用时需注意几个常见问题:1) 检查HTTP状态码以确保请求成功;2) 记得关闭响应体以防止资源泄漏;3) 设置超时限制,避免长时间等待;4) 根据需求处理重定向。理解这些细节能提升HTTP客户端编程的效率和质量。
18 1
|
2月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
32 0
|
4月前
|
SQL 开发框架 JavaScript
分享33个ASP.NET电子商务源码和40个ASP.NET控件组件源码,总有一款适合您
分享33个ASP.NET电子商务源码和40个ASP.NET控件组件源码,总有一款适合您
32 0
|
4月前
|
开发框架 .NET Java
ASP.NET Core高级编程--C#基本特性(一)
本文章简略介绍C#的部分特性
|
5月前
|
存储 开发框架 .NET
Asp.net就业课之Ado.net第一次课
Asp.net就业课之Ado.net第一次课
21 0
|
5月前
|
人工智能 机器人 C#
Windows编程课设(C#)——基于WPF和.net的即时通讯系统(仿微信)
一款参考QQ、微信的即时通讯软件。采用CS结构,客户端基于.Net与WPF开发,服务端使用Java开发。
|
7月前
|
开发框架 .NET 数据库
asp.net企业费用报销管理信息系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
asp.net 企业费用报销管理信息系统是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使 用c#语言开发 应用技术:asp.net c#+sqlserver 开发工具:vs2010 +sqlserver
56 0
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
46 0