.net 单点登录实践

哈喽,欢迎来到小朱课堂,下面开始你的学习吧!

.net 单点登录实践

前言
  最近轮到我在小组晨会来分享知识点,突然想到单点登录,准备来分享下如何实现单点登录,所以有了下文。实现方案以及代码可能写得不是很严谨,有漏洞的地方或者错误的地方欢迎大家指正。  
  刚开始头脑中没有思路,直接在博客园里面看看别人是如何来实现的,看了几篇文章发现,发现解决方案有点问题,或者说不算实现了单点登录。

名称定义
  为了方便说明先说明几个文中出现的名词的含义:
  P站:统一登录授权验证中心,demo中 域名是www.passport.com:801
  A站:处于不同域名下的测试网站,demo中 域名是www.a.com:802
  B站:处于不同域名下的测试网站,demo中 域名是www.b.com:803
 Token:用户访问P站的秘钥
 Ticket:用来保存用户信息的加密字符串
单点登录
  访问A站需要登陆的就跳转P站中进行登陆,P站登陆之后跳转回至A站,用户再次访问B站需要登陆的页面,用户不需要进行登陆操作就可以正常访问。

实现思路
  未登录用户访问A站,首先会重定向跳转至P站授权中心,P站首先通过检测Cookie来判断当前不是处于登陆状态,就跳转至登陆页面进行登陆操作,登陆成功之后把用户信息加密ticket附在A的请求地址上返回,A站通过解密ticket来获取用户信息,解密成功并存进Session中(这样用户在A中就处于登陆状态了),访问通过;当用户再次访问B站的时候,对于B站来说,用户是处于未登录状态,则同样会重定向跳转至P站授权中心,P站检测Cookie,判断当前用户处于登陆状态,就把当前用户信息加密成ticket附在B的请求地址上返回,后面的操作就和A站处理一样;这样都登陆之后再次访问A或者B,A和B中Session中都存储了用户信息,就不会再次请求P站了。
简单关系图
在这里插入图片描述
泳道流程图
在这里插入图片描述
主要逻辑说明

A站主要逻辑
用户首先访问A站,A站中会生成Token,并存入Cache中。Token是A访问P的钥匙,P在回调给A的时候需要携带这个Token。A请求P,P验证Token,P回调A,A检测Token是否是发送出去的Token,验证之后Token即失效,防止Token被再次使用。
  Token的生成是通过取时间戳的不同字段进行MD5加密生成,当然这里可以再加个盐进行防伪。

/// <summary>
        /// 生成秘钥
        /// </summary>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        public static string CreateToken(DateTime timestamp)
        {
            StringBuilder securityKey = new StringBuilder(MD5Encypt(timestamp.ToString("yyyy")));
            securityKey.Append(MD5Encypt(timestamp.ToString("MM")));
            securityKey.Append(MD5Encypt(timestamp.ToString("dd")));
            securityKey.Append(MD5Encypt(timestamp.ToString("HH")));
            securityKey.Append(MD5Encypt(timestamp.ToString("mm")));
            securityKey.Append(MD5Encypt(timestamp.ToString("ss")));
            return MD5Encypt(securityKey.ToString());
        }

P回调A的时候进行,A中对Token进行校验,校验不成功则请求P站统一授权验证。

/// <summary>
    /// 授权枚举
    /// </summary>
    public enum AuthCodeEnum
    {
        Public = 1,
        Login = 2
    }

    /// <summary>
    /// 授权过滤器
    /// </summary>
    public class AuthAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 权限代码
        /// </summary>
        public AuthCodeEnum Code { get; set; }

        /// <summary>
        /// 验证权限
        /// </summary>
        /// <param name="filterContext"></param>
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var request = filterContext.HttpContext.Request;
            var session = filterContext.HttpContext.Session;
            //如果存在身份信息
            if (Common.CurrentUser == null)
            {
                if (Code == AuthCodeEnum.Public)
                {
                    return;
                }
                string reqToken = request["Token"];
                string ticket = request["Ticket"];
                Cache cache = HttpContext.Current.Cache;
                //没有获取到Token或者Token验证不通过或者没有取到从P回调的ticket 都进行再次请求P
                TokenModel tokenModel= cache.Get(ConstantHelper.TOKEN_KEY)==null?null:(TokenModel)cache.Get(ConstantHelper.TOKEN_KEY);
                if (string.IsNullOrEmpty(reqToken) || tokenModel == null || tokenModel.Token!= reqToken ||
                    string.IsNullOrEmpty(ticket))
                {
                    DateTime timestamp = DateTime.Now;
                    string returnUrl = request.Url.AbsoluteUri;
                    tokenModel = new TokenModel
                    {
                        TimeStamp = timestamp,
                        Token = AuthernUtil.CreateToken(timestamp)
                    };
                    //Token加入缓存中,设计过期时间为20分钟
                    cache.Add(ConstantHelper.TOKEN_KEY, tokenModel, null, DateTime.Now.AddMinutes(20),Cache.NoSlidingExpiration,CacheItemPriority.Default, null);
                    filterContext.Result = new ContentResult
                    {
                       Content = GetAuthernScript(AuthernUtil.GetAutherUrl(tokenModel.Token, timestamp), returnUrl)
                    };
                    return;
                }
                LoginService service = new LoginService();
                var userinfo = service.GetUserInfo(ticket);
                session[ConstantHelper.USER_SESSION_KEY] = userinfo;
                //验证通过,cache中去掉Token,保证每个token只能使用一次
                cache.Remove(ConstantHelper.TOKEN_KEY);
            }
        }

        /// <summary>
        /// 生成跳转脚本
        /// </summary>
        /// <param name="authernUrl">统一授权地址</param>
        /// <param name="returnUrl">回调地址</param>
        /// <returns></returns>
        private string GetAuthernScript(string authernUrl, string returnUrl)
        {
            StringBuilder sbScript = new StringBuilder();
            sbScript.Append("<script type='text/javascript'>");
            sbScript.AppendFormat("window.location.href='{0}&returnUrl=' + encodeURIComponent('{1}');", authernUrl, returnUrl);
            sbScript.Append("</script>");
            return sbScript.ToString();
        }
    }

代码说明:这里为了方便设置Token的过期时间,所以使用Cache来存取Token,设定Token的失效时间为两分钟,当验证成功则从cache中移除Token。
调取过滤器

[Auth(Code = AuthCodeEnum.Login)]
2         public ActionResult Index()
3         {
4             return View();
5         }

P站主要逻辑
P站收到授权请求,P站首先通过Coookie来判断是否登陆,未登录则跳转至登陆页面进行登陆操作。

/// <summary>
        /// 授权登陆验证
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public ActionResult PassportVertify()
        {
            var cookie=Request.Cookies[ConstantHelper.USER_COOKIE_KEY];
            if (cookie == null ||string.IsNullOrEmpty(cookie.ToString()))
            {
                return RedirectToAction("Login", new { ReturnUrl = Request["ReturnUrl"] ,Token= Request["Token"] });
            }
            string userinfo = cookie.ToString();
            var success= passportservice.AuthernVertify(Request["Token"], Convert.ToDateTime(Request["TimeStamp"]));
            if (!success)
            {
                return RedirectToAction("Login", new { ReturnUrl = Request["ReturnUrl"], Token = Request["Token"] });
            }
            return Redirect(passportservice.GetReturnUrl(userinfo, Request["Token"],Request["ReturnUrl"]));
        }

已登陆则验证Token

/// <summary>
       /// 验证令牌
       /// </summary>
       /// <param name="token">令牌</param>
       /// <param name="timestamp">时间戳</param>
       /// <returns></returns>
        public bool AuthernVertify(string token,DateTime timestamp)
        {
            return AuthernUtil.CreateToken(timestamp) == token;
        }

测试说明
1、修改host
127.0.0.1 www.passport.com
127.0.0.1 www.a.com
127.0.0.1 www.b.com

2、部署IIS
P www.passport.com:801
A www.a.com:802
B www.b.com:803

3、测试账号和webconfig

用户名:admin 密码:123
demo
https://github.com/hexuefengx/study

搬砖路上,希望对你有帮助!可以关注一下哟,持续更新哟! 有问题可以私聊博主,快发表一下你的看法吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值