好久没写随笔了,这段时间没 什么事情,领导 一直没安排任务,索性 一直在研究代码,说实在的,这个登录都 搞得我云里雾里的,所以这次我可能也讲得不是 特别清楚,但是 我尽力把我知道的讲出来,顺便也对自己最近的工作 做义工总结吧。
我们的项目用到的一些 技术也跟大家提一下,主要是用了NHibernate,StructureMap,FluentHibernate等等,可能这篇文章我不会 全部 提到 ,不过相信后面,如果我要写文章的话,肯定是会提到的。我自己也模仿 了公司的框架做了一个 简单的登录(未完成)的,因为感觉公司的东西实在是很复杂,所以不一定可以完全消化,所以才 写了一上午没写完 = =
首先,登录的话,我是写在一个Area里面,至于 怎么建立Area(区域)我就 不多累赘了,Area的名字我就 取成MS2Auth,因为 自己 喜欢MapleStory这个游戏,所以取了 它的 一个缩写 单词,以下是结构。
我们新建一个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"] == "0") { 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,当然,这个是暂时的。