1.前言

  a.微软对ASP.NET的开发从WebForm到MVC的转变,已经正式过去5,6个年头,现在WebForm和MVC也都越来越完善,小小算来我也已经工作了将近三年,从大学的时候学习ASP.NET WebForm,感觉这就是我们以后吃饭的技术,所以当时可劲的学习拖各种控件,学习做各种各样的小项目,但是没想到的是,从大学最后一学期参加实习开始到现在也就一直没有机会接触ASP.NET WebForm,基本也都是在用MVC开发。我并不是说MVC就比WebForm开发好或者什么的,我只是觉得他们都是工具,只要我们使用任何一种工具能够顺手,能给我们带来更快速的开发和收获,我们就可以去使用它。

  b.那么MVC也同时给我们带来了不一样的开发体验,它增加了很多的开发技巧和技术,比如:路由机制(Web Form也已加入),Razor引擎,特性(Attribute)等,随着Visual Studio2015的发布,ASP.NET MVC6的跨平台也已经来到,那么说了这么多,我们今天主要的还是简单说一下MVC的特性,我将这些经常用到的技术都给整理起来,等到我们后面有机会用到的时候直接将其拿出来使用即可。

  c.源码(Git)地址:https://github.com/kencery/Common/blob/master/KenceryCommonMethod/AuthorizeAttribute/LoginAttribute.cs

2.AuthorizeAttribute登录特性解释

  a.在项目开发中,通常我们都会涉及到用户登录才能访问的网页,比如购物网站,我们浏览商品,添加购物车(以前开发的时候在这里就需要登录用户,但是现在有了缓存的实现,这里可以将商品加入缓存,等到结账的时候在登录)选择结账的时候需要登录,那么这时候我们需要跳转到登录页面登录,登录之后还可以回到记录下来的原始的页面,那么这之后我们有好几种方法可以实现这种效果,简单举例:

    (1):第一种:登录模块不管怎么样都是统一的,就是在每个需要登录的方法里面判断用户是否登录,如果没有登录,则跳转登录,这种的缺点是工作量大,代码冗余。

    (2):第二种:使用MVC的特性,定义类继承IAuthorizationFilter,重写OnAuthorization方法即可实现。此方法工作量少,代码不冗余,如果需要登录我们只需要给Controller或者Action给上标签即可。

  b.任何能够使用特性的判断都可以按照下面的思路来实现,例如(登录判断,权限判断,请求判断,去除空格,读取返回路径)等等,在项目中又遇到这些方方面面需要在请求发起之前或者请求发起之后访问的,都可以实现特性去实现。

  c.下面是我简单整理的的a中说的这种需要的特性实现,代码示例如下,请查看:

3.代码示例

  a.下面我简单说一下这个实现的思路,如果大家有什么意见可以在底下留言,纯属于整理,没多少技术含量,请大家见谅,如果代码不能实现你们的需求,请按照你们的需求更改即可实现。

    (1).首先定义类继承自FilterAttribute, IAuthorizationFilter,会让你实现重写OnAuthorization方法。

    (2).从web.Config中存放三个常量(登录地址,登录Session或Cookie键,Session或者Cookie登录的判断),在构造函数中读取Web.Config中的信息,实现构造函数。

    (3).重写OnAuthorization方法,判断是Cookie或者Session记录用户登录信息,然后判断Session或者Cookie是否为空,如果为空,则登录至指定的地址,否则退出即可。

    (4).使用,如果Controller或者Action需要判断是否登录,则打上特性[Login],否则不需要,如果在某一个Controller中,大部分方法需要,只有一两个不需要,在Controller中打上特性[Login],在不需要的方法上打上特性[AllowAnonymous]即可实现。

  b.上面说了这么多,基本都是一些思路,实现代码示例如下,请查看:

复制代码
  1 // 源文件头信息:
  2 // <copyright file="LoginAttribute.cs">
  3 // Copyright(c)2014-2034 Kencery.All rights reserved.
  4 // 个人博客:http://www.cnblogs.com/hanyinglong
  5 // 创建人:韩迎龙(kencery)
  6 // 创建时间:2015-8-7
  7 // </copyright>
  8 
  9 using System;
 10 using System.Web;
 11 using System.Web.Mvc;
 12 using System.Web.UI.WebControls;
 13 
 14 namespace KenceryCommonMethod
 15 {
 16     /// <summary>
 17     /// 添加此特性的功能是:需要用户登录才能够浏览网页,如果不需要用户登录,则可以使用AllowAnonymousAttribute属性
 18     /// 使用:给ASP.NET MVC中的控制器类名或者Action方法上面打上[Login]标签
 19     /// 如果不需要验证用户是否登录就可以浏览,则给ASP.NET MVC中的控制器类名或者Action方法上面打上[AllowAnonymous]标签
 20     /// </summary>
 21     /// <auther>
 22     ///     <name>Kencery</name>
 23     ///     <date>2015-8-7</date>
 24     /// </auther>
 25     [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
 26     public class LoginAttribute : FilterAttribute, IAuthorizationFilter
 27     {
 28         /// <summary>
 29         /// 该值表示登录地址:默认路径为user/login,在Web.Config中设置
 30         /// </summary>
 31         private string _authUrl = string.Empty;
 32 
 33         public string AuthUrl
 34         {
 35             get
 36             {
 37                 return _authUrl.Trim();
 38             }
 39             set
 40             {
 41                 if (string.IsNullOrEmpty(value))
 42                 {
 43                     throw new ArgumentNullException("AuthUrl为空,验证用户登录信息的用户登录地址不能为空");
 44                 }
 45                 _authUrl = value.Trim();
 46             }
 47         }
 48 
 49         /// <summary>
 50         /// 该值表示yoghurt登录保存登录信息的键名,默认user,在web.Config中设置
 51         /// </summary>
 52         private string _authSaveKey = string.Empty;
 53 
 54         public string AuthSaveKey
 55         {
 56             get { return _authSaveKey.Trim(); }
 57             set
 58             {
 59                 if (string.IsNullOrEmpty(value))
 60                 {
 61                     throw new ArgumentNullException("AuthSaveKey为空,验证用户登录的时候保存的登录键信息不能为空");
 62                 }
 63                 _authSaveKey = value.Trim();
 64             }
 65         }
 66 
 67         /// <summary>
 68         /// 该值表示用户登录保存登录信息的类型,默认Session,在web.Config中设置
 69         /// </summary>
 70         private string _authSaveType = string.Empty;
 71 
 72         public string AuthSaveType
 73         {
 74             get { return _authSaveType.Trim().ToUpper(); }
 75             set
 76             {
 77                 if (string.IsNullOrEmpty(value))
 78                 {
 79                     throw new ArgumentNullException("AuthSaveType为空,验证用户登录的时候未保存键信息类型,类型不能为空");
 80                 }
 81                 _authSaveType = value.Trim().ToUpper();
 82             }
 83         }
 84 
 85         /// <summary>
 86         /// 默认构造函数
 87         /// </summary>
 88         public LoginAttribute()
 89         {
 90             const string authUrl = "/User/Index"; //登录页面路径,从Web.Config中读取
 91             const string saveKey = "user"; //验证(登录存放用户信息),从Web.Config中读取
 92             const string saveType = "Session"; //存放类型判断(Session或者Cookie存放),,从Web.Config中读取
 93             _authUrl = string.IsNullOrEmpty(authUrl) ? "/User/Index" : authUrl;
 94 
 95         }
 96 
 97         /// <summary>
 98         /// 构造函数重载
 99         /// </summary>
100         /// <param name="authUrl"></param>
101         public LoginAttribute(string authUrl) : this()
102         {
103             _authUrl = authUrl;
104         }
105 
106         /// <summary>
107         /// 构造函数重载
108         /// </summary>
109         /// <param name="authUrl"></param>
110         /// <param name="saveKey"></param>
111         public LoginAttribute(string authUrl, string saveKey) : this(authUrl)
112         {
113             _authSaveKey = saveKey;
114             _authSaveType = "Session";
115         }
116 
117         /// <summary>
118         /// 构造函数重载
119         /// </summary>
120         /// <param name="authUrl"></param>
121         /// <param name="saveKey"></param>
122         /// <param name="saveType"></param>
123         public LoginAttribute(string authUrl, string saveKey, string saveType) : this(authUrl, saveKey)
124         {
125             _authSaveType = saveType;
126         }
127 
128         public void OnAuthorization(AuthorizationContext filterContext)
129         {
130             if (filterContext.HttpContext == null)
131             {
132                 throw new Exception("此特性只试用于Web应用程序试用");
133             }
134             var flAd = filterContext.ActionDescriptor;
135             var url = string.Format("{0}?Ref=/{1}/{2}", _authUrl, flAd.ControllerDescriptor.ControllerName,
136                 flAd.ActionName);
137             switch (AuthSaveType)
138             {
139                 case "SESSION":
140                     if (filterContext.HttpContext.Session == null)
141                     {
142                         throw new Exception("服务器Session不可用");
143                     }
144                     if (!filterContext.ActionDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true) &&
145                         !filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
146                             typeof (AllowAnonymousAttribute), true))
147                     {
148                         if (filterContext.HttpContext.Session[_authSaveKey] == null)
149                         {
150                             filterContext.Result = new RedirectResult(url);
151                         }
152                     }
153                     break;
154                 case "COOKIE":
155                     if (!filterContext.ActionDescriptor.IsDefined(typeof (AllowAnonymousAttribute), true) &&
156                         !filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
157                             typeof (AllowAnonymousAttribute), true))
158                     {
159                         if (filterContext.HttpContext.Request.Cookies[_authSaveKey] == null)
160                         {
161                             filterContext.Result = new RedirectResult(url);
162                         }
163                     }
164                     break;
165                 default:
166                     throw new ArgumentNullException("用户保存登录信息的方法不能为空,只能为Cookie和Session,请您检查");
167             }
168         }
169     }
170 }
复制代码