ASP.NET CORE 使用 JWT 进行令牌身份验证和授权(不使用 Cookie)– 第二部分






4.80/5 (22投票s)
本文介绍了如何在 ASP.NET CORE 中使用 JWT 实现令牌身份验证和授权。
目录
- 引言
- 用户角色
- 如何创建自定义授权特性?
- 如何在控制器和操作级别设置权限?
- 检查用户权限的扩展方法
- 如何在操作方法中检查权限(内联代码)?
- 如何在视图页面中控制用户权限?
- 如何处理未经授权的请求?
- 如何处理和身份验证/授权 Ajax 调用?
- LoginDemo.sln 项目
- 第三部分
引言
第一部分介绍了如何使用登录凭据对用户进行身份验证。在第二部分中,我们将介绍如何为用户实现授权。换句话说,授予用户使用应用程序的某些部分或全部部分的权限。用户权限(授权)在 3 个级别处理:控制器、操作方法和视图页面。为此,我们将编写自定义特性和一些扩展方法。第二部分包含一个独立的 LoginDemo.sln 项目,其中实现了下面所有关于授权的主题。
用户角色
让我们设置三种不同的角色
总监
主管
分析师
创建一个 static
类 Roles.cs 来定义用户角色。这些是参数值,将传递到自定义特性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LoginDemo.CustomAttributes
{
public static class Roles
{
public const string DIRECTOR = "DIRECTOR";
public const string SUPERVISOR = "SUPERVISOR";
public const string ANALYST = "ANALYST";
}
}
让我们向 User 集合添加“WRITE”权限。
//Using hard coded collection list as Data Store for demo purpose.
//In reality, User data comes from Database or some other Data Source - JRozario
private List UserList = new List
{
new User { USERID = "jsmith@email.com", PASSWORD = "test",
EMAILID = "jsmith@email.com", FIRST_NAME = "John",
LAST_NAME = "Smith", PHONE = "356-735-2748",
ACCESS_LEVEL = Roles.DIRECTOR.ToString(), WRITE_ACCESS = "WRITE_ACCESS" },
new User { USERID = "srob@email.com", PASSWORD = "test",
FIRST_NAME = "Steve", LAST_NAME = "Rob",
EMAILID = "srob@email.com", PHONE = "567-479-8537",
ACCESS_LEVEL = Roles.SUPERVISOR.ToString(), WRITE_ACCESS = "WRITE_ACCESS" },
new User { USERID = "dwill@email.com", PASSWORD = "test",
FIRST_NAME = "DJ", LAST_NAME = "Will",
EMAILID = "dwill@email.com", PHONE = "599-306-6010",
ACCESS_LEVEL = Roles.ANALYST.ToString(), WRITE_ACCESS = "WRITE_ACCESS" },
new User { USERID = "JBlack@email.com", PASSWORD = "test",
FIRST_NAME = "Joe", LAST_NAME = "Black",
EMAILID = "JBlack@email.com", PHONE = "764-460-8610",
ACCESS_LEVEL = Roles.ANALYST.ToString(), WRITE_ACCESS = "" }
};
如何创建自定义授权特性?
用户权限通过特性进行处理,这些特性可用于装饰控制器和操作方法。当一个特性被装饰在控制器顶部时,它将应用于该控制器中的所有操作方法。同样,每个操作方法也可以被装饰。
为了创建自定义授权特性,我们将使用 IAuthorizationFilter
接口和 TypeFilterAttribute
。IAuthorizationFilter
通过 OnAuthorization
方法实现实际的授权过滤。TypeFilterAttribute
用于依赖注入。更多信息,请参阅 Microsoft 文档。
让我们编写自定义授权特性
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace LoginDemo.CustomAttributes
{
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
{
Arguments = new object[] { claim };
}
}
public class AuthorizeFilter : IAuthorizationFilter
{
readonly string[] _claim;
public AuthorizeFilter(params string[] claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
var claimsIndentity = context.HttpContext.User.Identity as ClaimsIdentity;
if (IsAuthenticated)
{
bool flagClaim = false;
foreach (var item in _claim)
{
if (context.HttpContext.User.HasClaim(item, item))
flagClaim = true;
}
if (!flagClaim)
context.Result = new RedirectResult("~/Dashboard/NoPermission");
}
else
{
context.Result = new RedirectResult("~/Home/Index");
}
return;
}
}
}
如您所见,上面的代码中有两个类:AuthorizeAttribute
和 AuthorizeFilter
。
AuthorizeAttribute
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
{
Arguments = new object[] { claim };
}
}
此类实现了 TypeFilterAttribute
,它由依赖项容器用于创建 AuthorizeFilter
的对象。AuthorizeAttribute
构造函数接受 string
数组作为参数。由于它是一个数组,我们可以传递用逗号(,)分隔的角色值 [例如:“Director
”、“Supervisor
”、“Analyst
”]。
AuthorizeFilter
public class AuthorizeFilter : IAuthorizationFilter
AuthorizeFilter
类实现了 IAuthroizationFilter
接口,其中包含 OnAuthorization
方法。
public AuthorizeFilter(params string[] claim)
{
_claim = claim;
}
AuthorizeFilter
构造函数接受 string
数组作为参数,该参数从 AuthorizeAttribute
传递。
public void OnAuthorization(AuthorizationFilterContext context)
OnAuthorization
方法接收 AuthorizationFilterContext
。从这个“context”对象,我们可以获取 HttpContext
和包含声明的用户身份。
var IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
var claimsIndentity = context.HttpContext.User.Identity as ClaimsIdentity;
从“context
”对象,我们可以得知用户是否已进行身份验证。使用同一个“context
”对象,我们可以获取用户声明的身份集合。
if (IsAuthenticated)
{
bool flagClaim = false;
foreach (var item in _claim)
{
if (context.HttpContext.User.HasClaim(item, item))
flagClaim = true;
}
if (!flagClaim)
context.Result = new RedirectResult("~/Dashboard/NoPermission");
}
else
{
context.Result = new RedirectResult("~/Home/Index");
}
return;
在此块中,我们检查用户是否已进行身份验证。如果没有,我们将用户重定向到登录页面。然后,我们循环遍历“claim”数组集合(这个数组集合将是用户角色,我们会将其作为参数从控制器传递,例如:Authorize[new string[] “Director”, “Supervisor”]
)。
在这里,context.HttpContext.User.Identity
对象在声明对象集合中包含用户权限。在第一部分中,我们创建了具有用户声明的令牌并将其加载到 context.HttpContext.User
身份对象中。请参阅第一部分中的“Middleware app.UseAuthentication()
”。
在循环内部,我们使用“HasClaim
”方法检查参数值是否在用户身份声明集合中。在第一部分中,我们解释了如何创建用户声明身份对象集合并将其分配给 HttpContext
。正是从同一个 HttpContext
中,我们获取用户声明并与传入的参数值进行比较。
如果参数值在声明集合中找到,则用户已授权,允许通过 HTTP 请求。否则,我们将用户重定向到一个显示“您无权访问”的页面。在本文中,重定向到“无权限”页面。
如何在控制器和操作方法级别设置权限?
既然我们已经创建了自定义授权特性,现在是时候在控制器中使用它了。
public class DashboardController : Controller
{
[Authorize(Roles.DIRECTOR)]
public IActionResult DirectorPage()
{
return View("DirectorPage");
}
[Authorize(Roles.SUPERVISOR)]
public IActionResult SupervisorPage()
{
ViewBag.Message = "Permission controlled through action Attribute";
return View("SupervisorPage");
}
[Authorize(Roles.ANALYST)]
public IActionResult AnalystPage()
{
return View("AnalystPage");
}
}
操作方法使用 [Authorize]
特性进行装饰。我们正在传递用户角色作为参数。Director
、Supervisor
和 Analyst
各自拥有独立的操作方法。
[Authorize(Roles.DIRECTOR)]
public IActionResult DirectorPage()
{
return View("DirectorPage");
}
在此“DirectorPage
”操作方法中,我们设置了 Authorize
特性,值为 Roles.DIRECTOR
。这意味着只有拥有 Director
角色的用户才能查看该页面。如果 Supervisor
或 Analyst
尝试访问该页面,他们将从 AuthorizeFilter
类被重定向到“无权限
”页面。
Roles.DIRECTOR
是我们传递给 AuthorizeFilter
类的参数。Authorize
过滤器类接收“Roles.DIRECTOR
”参数值,并检查该值是否在用户声明身份集合中找到。如果找到,则用户被允许通过操作方法,并允许查看“DirectorPage
”。如果登录用户是 Supervisor 或 Analyst,则他们无法看到“DirectorPage
”。
如果我们希望允许多个用户访问操作方法,则可以像下面这样使用逗号(,)分隔的值来传递参数值。
[Authorize(Roles.DIRECTOR, Roles.SUPERVISOR, Roles.ANALYST)]
public IActionResult AllRoles()
{
return View();
}
要在控制器级别设置权限,可以像下面这样设置。当 Authorize
特性设置在控制器级别时,该权限将应用于该控制器中的所有操作方法。
namespace LoginDemo.Controllers
{
[Authorize(Roles.DIRECTOR, Roles.SUPERVISOR, Roles.ANALYST)]
public class YourController : Controller
{
public IActionResult Action1()
{
return View();
}
public IActionResult Action2()
{
return View();
}
public IActionResult Action3()
{
return View();
}
}
}
检查用户权限的扩展方法
过滤器特性只能在控制器中使用。但在某些情况下,我们需要检查用户权限,而不能仅依赖于过滤器特性。如果我们想在操作方法内的“If
”条件中检查用户权限怎么办?如果我们想在视图页面中检查用户权限怎么办?在这种情况下,我们不能使用过滤器特性。因此,我们将使用扩展方法。一个用于控制器操作方法,一个用于视图页面。
让我们创建一个 static
类 PermissionExtension.cs 来为控制器提供扩展方法。
namespace LoginDemo.CustomAttributes
{
public static class PermissionExtension
{
public static bool HavePermission(this Controller c, string claimValue)
{
var user = c.HttpContext.User as ClaimsPrincipal;
bool havePer = user.HasClaim(claimValue, claimValue);
return havePer;
}
public static bool HavePermission(this IIdentity claims, string claimValue)
{
var userClaims = claims as ClaimsIdentity;
bool havePer = userClaims.HasClaim(claimValue, claimValue);
return havePer;
}
}
}
为方便控制器和视图页面使用而实现的扩展方法。这些方法以声明值为参数,并检查该声明是否存在于 HttpContext.User
声明对象集合中。要在控制器中调用该方法,我们使用“this Controller”作为第一个参数,使其成为一个扩展方法。对于视图页面的使用,它是“this IIdentity
”。
这些扩展方法是为了方便并将事物集中管理。您也可以直接在操作方法和视图页面中使用“User.HasClaims()
”。
如何在操作方法中检查权限(内联代码)?
假设您有一个操作方法,并希望将其用于多个角色。但您想根据角色执行不同的逻辑或从不同的源获取数据,或者做一些特定的事情。在这种情况下,我们需要在操作方法中了解当前登录的用户的角色。
在下面的操作方法中,所有登录用户都有权限。我们在 action
方法中检查用户角色。在 action
方法内部,我希望根据角色返回不同的视图。对于“Supervisor
”,它返回“SupervisorPage
”视图;对于“Analyst
”,它返回“AnalystPage
”视图。这仅为例,它可用于实现不同的逻辑、从不同的源获取数据等。
这是一个简单的 if
条件,带有扩展方法 HavePermission()
,用于控制权限。
public IActionResult SupervisorAnalystPage()
{
ViewBag.Message = "Permission controlled inside action method";
if (this.HavePermission(Roles.SUPERVISOR))
return View("SupervisorPage");
if (this.HavePermission(Roles.ANALYST))
return View("AnalystPage");
return new RedirectResult("~/Dashboard/NoPermission");
}
上面,我们创建了 HavePermission()
扩展方法。使用该扩展方法,我们通过简单的“if
”条件来控制权限。扩展方法检查 HttpContext
用户声明对象集合中的用户角色。
this.HavePermission(Roles.SUPERVISOR)
this.HavePermission(Roles.ANALYST))
如何在视图页面中控制用户权限?
如果我们想根据用户角色/权限在视图页面中控制某些内容怎么办?如何为 Director、Supervisor 和 Analyst 显示和隐藏不同的顶部“菜单”项?为此,我们需要在视图页面级别了解用户权限。我们希望实现类似这样的功能:如果用户是 Director,则显示/隐藏一个菜单、按钮等。
假设我们想根据用户角色/权限禁用和启用“保存”按钮。这可以通过上面的 HavePermission()
扩展方法来完成。在此示例中,对于没有 WRITE 访问权限的用户,按钮被禁用。在此视图页面中,有一个表单可以接收用户输入。只有具有 WRITE 访问权限的用户才能保存此表单。对于其他人,“保存”按钮被禁用,他们无法保存表单数据。
@if (User.Identity.HavePermission("WRITE_ACCESS") == true)
{
<button type="button"</button<>
}
else
{
<button type="button" disabled="disabled"<</button>
}
上面,我们创建了两个 HavePermission()
扩展方法,一个用于控制器,一个用于视图页面。在这里,HavePermission()
检查 HttpContext
用户声明对象集合中的“WRITE_ACCESS
”声明。如果为 true
,则启用保存按钮,否则禁用。这样,我们可以控制用户视图页面内容的“显示什么?”和“如何显示?”基于他们的角色/权限。
@if (User.Identity.HavePermission("WRITE_ACCESS") == true)
以 Analyst JBlack@email.com(没有 WRITE 权限)登录演示项目。在登陆页面,点击“查看页面级别权限如何工作”。
您可以看到“保存”按钮被禁用,用户无法保存表单数据。
对于其他用户,他们拥有 WRITE 权限,“保存”按钮已启用,可以保存表单数据。
如何处理未经授权的请求?
如何限制未登录应用程序但直接尝试通过提供页面 URL 来访问页面的用户?假设应用程序 URL 是 http://www.yourapplication.com,它将用户带到登录页面。但是,有一个“Supervisor”页面,用户知道该页面的 URL。如果“Supervisor”页面的 URL 是 http://www.yourapplication.com/supervisor,并且用户在未登录的情况下直接在浏览器中输入该 URL,则属于未经授权的访问。让我们看看如何处理这种情况。
我们将实现一个过滤器特性(类似于 AuthorizeFilter
)。但我们将它用于控制器级别。创建一个类 UnAuthorized.cs。实现 IAuthorizationFilter
接口并在 OnAuthroization()
方法中处理请求。我们无需为此过滤器传递任何参数值。此过滤器仅检查身份验证。
namespace LoginDemo.CustomAttributes
{
public class UnAuthorizedAttribute : TypeFilterAttribute
{
public UnAuthorizedAttribute() : base(typeof(UnauthorizedFilter))
{
//Empty constructor
}
}
public class UnauthorizedFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
bool IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
if (!IsAuthenticated)
{
context.Result = new RedirectResult("~/Home/Index");
}
}
}
}
从 AuthorizationFilterContext
获取用户身份对象,从中我们可以发现用户是否已进行身份验证。
bool IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
如果用户未进行身份验证或未登录,则将用户重定向到登录页面。
if (!IsAuthenticated)
{
context.Result = new RedirectResult("~/Home/Index");
}
您可以通过简单地将 [UnAuthorized]
特性添加到顶部来将其用于控制器级别,这将应用于该控制器中的所有操作方法。Authorize
特性也检查 IsAuthenticated
,所以有人可能会问 [UnAuthorized]
特性的用途是什么。可能存在一种情况,权限未受控制,这意味着所有用户都可以拥有操作方法的权限,并且该操作方法没有 [Authorize]
特性。在下面的控制器中,SupervisorAnalystPage()
操作方法没有 [Authorize]
特性。任何知道 URL 的用户都可以尝试访问它。使用 [UnAuthorized]
特性的另一个原因是为了处理 Ajax 调用,我们将在下一个主题中看到。
namespace LoginDemo.Controllers
{
[UnAuthorized]
public class DashboardController : Controller
{
[Authorize(Roles.DIRECTOR)]
public IActionResult DirectorPage()
{
return View("DirectorPage");
}
[Authorize(Roles.SUPERVISOR)]
public IActionResult SupervisorPage()
{
ViewBag.Message = "Permission controlled through action Attribute";
return View("SupervisorPage");
}
[Authorize(Roles.ANALYST)]
public IActionResult AnalystPage()
{
return View("AnalystPage");
}
public IActionResult SupervisorAnalystPage()
{
ViewBag.Message = "Permission controlled inside action method";
if (this.HavePermission(Roles.SUPERVISOR))
return View("SupervisorPage");
if (this.HavePermission(Roles.ANALYST))
return View("AnalystPage");
return new RedirectResult("~/Dashboard/NoPermission");
}
}
}
如何处理和身份验证/授权 Ajax 调用?
在我们实现代码之前,让我们先看看“为什么?”、“在哪里?”和“如何?”执行此操作。
用户可以使应用程序处于空闲状态直到会话过期。当会话过期时,令牌将不可用,用户将从应用程序注销。但页面仍然在浏览器中打开。用户不知道会话已过期且已注销,他们可能会点击页面上的任何内容。如果该点击是常规的 HTTP 调用(页面会重新加载或其他操作),则用户将自动重定向到登录页面。如果该点击碰巧是 Ajax 调用,那么页面将保持不变,并且不会发生重定向到登录页面的情况。
附注:Ajax 调用也是 HTTP 调用。常规 HTTP 调用和 Ajax 调用之间的区别在于它们的调用方式。大多数常规 HTTP 调用直接由浏览器执行。Ajax 调用是从代码(javascript/jquery)发出的,然后浏览器执行调用。对于常规 HTTP 调用,返回结果可以是视图页面或 Json 结果等。但对于 Ajax 调用,只能是 Json 结果。
- 用于识别 Ajax 调用的扩展方法
- 在
[UnAuthorized]
过滤器特性中处理 Ajax 调用身份验证 - 在
[Authorize]
过滤器特性中处理 Ajax 调用授权 - 在 jquery 中捕获 Ajax 调用的 HTTP 状态码并重定向到登录页面
用于识别 Ajax 调用的扩展方法
首先,我们需要知道一个请求是 Ajax 调用还是常规 HTTP 调用。我们可以通过 HTTP Headers 来确定。其次,我们将创建一个扩展方法,以便可以将其与 Http Request 对象一起使用。
创建一个 static
类 AjaxExtension.cs 和一个 static
方法“IsAjaxRequest
”。添加以下代码
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LoginDemo.CustomAttributes
{
public static class AjaxExtension
{
//HttpRequest Extension method to
//check if the incoming request is an AJAX call - JRozario
public static bool IsAjaxRequest(this HttpRequest request)
{
if (request == null)
throw new ArgumentNullException("request");
if (request.Headers != null)
return request.Headers["X-Requested-With"] == "XMLHttpRequest";
return false;
}
}
}
这是一个使用 HttpRequest
对象实现的简单扩展方法。我们可以通过 HTTP Headers 中的一个“X-Requested-With
”来判断一个请求是否为 Ajax 调用。如果此 Header 的值为“XMLHttpRequest
”,则它是 Ajax 调用。
return request.Headers["X-Requested-With"] == "XMLHttpRequest";
处理 [UnAuthorized] 过滤器特性中的 Ajax 调用身份验证
现在我们有了一个 IsAjaxRequest()
扩展方法来判断一个请求是否为 Ajax 调用。让我们使用扩展方法向 [UnAuthorized]
过滤器特性添加一个条件。
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
namespace LoginDemo.CustomAttributes
{
public class UnAuthorizedAttribute : TypeFilterAttribute
{
public UnAuthorizedAttribute() : base(typeof(UnauthorizedFilter))
{
//Empty constructor
}
}
public class UnauthorizedFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
bool IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
if (!IsAuthenticated)
{
if (context.HttpContext.Request.IsAjaxRequest())
{
context.HttpContext.Response.StatusCode =
(int)HttpStatusCode.Forbidden; //Set HTTP 403 Forbidden - JRozario
}
else
{
context.Result = new RedirectResult("~/Home/Index");
}
}
}
}
}
如您所见,条件 context.HttpContext.Request.IsAjaxRequest()
检查它是否为 Ajax 调用。如果它是 Ajax 调用,我们不会重定向调用。重定向对 Ajax 调用不起作用。Ajax 调用的返回结果只能是数据。在这里,我们将 HTTP 响应状态码设置为 403 - Forbidden。这表示 Ajax 调用遇到了错误,调用不成功。此演示中的所有 Ajax 调用都用 Jquery 编写。Jquery Ajax 调用可以从 HTTP 响应对象(Header)中获取 HTTP 状态码。如果它是常规 HTTP 调用,则重定向到登录页面。我们完成了对 Ajax 请求的身份验证。接下来,我们需要将用户重定向到登录页面。
在 [Authorize] 过滤器特性中处理 Ajax 调用授权
对于身份验证,我们将 HTTP 状态码设置为 403 Forbidden。对于授权,我们将设置 HTTP 状态码为 401 Unauthorized。逻辑与之前相同,我们使用扩展方法 IsAjaxRequest()
检查它是否为 Ajax 调用。如果是 Ajax 调用,则将 HTTP 状态码设置为 401 Unauthorized。如果是常规 HTTP 调用,则重定向到“无权限”页面。
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
namespace LoginDemo.CustomAttributes
{
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
{
Arguments = new object[] { claim };
}
}
public class AuthorizeFilter : IAuthorizationFilter
{
readonly string[] _claim;
public AuthorizeFilter(params string[] claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var IsAuthenticated =
context.HttpContext.User.Identity.IsAuthenticated;
var claimsIndentity =
context.HttpContext.User.Identity as ClaimsIdentity;
if (IsAuthenticated)
{
bool flagClaim = false;
foreach (var item in _claim)
{
if (context.HttpContext.User.HasClaim(item, item))
flagClaim = true;
}
if (!flagClaim)
{
if (context.HttpContext.Request.IsAjaxRequest())
context.HttpContext.Response.StatusCode =
(int)HttpStatusCode.Unauthorized; //Set HTTP 401
//Unauthorized - JRozario
else
context.Result =
new RedirectResult("~/Dashboard/NoPermission");
}
}
else
{
if (context.HttpContext.Request.IsAjaxRequest())
{
context.HttpContext.Response.StatusCode =
(int)HttpStatusCode.Forbidden; //Set HTTP 403 - JRozario
}
else
{
context.Result = new RedirectResult("~/Home/Index");
}
}
return;
}
}
}
在 Jquery 中捕获 Ajax 调用的 HTTP 状态码并重定向到登录页面
在这里,我们将从 [UnAuthorized]
过滤器特性中设置的 HTTP 状态码中获取信息。这应该在进行 Ajax 调用的地方完成。如前所述,在此演示中,我们使用 Jquery 进行 Ajax 调用。Jquery 提供了一个 .ajaxError
方法来捕获 Ajax 调用期间发生的错误。但是,我们是否要为每个 Ajax 调用编写 .ajaxError
方法?不,.ajaxError
方法可以全局实现,用于应用程序中的所有 Ajax 调用。唯一需要注意的是,.ajaxError
方法必须在该页面上可用。我们所要做的就是在通用页面 _Layout.cshtml(这是主页面)中添加它。将此脚本添加到您的 _Layout.cshtml 或您使用的任何主页面。
<script>
$(document).ajaxError(function (xhr, result) {
//Catch Ajax error with http status code 403 Forbidden
//and 401 Unauthorized – Jrozario
if (result.status === 403) {
window.location.href = '/Home/Index';
}
if (result.status === 401) {
window.location.href = '/Dashboard/NoPermission';
}
});
</script>
.ajaxError
在参数之一中获取 HTTP 标头,这里将其声明为“result
”。在 [UnAuthorized]
过滤器特性中设置的 HTTP 标头和状态码 403 Forbidden 在“result
”参数中返回。如果 result.status
为 403,则我们从客户端重定向用户到登录页面。
if (result.status === 403) {
window.location.href = '/Home/Index';
}
在演示项目中,有一个页面 CheckAjaxCalls.cshtml,您可以在其中检查 Ajax 调用的身份验证/授权。要检查身份验证,请以 Director jsmith@email.com 登录演示项目。您应该会看到下面的页面。
点击链接“查看身份验证和授权如何处理 Ajax 调用”。在此页面上,您可以同时检查身份验证和授权。
上面的屏幕有 3 个按钮:“结束会话”、“身份验证 Ajax 调用”和“授权 Ajax 调用”。所有 3 个按钮都有 JavaScript 函数(OnClick_EndSession
、OnClick_AuthenticateAjaxCall
、OnClick_AuthorizeAjaxCall
),它们会进行 Ajax 调用。下面是带有 HTML 标签的完整代码。
<script>
function OnClick_EndSession() {
$.ajax({
type: 'GET',
url: '/Home/EndSession',
data: {},
cache: false,
success: function (result) { }
});
alert("End of User Session,
Click on Ajax Call button to autneticate Ajax calls,
It should take you to login page.");
}
function OnClick_AuthenticateAjaxCall() {
$.ajax({
type: 'GET',
url: '/Dashboard/AuthenticateAjaxCalls',
data: {},
cache: false,
success: function (result) {
if (result != "")
alert("Your session is still active,
end session to see how authentication for Ajax call works!");
}
});
}
function OnClick_AuthorizeAjaxCall() {
$.ajax({
type: 'GET',
url: '/Dashboard/AuthorizeAjaxCalls',
data: {},
cache: false,
success: function (result) {
if (result != "")
alert("Your have permission for this Ajax call!");
}
});
}
</script>
下面是页面上 3 个 Ajax 调用的控制器操作方法,返回 Json 结果。此代码在演示项目的 DashboardController.cs 文件中。您可以看到“AuthorizeAjaxCalls
”操作方法为 Director
和 Supervisor
设置了 [Authorize]
特性。
//This action method is in Dashboard.cs
public JsonResult AuthenticateAjaxCalls()
{
return Json(new {result = "success" });
}
[Authorize(Roles.DIRECTOR, Roles.SUPERVISOR)]
public JsonResult AuthorizeAjaxCalls()
{
return Json(new { result = "success" });
}
//This action method is in HomeController.cs
public JsonResult EndSession()
{
HttpContext.Session.Clear();
return Json(new {result = "success"});
}
现在点击“身份验证 Ajax 调用”按钮。如果会话仍然有效,您应该会收到一个警告消息。
要结束会话,请点击“结束会话”,然后点击“身份验证 Ajax 调用”。您应该会被重定向到登录页面。
要检查授权,请以 Analyst JBlack@email.com(属于 Analyst 角色)登录演示项目。在此演示中,Ajax 调用仅针对 Director 和 Supervisor。Analyst 没有 Ajax 调用的权限,因此他们将被重定向到“无权限”页面。登录后,点击链接“查看身份验证和授权如何处理 Ajax 调用”。在下一个页面,点击“授权 Ajax 调用”。由于 JBlack@email.com 是 Analyst,他没有权限,将被重定向到“无权限”页面。
如果您以 Director 或 Supervisor 身份登录,那么在点击“授权 Ajax 调用”时,您会看到一个警告消息。
LoginDemo.sln 项目
完全可用的 LoginDemo.sln 项目可供下载。请参阅下面的屏幕截图,快速浏览 LoginDemo.sln 项目。
登录页面
登陆页面
顶部横幅显示所有页面的已登录用户名及其角色。点击提供的链接以查看不同的场景。
第三部分
在第三部分,我们将介绍如何进行忘记密码和重置密码。