欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SSO集成方案[随笔]

程序员文章站 2022-03-01 13:24:45
看这个方案之前,先说明下为什么要加入SSO,以防对大家产生不好的影响。我们产品使用传统winform+db服务+Db存储方式开发,一群老菜帮子开发,以传统的datatble做数据传递,很多年了未有变化。 然后我来了,感觉我这个老菜帮子都受不了这种开发,然后下定决心,作了一些封装,看起来有点像orm的 ......

看这个方案之前,先说明下为什么要加入sso,以防对大家产生不好的影响。我们产品使用传统winform+db服务+db存储方式开发,一群老菜帮子开发,以传统的datatble做数据传递,很多年了未有变化。

然后我来了,感觉我这个老菜帮子都受不了这种开发,然后下定决心,作了一些封装,看起来有点像orm的感觉了,并决定加入嵌入bs页面,美化界面,并补充winform在图表功能方面的短板。

然后就造成了各bs模块分别嵌入到cs不同的页面中,并且各bs模块中有涉及到对业务的操作。很危险!因为在网页中可以直接打开各bs模块视图,无需登录,无需验证等。针对这种情况,sso需求由此而来…

 

目标:

  1.bs模块统一使用单点登录,不能没有限制就使用业务系统

  2.cs端嵌入bs时自动模拟sso,实现可以免登录使用页面

 

下文sso科普是复制不知道谁的博文的,具体是谁忘了,在此说明下

sso整体流程图:

 SSO集成方案[随笔]

 

 

sso分为sso-serversso-client两个部分,sso-client可以是多个的,即各个需要单点登录的client

sso-server

sso-server主要负责用户登录、注销、为sso-client分配token、验证token的工作。

sso-server分配token

为sso-client分配token的部分,在sso-client请求sso受信页面的时候,检查sso-server是否登录,如果没有登录则跳转到sso-server的登录页面,如果已登录,则执行分配token的代码,在分配完成以后将tokenid作为参数添加到returnurl中,并跳转到returnurl。

当完成token分配之后,页面将带有token的参数跳转到sso-client页面,并在sso-client的cookie中添加token值,在以后的每次请求中,sso-client通过调用sso-server的服务来验证token的合法性。

validatetoken用来验证tokenid的合法性,keeptoken用来保持token不会过期。

sso-client通过调用validate验证token,并得到当前的登录用户信息。

sso-client

sso-client作为受信系统来存在的,它自己没有认证系统,只能通过sso-server来完成用户身份认证的工作。

当用户请求sso-client的受保护资源时,sso-client会首先是否有tokenid,如果存在tokenid,则调用sso-server的接口来验证这个tokenid是否合法;

验证成功以后将会返回ssotoken的实例,里面包含已登录的用户信息

 

科普后动手:

mvc.sso.service:mvc+redis,mvc实现sso-server的所有功能,存储和时效使用redis管理,并使用redis共享session

主要代码:

     /// <summary>
        /// 验证是否包含该token,并返回信息
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        [httppost]
        public string validatetoken(string key)
        {
            //var token = tokensmanager.gettoken(key);

            try
            {
                mytoken token = new mytoken();
                token.validatetoken = false;

                if (boolvalidatetoken(key))
                {
                    token.user = redis.stringget<users>(key);
                    token.validatetoken = true;
                }

                return jsonconvert.serializeobject(token);
            }
            catch (exception ex)
            {
                throw new exception("validatetoken:" + ex.message);
            }
        }


     /// <summary>
        /// 登录
        /// </summary>
        /// <param name="name"></param>
        /// <param name="password"></param>
        /// <param name="backurl"></param>
        /// <returns></returns>
        [httppost]
        public string login(string name, string password, string backurl)
        {
            try
            {
                mymsg msg = new mymsg();
                //tokenkey
                byte[] byts = system.text.encoding.default.getbytes(string.format("{0},{1}", name, password));
                var key = convert.tobase64string(byts);
                //判断是否tokenids是否已存在该用户,不存在则判断登录,添加到token
                if (!boolvalidatetoken(key))
                {
                    var user = userbll.getuser(name, password);
                    if (user == null)
                    {
                        msg.islogined = false;
                        msg.msg = "用户名或密码错误,登录失败!";
                    }
                    msg.islogined = true;
                    //并添加到全局变量中
                    //tokensmanager.addtoken(key, user);
                    session["token"] = key;
                    redis.stringset<users>(key, user);
                }

                session["token"] = key;
                response.cookies.add(new httpcookie("ctoken", key));
                if (!string.isnullorempty(backurl))
                {
                    var url = backurl + "?token=" + key;
                    //response.redirect(backurl + "?token=" + key, true);//生成一个tokenid 发放到客户端
                    msg.backurl = url;
                }
                msg.msg = "欢迎您:" + name;
                return jsonconvert.serializeobject(msg);
            }
            catch (exception ex)
            {
                throw;
            }
        }

 

bs端mvc-client集成方案

mvc集成sso方式为添加过滤器,并在需要添加sso验证的控制器上添加上该过滤器

主要代码:

public override void onactionexecuting(actionexecutingcontext filtercontext)
        {
            base.onactionexecuting(filtercontext);

            try
            {
                bool islogined = false;//登录标志
                var ctoken = filtercontext.httpcontext.request.cookies["ctoken"];
                var token = filtercontext.httpcontext.request["token"];
                var sessionkey = httpcontext.current.session["token"];
                if (token != null || sessionkey != null || ctoken != null)
                {//ewfuc2hplge=
                    token = token ?? sessionkey.tostring() ?? ctoken.value;
                    var data = new { key = token };
                    //如果token不为空则去服务验证token是否有效
                    httpcontent httpcontent = new stringcontent(jsonconvert.serializeobject(data));
                    httpcontent.headers.contenttype = new mediatypeheadervalue("application/json");
                    var httpclient = new httpclient();
                    var responsejson = httpclient.postasync(ssourl + "/validatetoken", httpcontent)
                        .result.content.readasstringasync().result;
                    var mytoken = jsonconvert.deserializeobject<jh_oemr_model.mytoken>(responsejson);
                    if (mytoken.validatetoken)
                    {
                        islogined = true;
                    }
                }

                if (!islogined)
                    filtercontext.httpcontext.response.redirect(ssourl+"?backurl=" + filtercontext.httpcontext.request.url, true);
            }
            catch (exception ex)
            {
                filtercontext.httpcontext.response.write("aaaaa"+ex.message.tostring());
                throw;
            }
        }

cs端集成sso:

cs端加载bs页面时,先判断是否模拟登录,如未登录,模拟登录,然后访问bs端网址带上token即可

主要代码:

private static void loginsso()
        {
            var data = new { name = uname, password = upwd, backurl = string.empty };
            httpcontent httpcontent = new stringcontent(jsonconvert.serializeobject(data));
            httpcontent.headers.contenttype = new mediatypeheadervalue("application/json");
            var httpclient = new httpclient();
            var responsejson = httpclient.postasync(ssourl+"/login", httpcontent)
                .result.content.readasstringasync().result;
            var mytoken = jsonconvert.deserializeobject<mymsg>(responsejson);
            if (mytoken.islogined)
            {
                isloginedsso = true;
            }
        }