[ASP.NET MVC] 利用自定义的AuthenticationFilter实现Basic认证
很多情况下目标Action方法都要求在一个安全上下文中被执行,这里所谓的安全上下文主要指的是当前请求者是一个经过授权的用户。授权的本质就是让用户在他许可的权限范围内做他能够做的事情,授权的前提是请求者是一个经过认证的用户。质询-应答(Chanllenge-Response)”是用户认证采用的一种常用的形式,认证方向被认证方发出质询以要求其提供用于实施认证的用户凭证,而被认证方提供相应的凭证以作为对质询的应答。旨在目标Action方法执行之前实施身分认证的AuthenticationFilter也对这种认证方法提供了支持。 一、IAuthenticationFilter接口所有的AuthenticationFilter类型均实现了IAuthenticationFilter接口,该接口定义在命名空间“System.Web.Mvc.Filters”下(其他四种过滤器接口都定义在“System.Web.Mvc”命名空间下)。如下面的代码片断所示,OnAuthentication和OnAuthenticationChallenge这两个方法被定义在此接口中,前者用于对请求实施认证,后者则负责将相应的认证质询发送给请求者。 1: public interface IAuthenticationFilter 2: {
3: void OnAuthentication(AuthenticationContext filterContext); 4: void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext); 5: }
定义在IAuthenticationFilter接口的两个方法都将一个上下文对象作为其唯一参数。OnAuthentication方法的这个参数类型为AuthenticationContext,如下面的代码片断所示,它是ControllerContext的子类。AuthenticationContext的ActionDescriptor返回的自然是用于描述目标Action方法的ActionDescriptor对象。借助于Principal属性,我们可以获取或设置代表当前用户的Principal对象。如果我们在执行OnAuthentication方法的过程中设置了AuthenticationContext的Result属性,提供的ActionResult将直接用于响应当前请求。 2: { public ActionExecutingContext(ControllerContext controllerContext,ActionDescriptor actionDescriptor,IDictionary<string,object> actionParameters); 6: virtual ActionDescriptor ActionDescriptor { get; set; } 7: virtual IDictionary<object> ActionParameters { get; set; } 8: public ActionResult Result { get; set; } 9: }
OnAuthenticationChallenge方法的参数类型为AuthenticationChallengeContext。如下面的代码片断所示,它依然是ControllerContext的子类。它同样具有一个用于描述目标Action方法的ActionDescriptor属性,其Result属性代表的ActionResult对象将用于响应当前请求。 public ActionExecutedContext();
virtual ActionDescriptor ActionDescriptor { get; set; } virtual Exception Exception { get; set; } 10: public ActionResult Result { get; set; } 11: }
二、AuthenticationFilter的执行流程我们知道身份认证总是对请求处理的第一个步骤,因为只有确定了请求者的真实身份,安全才能得到保障,所以AuthenticationFilter是最先被执行的一类过滤器。所有过滤器的执行都是ActionInvoker来驱动的,ASP.NET MVC在默认情况下采用的ActionInvoker是一个AsyncControllerActionInvoker对象,后者类型派生于ControllerActionInvoker。ControllerActionInvoker针对AuthenticationFilter的执行体现在如下两个方法(InvokeAuthenticationFilters和InvokeAuthenticationFiltersChallenge)上。 //其他成员
5: virtual AuthenticationChallengeContext InvokeAuthenticationFiltersChallenge(ControllerContext controllerContext,ActionResult result);
1: [Authenticate] 3: { 5: { 7: Response.Write("HttpContext.User: {0}<br/>",1)">this.ControllerContext.HttpContext.User.Identity.Name));
9: } class AuthenticateAttribute:FilterAttribute,IAuthenticationFilter string WwwAuthenticationHeaderName ="WWW-Authenticate";
private static Dictionary<string> userAccounters; static AuthenticateAttribute() 10: userAccounters = new Dictionary<string>(StringComparer.OrdinalIgnoreCase); 12: userAccounters.Add("Foo",1)">"Password"); 13: userAccounters.Add("Bar",1)'> 14: userAccounters.Add("Baz",1)'> 15: } 16:?
17: void OnAuthentication(AuthenticationContext filterContext) 18: {
19: IPrincipal user;
20: if (this.IsAuthenticated(filterContext,1)">out user)) 21: {
22: filterContext.Principal = user;
23: }
24: else 25: {
26: this.ProcessUnauthenticatedRequest(filterContext); 27: }
28: }
29:?
30: virtual AuthenticationHeaderValue GetAuthenticationHeaderValue(AuthenticationContext filterContext) 31: {
32: string rawValue = filterContext.RequestContext.HttpContext.Request.Headers[AuthorizationHeaderName]; 33: string.IsNullOrEmpty(rawValue)) 34: {
35: return null; 36: }
37: string[] split = rawValue.Split(' '); 38: if (split.Length != 2) 39: {
40: 41: } 42: new AuthenticationHeaderValue(split[0],split[1]); 43: }
44:?
45: bool IsAuthenticated(AuthenticationContext filterContext,1)">out IPrincipal user) 46: {
47: user = filterContext.Principal;
48: null != user & user.Identity.IsAuthenticated) 49: {
50: true; 51: }
52:?
53: AuthenticationHeaderValue token = this.GetAuthenticationHeaderValue(filterContext); 54: null != token && token.Scheme == BasicAuthenticationScheme) 55: {
56: string credential = Encoding.Default.GetString(Convert.FromBase64String(token.Parameter)); 57: string[] split = credential.Split(':'); 58: if (split.Length == 2) 59: {
60: string userName = split[0]; 61: string password; 62: if (userAccounters.TryGetValue(userName,1)">out password)) 63: {
64: if (password == split[1]) 65: {
66: GenericIdentity identity = new GenericIdentity(userName); 67: user = new GenericPrincipal(identity,1)">new string[0]); 68: 69: } 70: }
71: }
72: }
73: false; 74: }
75:?
76: void ProcessUnauthenticatedRequest(AuthenticationContext filterContext) 77: {
78: string parameter = "realm="{0}"",filterContext.RequestContext.HttpContext.Request.Url.DnsSafeHost); 79: AuthenticationHeaderValue challenge = new AuthenticationHeaderValue(BasicAuthenticationScheme,parameter); 80: filterContext.HttpContext.Response.Headers[WwwAuthenticationHeaderName] = challenge.ToString();
81: filterContext.Result = new HttpUnauthorizedResult(); 82: }
83:?
84: void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) {} 85: }
在对请求实施认证的IsAuthenticated方法中,我们会试图从请求的Authorization报头中提取安全凭证,并按照Basic凭证的格式解析出用户名和密码。只有在用户名和密码匹配的情况下,我们认为请求通过认证,并根据解析出来的用户名创建一个GenericPrincipal对象作为输出参数user的值。如果请求并为通过认证(它可以是一个匿名请求,或者提供的用户名与密码不匹配),方法ProcessUnauthenticatedRequest会被调用。在此情况下,它会对响应的WWW-Authenticate报头进行相应的设置,并创建一个HttpUnauthorizedResult对象作为AuthenticationContext对象的Result属性,那么客户端最终会接收到一个状态为“401, Unauthorized”的响应。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – 使用城堡温莎的实体框架
- Asp.net mvc 知多少(九)
- asp.net – Web部署项目构建不再属于项目的文件
- asp.net-mvc – 从Asp.Net Mvc应用程序找不到DataContractJ
- iis-7 – 如何在IIS经典模式池下托管ASP.NET 5(vNext)MVC 6
- asp.net – Visual Studio 2005:是否有一个简单的方法在AS
- asp.net-mvc – ASP.NET MVC中的静态文件路由
- asp.net – 如何将其他页面包含到aspx文件中
- asp.net-mvc – 在nopcommerce 2.8中使用Telerik插件的自定
- asp.net-core – dnx和dnu没有在Ubuntu 15.10上运行