所有的web系统,只要是有用户登录这一块有权限这个的需求的就要处理用户登录状态保存这个问题,如果没有登录,那么跳转到登录页面让用户登录。
在webform中,一般是通过让页面继承System.Web.UI.Page,重写它的OnInit()方法,在OnInit()中判断Session中是否有用户登录的信息;页面也有PageLoad()方法,但是在页面的生命周期中,Init方法先执行,所以,截获OnInit,重写父类的init方法最准确;
public class BasePage : System.Web.UI.Page { protected override void OnInit(EventArgs e) { base.OnInit(e); if (Session["UserInfo"] == null) //如果用户没有登录 { //跳转到登录页面 } } }
在MVC中,MVC下可以自定义特性类为控制器或控制器中的Action打上[特性],这里只需要ActionFilter过滤器(Action方法执行前后执行),微软已提供好的ActionFilterAttribute类,他是筛选器特性的基类,也是一个抽象类,其实这个抽象类实现了IActionFilter和IResultFilter,
IActionFilter接口的定义了两个方法:
//在执行操作方法后调用。 void OnActionExecuted(ActionExecutedContext filterContext); // 在执行操作方法之前调用。 void OnActionExecuting(ActionExecutingContext filterContext);
我们如果也要做验证控制,那么我们首先新建一个自己需求的特性类,让他继承ActionFilterAttribute,并重写其中的OnActionExecuting方法,在其中完成校验:
比如public class LoginCheckFilterAttribute : ActionFilterAttribute { //表示是否检查登录 public bool IsCheck { get; set; } //Action方法执行之前执行此方法 public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); if (IsCheck) { //校验用户是否已经登录 if (filterContext.HttpContext.Session["UserInfo "] == null) { //跳转到登陆页 filterContext.HttpContext.Response.Redirect("/admin/login"); } } } }
然后在全局中注册这个过滤器。
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new LoginCheckFilterAttribute() { IsCheck = true }); } }
这样,你就可以在你的controller类中去使用这个filter特性。如果你在你的action前加上了这个过滤器,那么在执行这个Action方法之前会先调用特性类中的重写OnActionExecuting方法,这样用户在访问网站的时候会首先检测用户是否已经登录,如果没有登录会跳转到登录页面。
但是这样做,你会很费劲,你每个action都要弄一遍。
对于客户端的发来的请求,响应都由路由交给了controller控制器,同webform方式一样,也可以采取同样的方式,让我们的controller类继承自System.Web.Mvc.Controller;
System.Web.Mvc.Controller这个类实现了IActionFilter这个接口,那么我们就可以自己来覆盖OnActionExecuting方法,在这个我们自己定义的controller基类中做好这些登录验证;
形如代码:protected override void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } var reurl = filterContext.HttpContext.Request.Url == null ? "#" : filterContext.HttpContext.Request.Url.PathAndQuery; if (!filterContext.HttpContext.Request.IsAuthenticated) { // filterContext.Result = RedirectToAction("index", "login", new { ReturnUrl = reurl }); } else { if (null != Session["UserInfo "]) { ViewBag.UserName = Session["UserInfo "].Name; ViewBag.CurrentUser = Session["UserInfo "]; if (filterContext.HttpContext.Request.UrlReferrer != null) { ViewBag.Reurl = filterContext.HttpContext.Request.UrlReferrer.PathAndQuery; } } else { filterContext.Result = RedirectToAction("index", "login", new { ReturnUrl = reurl }); return; } } }
这里要注意的是,这里不能使用
Response.Redirect("/Login/index");
这种方式来调整到登录页面。这会导致:
“无法在发送 HTTP 标头之后进行重定向。”这种错误;
请使用:
filterContext.Result = RedirectToAction("index","login", new { ReturnUrl = reurl });
还有可能会碰到这个问题就是浏览器报错误:
“此网页包含重定向循环”
这是由于你的疏忽造成的,你想想,当你没有登录时,检测到session没有,为空,所以定向到了登录,登录的action被捕获,本应该到登录页面,但是OnActionExecuting要先执行,于是进入死循环了。这个死循环的解脱也简单,因为登录页面根本就不需要验证登录,所以我们可以建立个新的controller基类,里面不去覆盖OnActionExecuting方法,不做登录验证跳转这个步骤,这样的话就可以逃出这个死循环;这也是符合设计需求的;