首页 .Net Asp.Net 实现单点登录(SSO)

Asp.Net 实现单点登录(SSO)

SSO英文全称Single Sign On(单点登录)。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。

它是目前比较流行的企业业务整合的解决方案之一。(本段内容来自百度百科)   话不多说,开撸!

逻辑分析:

Client1:用户A在电脑1上登录管理员账号

Service:验证用户A登陆成功生成Admin账号的Token令牌,分别存储电脑1的cookie中服务器的全局变量中(可以是session,缓存,全局变量,数据库)

Client2:用户B在电脑2上登录管理员账号

Service:验证用户B登陆成功重新生成Admin账号的Token令牌,分别存储电脑2的cookie中服务器的全局变量中(可以是session,缓存,全局变量,数据库)

Client1:触发验证:

    1,判断服务器全局变量是否过期,提示:身份信息过期,请重新登录。

    2,判断客户端的cookie是否过期,提示:长时间未登录,已下线。

    3,判断电脑1上的cookie与服务器全局变量相比是否一致。提示:此用户已在别处登陆!你被强制下线!

 

 

代码实现

Service:

1,创建一个服务器校验登录类,代码如下

复制代码

using Coldairarrow.Business;using Coldairarrow.Util;using System;using System.Text;using System.Web.Mvc;namespace Coldairarrow.Web
{    /// <summary>
    /// 校验登录    /// </summary>
    public class CheckLoginAttribute : FilterAttribute, IActionFilter
    {        public IOperator _operator { get; set; }        public ILogger _logger { get; set; }        /// <summary>
        /// Action执行之前执行        /// </summary>
        /// <param name="filterContext">过滤器上下文</param>
        public void OnActionExecuting(ActionExecutingContext filterContext)
        {            var request = filterContext.RequestContext.HttpContext.Request;            try
            {                //若为本地测试,则不需要登录
                if (GlobalSwitch.RunModel == RunModel.LocalTest)
                {                    return;
                }                //判断是否需要登录
                bool needLogin = !filterContext.ContainsAttribute<IgnoreLoginAttribute>();                //获取session里面的用户id
                var uid = SessionHelper.Session["UserId"]?.ToString();                if (needLogin)
                {                    if (string.IsNullOrEmpty(uid))
                    {                        //转到登录                        RedirectToLogin();

                    }                    else
                    {                        var Cguid = filterContext.HttpContext.Request.Cookies["CToken"].Value?.ToString();                        var Sguid = CacheHelper.Cache.GetCache(uid + "_SToken")?.ToString();                        //判断是否过期
                        if (string.IsNullOrEmpty(Cguid) || string.IsNullOrEmpty(Sguid))
                        {                            //  过期  转到登录
                            ReturnLogin("身份信息以失效,请重新登陆!");
                            SessionHelper.Session["UserId"] = "";
                        }                        else
                        {                            //判断用户是否重复登陆
                            if (Sguid != Cguid)
                            {                                //  过期  转到登录
                                ReturnLogin("此用户已在别处登陆!你被强制下线!");
                                SessionHelper.Session["UserId"] = "";                                //message = "已登陆";                            }
                        }

                    }


                }                //if (needLogin && !_operator.Logged())                //{                //转到登录                //    RedirectToLogin();                //}                //else                //{                //    string Id = _operator.UserId;                //    _operator.Login(Id);                //    return;                //}            }            catch (Exception ex)
            {
                _logger.Error(ex);
                RedirectToLogin();
            }            void RedirectToLogin()
            {                if (request.IsAjaxRequest())
                {
                    filterContext.Result = new ContentResult
                    {
                        Content = new AjaxResult { Success = false, ErrorCode = 1, Msg = "未登录" }.ToJson(),
                        ContentEncoding = Encoding.UTF8,
                        ContentType = "application/json"
                    };
                }                else
                {
                    UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext);                    string loginUrl = urlHelper.Content("~/Home/Login");                    string script = $@"    
            <html>
                <script>
                    top.location.href = '{loginUrl}';
                </script>
            </html>            ";
                    filterContext.Result = new ContentResult { Content = script, ContentType = "text/html", ContentEncoding = Encoding.UTF8 };
                }
            }            void ReturnLogin(string msg)
            {
                UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext);                string loginUrl = urlHelper.Content("~/Home/Login");                string script = $@"    
            <html>
                <script>
                    alert('{msg}');
                    top.location.href = '{loginUrl}';
                </script>
            </html>            ";
                filterContext.Result = new ContentResult { Content = script, ContentType = "text/html", ContentEncoding = Encoding.UTF8 };
            }
        }        /// <summary>
        /// Action执行完毕之后执行        /// </summary>
        /// <param name="filterContext"></param>
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {

        }
    }
}

复制代码

 

2,创建一个mvc基控制器继承Controller并且引用特性【CheckLogin】

 

 

 3,业务控制器继承BaseMvcController,并编写登录代码。登陆成功后调用login方法,代码如下:

 

 

 

复制代码

        /// <summary>
        /// 登录        /// </summary>
        /// <param name="userId">用户逻辑主键Id</param>
        public void Login(string userId)
        {            //保存登陆成功的令牌
            string Guid_str = "";            //分配一个唯一标识符
            Guid_str = GuidHelper.GuidTo16String();
            HttpContext.Current.Response.Cookies["CToken"].Value = Guid_str;            //给系统变量存储一个值,Uid代表哪个用户,GUID则是唯一标识符
            CacheHelper.Cache.SetCache(userId + "_SToken", Guid_str, new TimeSpan(0, 0, 30, 0, 0), ExpireType.Absolute);
            SessionHelper.Session["UserId"] = userId;
        }

复制代码

4,这个时候基本就结束了,还需要增加一个忽略验证的类,这个特性加在登录页面。意思是登录页面不需要触发验证;

 

 

 

 

5,服务器验证的核心代码有点不优雅,不过实现逻辑了。有问题可以评论区沟通一下。本人用的是将token分别存储在服务器缓存+客户端cookie完成  ,大家服务器上可以用session,缓存,全局变量,数据库等任意方式实现;


特别声明:本站部分内容收集于互联网是出于更直观传递信息的目的。该内容版权归原作者所有,并不代表本站赞同其观点和对其真实性负责。如该内容涉及任何第三方合法权利,请及时与824310991@qq.com联系,我们会及时反馈并处理完毕。