ASP.NET MVC 登录验证

时间:2022-12-03 22:11:24

   好久没写随笔了,这段时间没 什么事情,领导 一直没安排任务,索性 一直在研究代码,说实在的,这个登录都 搞得我云里雾里的,所以这次我可能也讲得不是 特别清楚,但是 我尽力把我知道的讲出来,顺便也对自己最近的工作 做义工总结吧。

  我们的项目用到的一些 技术也跟大家提一下,主要是用了NHibernate,StructureMap,FluentHibernate等等,可能这篇文章我不会 全部 提到 ,不过相信后面,如果我要写文章的话,肯定是会提到的。我自己也模仿 了公司的框架做了一个 简单的登录(未完成)的,因为感觉公司的东西实在是很复杂,所以不一定可以完全消化,所以才 写了一上午没写完 = =

  首先,登录的话,我是写在一个Area里面,至于 怎么建立Area(区域)我就 不多累赘了,Area的名字我就 取成MS2Auth,因为 自己 喜欢MapleStory这个游戏,所以取了 它的 一个缩写 单词,以下是结构。

ASP.NET  MVC  登录验证

我们新建一个Controller名叫AccountController这个就是我们 登录用的主的控制器,然后再往Controller里面添加一些Action,比如我们添加一个LogOn的Action,代码如下:

        /// <summary>
/// 登录
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public ActionResult LogOn(MS2UserCheckTransferMsg message)
{
if (message.IsLogin)
{
return Json(new { Data=ShowpopupReminder(message)},JsonRequestBehavior.AllowGet);
} if (string.IsNullOrEmpty(message.Email))
{
ViewBag.ShowpopupReminder = ShowpopupReminder(message);
ViewBag.UserEmail = message.Email;
} var logOnModel = new LogOnModel(); if (Request.QueryString["GUID"] == "")
{
TempData["errorMessage"] = ErrorMessage;
}
else if (Request.QueryString["Reason"] == "NoCookie")
{
TempData["errorMessage"] = NoCookieErrMessage;
} if (User.Identity.IsAuthenticated)
{
return Redirect("~/LogOff.aspx");
} else
{
return View(logOnModel);
}
}

  大家 可能会先去关心MS2UserCheckTransferMsg这个类里面的东西。

    /// <summary>
/// 登陆检查
/// </summary>
public class MS2UserCheckTransferMsg:IMessage
{
public string Email { get; set; } public string password { get; set; } public bool IsLogin { get; set; } }

一个 是Email,一个 是密码,另一个 就是“是否登录”,是否登录 是检测 如果你访问到了这个ACTION的话,如果你 已经处于登录状态了,那么就会执行下面的流程。

这里面的一个 重点是 “反射”,通过反射 去获取某个类底下的方法。这个话 怎么 去理解 呢?首先大家主营到 下面的一个 方法。

        private bool ShowpopupReminder(MS2UserCheckTransferMsg message)
{
var queryBus = ObjectFactory.GetInstance<IQueryBus>();
return queryBus.Query<MS2UserCheckTransferMsg, dynamic>(message); }

  这里我们 通过 一个 工厂 方法去 得到IQueryBus的 一个 实例,然后 再用 这个实例反过来,去执行一个 Query的方法。

namespace YLW.MS2.Common.TransferMsg
{
public interface IQueryBus
{
TResponse Query<TRequest, TResponse>(TRequest message) where TResponse : class; }
}

  那么 ,这个IQueryBus其实是 一个 接口,我需要 得到这个接口的一个实例,该怎么 办呢?下面就提供了一种解决方法,大家想起来那个Global.asax文件了吗,那个不是 进行初始化的一个文件吗?对的,就是 这么一个文件,我们 需要 做的,就是 在Application_Start里面进行初始化,把这些 实例先一股脑的丢进去,进行工厂化处理,然后要的时候只要 指定实例名或者 类型的话,就可以 方便的取出来。

为此我们需要用到一个东西。那 就是StructureMap的For方法,来 注册这个接口的实例。

  

        public static void Init()
{
ObjectFactory.Configure(x =>
x.For<IQueryBus>().Use<LocalQueryBus>(););
}

  当然 ,这只是一个例子而已,具体的你 如果想注册其他的东西的话,也可以 用类似的方法去 写,大家 注意到 了没有 ,有 一个 叫做IMessage的接口,这个接口里面不 包含 任何东西,我的理解就是 这个接口的作用只是作为一个 中转而用的,另 一个接口 叫做IMessageHandler,这个 接口的话里面只有一个Execute方法。也就是下面要提到的,IMESSAGE只是 作为一个转换而已 ,也就是 说 任何 实现了IMessage接口的类,不需要实现任何多余的方法,也就是说,这个只是依赖注入的一个中转站而已(我个人的理解)。

  下面的方法就是通过反射区调用Execute方法,而这个Execuute方法真正 所在的类,其实是被IMessageHanlder所 实现的接口的一个 方法。

    public class LocalQueryBus:IQueryBus
{
public TResponse Query<TRequest, TResponse>(TRequest message) where TResponse : class
{
return (TResponse)QueryHandlerExecute((IMessage)message);
} private object QueryHandlerExecute(IMessage message)
{
var handlerType = typeof(IMessageHandler<>);
var genericType = handlerType.MakeGenericType(message.GetType()); var messageHandler = ObjectFactory.GetInstance(genericType); return messageHandler.GetType().GetMethod("Execute").Invoke(messageHandler, new object[] { message });
} }

下面 就说明了真正执行的方法的实际位置:

public class MS2UserCheckHandler: IMessageHandler<MS2UserCheckMessage>

  总之我感觉 这种 反射 跳来跳去的,我头都有点 花了,其中具体的用法具体去 考虑,比如 那个 MakeGenericType,大家可以自己 去 查查 用法 。

  由于有些 登录的机制特别的复杂,比如有你需要 一个“公共秘钥”去登录 ,就是说你 需要 返回 一个公共密码到另一台服务器上面 去,只有你的用户名和密码以及那个公共秘钥都对了,你才能登录,这个有点 类似于支付宝的数字证书吧,虽然 没 那么 复杂。那么,我们总不可能每次都去 匹配吧?这里告诉 大家 一个简单的方法,看下面的代码。

#if DEBUG
public ActionResult LogMeIn(LogOnModel model)
{
return RedirectToAction("Landing");
}
#endif

  大家只要 用#if DEBUG #endif 代码段,就可以调试本地的了,如果是服务器上的话,就不会执行本段代码 ,这段 代码只是在 本地启动的时候才有用哦。下面告诉 大家 一种伪造 登录的方法:可以使用Request.Headers.Add()方法去添加相应的键值对,比如 你 可以 往这里面 添加 用户名,EMAIL,公共秘钥ID等,如下面的代码 所示。

            Request.Headers.Add("SM_USER", user);
Request.Headers.Add("EMAIL", email);

  然后 进行本地调试的时候,只要把这些值取出来就行了,当然了,我这么做 也许有些人会说没意义,我的目的只是 为了模拟登录的流程而已,仅此而已。

  当然 ,你需要一个判断的过程,于是就有 了下面的方法:

        public CommandResponse Command<TRequest>(TRequest message)
{
var result = QueryHandlerExecute((IMessage)message); if (result!= null)
{
return (CommandResponse) result;
} return new CommandResponse();
}

  具体的我也说不清楚,这里面感觉 还是 有点 复杂。。。

 

    public class CommandResponse
{
public bool Success { get; set; } public string Message { get; set; }
}

  如果Success=true的话,那么就 使用下面这句话,这句话的作用就是使你的User.Identity.IsAuthenticated为True,表示 你已经通过了登录 验证。

FormsAuthentication.SetAuthCookie(model.MS2User, false);

  如果 你 没有通过验证的话,那么 还需要 往TempData里面去放一些值,跳转带到新页面 ,为什么 使用 TempData,因为登录后,错误 信息只显示一次 ,所以用TempData,当然如果 你是正常 退出的话,那么就 可以 这样 。

                System.Web.HttpContext.Current.Session.RemoveAll();
FormsAuthentication.SignOut();
return RedirectToAction("LogOn", "Account", new
{
area = "MS2Auth"
});

注意 在 这里 area是必须的,因为 我们把登录的接口 放到了Area里面。好久没写了,文字比较拙劣,今天试着做了下登录,没 完全做出来,希望以后能有 进一步的提高吧。

 PS:差点 忘了贴登录的View页面,比较简单。

@using System.Configuration
@using YLW.MS2.Common
@using StructureMap
@model YLW.MS2.Web.Models.LogOnModel @{
ViewBag.Title = "LogOn";
Layout = "~/Views/Shared/_Layout.cshtml";
} <div class="error-container">
<ul>
@Html.ValidationSummary(true,"登录失败!请检查!")
@if (TempData.ContainsKey("errorMessage"))
{
Html.Raw(TempData["errorMessage"].ToString());
}
@if (ViewData.ContainsKey("errorMessage"))
{
Html.Raw(ViewData["errorMessage"].ToString());
}
</ul> </div> <form name="login" id="login" method="post" action="@Url.Action("LogMeIn", "Account", new { area="MS2Auth"})"> <div>
@Html.TextBoxFor(m=>m.UserName)
@Html.ValidationMessageFor(m=>m.UserName)
</div> <div>
@Html.PasswordFor(m=>m.Password)
@Html.ValidationMessageFor(m=>m.Password) </div> <div>
<button type="submit" class="button" onclick="CheckUser();return false" value="Log In"></button> </div> </form> <script type="text/javascript">
function CheckUser()
{
$.ajax({
type: "POST",
url: "@Url.Action("LogOn","Account")",
data: { Email: $("#UserName").val(), password: $("#Password").val(), IsLogin: 'true' },
dataType: "text json",
crossDomain: false,
cache: false,
async: true,
success: function (result) {
if (result.Data) { }
else
{ } } }); } </script>

  里面很 简单,就是一个 提交表单,其中注意一下 如果 是自己DEBUG环境的话,记得吧把 IsLogin设为TRUE,当然,这个是暂时的。