asp.net mvc5实现单点登录

写这个之前特意找了下,看到也有这方面的别人写的文章资料,但是貌似都是类似于这个的:

http://www.cnblogs.com/New-world/p/3865939.html

想了下,要不要把自己做的写出来。这里记录下吧。

基本场景描述:用户ericlee先打开一个浏览器如chrome,输入账号密码,在某系统登录成功了,他再打开一个浏览器如firefox,再输入同一个账号密码,还是登录成功了(当然,同样的账号密码嘛)。他再来操作之前的chrome浏览器,发现登录效果还在,没被踢下线,系统还是可以操作。他可能就不开心了,也许他希望这个时候他之前在chrome浏览器的登录被挤下线的。

用户是衣食父母,为了让衣食父母开心,我们来满足一下他们这个小小的愿望。

基本思路:ericlee在chrome浏览器登录的时候将他的登录名(ericlee)与当前时间Datetime.Now收集起来放到一个对象里(CurrentUser的一个实例cuser),再把cuser做成json,保存到浏览器cookie中,再保存到缓存中(我用的是redis)。当ericlee在操作系统的时候,会经过一个Controller吧?Controller可以定义过滤器的吧?对的,就使用过滤器。每次操作,都在过滤器里从cookie里获取cuser,再从缓存中获取cuser,它两个都是字符串,看它们两个是否一样咯。完全一样的话,是可以保证登录时间一样的,登录时间一样的话,就可以说明这两个东东是同一次登录造成的结果。当ericlee再在firefox中登录的时候,没问题,这里我没打算说当有人已在登录状态的时候不允许再次登录同一个账号,So,很顺利的,ericlee在firefox中登录还是成功的(当然前提是输入正确的账号密码)。这样,当ericlee再回到chrome中再来操作系统的时候,过滤器就起作用了,从chrome的cookie中获取到的他的登录时间比如是2016/03/12 22:16:23.336,然而从缓存中获取到的登录时间是2016/03/12 22:20:55.256,这样,方便的判断出chrome不是最新登录的状态,让他回登录页面吧,走好不送。

来一个代码尝尝:

1、自定义的CurrentUser

    public class CurrentUser
    {
        public string UserID { get; set; }
        public string LoginName { get; set; }
        public DateTime? LastLoginTime { get; set; }
        public static CurrentUser GetCurrentUser()
        {
            if (HttpContext.Current == null)
            {
                return null;
            }
            CurrentUser cuser = null;
            string cookieLoginInfo = CookieMgr.Get(UserBLL.LoginCookieName);
            if (!string.IsNullOrEmpty(cookieLoginInfo))
            {
                var CrypteKey = ConfigMgr.GetAppSettingString("CrypteKey");
                cuser = JsonConvert.DeserializeObject<CurrentUser>(Cryptor.DesDecrypt(cookieLoginInfo, CrypteKey));
                string cacheLoginInfo = CacheMaker.Cache.Get<string>(cuser.UserID);
                if (!string.IsNullOrEmpty(cacheLoginInfo))
                {
                    if(cookieLoginInfo.Equals(cacheLoginInfo))
                    {
                        return cuser;
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }
    }

2、用户登录与注销代码:(稍微说下,这里用到了自定义的CurrentUser类,使用到了Json序列化,DES加密与解密,Cookie操作等等等)

    public class UserBLL
    {
        public static string LoginCookieName=ConfigMgr.GetAppSettingString("LoginCookieName");
        #region 登录注销
        /// <summary>
        /// 登录,将用户信息保存到浏览器的cookie与服务器cache里
        /// </summary>
        /// <param name="Model"></param>
        /// <returns></returns>
        public static bool SignIn(SysUser Model)
        {
            CurrentUser cuser = new CurrentUser();
            cuser.UserID = Model.ID;
            cuser.LoginName = Model.LoginName;
            cuser.LastLoginTime = DateTime.Now;
            var cuserStr = Newtonsoft.Json.JsonConvert.SerializeObject(cuser);
            var CrypteKey = ConfigMgr.GetAppSettingString("CrypteKey");
            var cuserHash = Cryptor.DesEncrypt(cuserStr, CrypteKey);
            string domain = CookieMgr.GetDomain(HttpContext.Current.Request.Url.ToString());
            CookieMgr.Set(LoginCookieName, cuserHash, 0, domain);
            if (CacheMaker.Cache.Set(cuser.UserID, cuserHash))
            {
                return true;
            }
            else
            {
                CookieMgr.Remove(LoginCookieName);
                return false;
            }
        }
        /// <summary>
        /// 注销,清除浏览器的cookie和服务器中缓存的用户信息
        /// </summary>
        /// <returns></returns>
        public static bool SignOut()
        {
            var cuser=CurrentUser.GetCurrentUser();
            if (cuser == null)
            {
                return false;
            }
            else
            {
                CookieMgr.Remove(LoginCookieName);
                CacheMaker.Cache.Remove(cuser.UserID);
                return true;
            }
        }
        #endregion

    }

3、说好的过滤器一定要:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class LSAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
    {
        public LSAuthorizeAttribute()
        {

        }       
public virtual void OnAuthorization(AuthorizationContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } CurrentUser cuser; UrlHelper Url = new UrlHelper(filterContext.RequestContext); var request = filterContext.RequestContext.HttpContext.Request; string url = string.Empty; if (IsSignIn(request, out cuser)) { ControllerBase c = filterContext.Controller; PropertyInfo p = c.GetType().GetProperty("cuser"); p.SetValue(c, cuser); } else { url =string.IsNullOrEmpty(url)? Url.Action("login", "index",new { area = "", returnurl = request["returnurl"] }):url; //HttpContext.Current.Response.Redirect(url, true); //return; filterContext.Result = new RedirectResult(url); } } /// <summary> /// 判断当前请求是否还了用户登录的cookie信息,如果带了,则再判断服务器缓存中记录是否有该用户的登录信息并是否一致 /// </summary> /// <param name="request"></param> /// <param name="cuser"></param> /// <returns></returns> private bool IsSignIn(HttpRequestBase request,out CurrentUser cuser) { cuser = null; string cookieLoginInfo=CookieMgr.Get(UserBLL.LoginCookieName); if (!string.IsNullOrEmpty(cookieLoginInfo)) { var CrypteKey = ConfigMgr.GetAppSettingString("CrypteKey"); cuser = JsonConvert.DeserializeObject<CurrentUser>(Cryptor.DesDecrypt(cookieLoginInfo, CrypteKey)); string cacheLoginInfo = CacheMaker.Cache.Get<string>(cuser.UserID); if (!string.IsNullOrEmpty(cacheLoginInfo)) { return cookieLoginInfo.Equals(cacheLoginInfo); } else { return false; } } else { return false; } } }

关于过滤器,要了解简单的可以看这个:

http://www.cnblogs.com/ben-zhang/archive/2013/03/11/2954298.html

想看详细的,可以看这个:

http://www.fwqtg.net/asp-net-mvc-%E8%BF%87%E6%BB%A4%E5%99%A8%E8%AF%A6%E8%A7%A3.html

详细这个太多我表示没有看完。

大概的就这样子,酱,night!!