SSO集成方案[随笔]
看这个方案之前,先说明下为什么要加入sso,以防对大家产生不好的影响。我们产品使用传统winform+db服务+db存储方式开发,一群老菜帮子开发,以传统的datatble做数据传递,很多年了未有变化。
然后我来了,感觉我这个老菜帮子都受不了这种开发,然后下定决心,作了一些封装,看起来有点像orm的感觉了,并决定加入嵌入bs页面,美化界面,并补充winform在图表功能方面的短板。
然后就造成了各bs模块分别嵌入到cs不同的页面中,并且各bs模块中有涉及到对业务的操作。很危险!因为在网页中可以直接打开各bs模块视图,无需登录,无需验证等。针对这种情况,sso需求由此而来…
目标:
1.各bs模块统一使用单点登录,不能没有限制就使用业务系统
2.cs端嵌入bs时自动模拟sso,实现可以免登录使用页面
下文sso科普是复制不知道谁的博文的,具体是谁忘了,在此说明下
sso整体流程图:
sso分为sso-server和sso-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; } }
推荐阅读
-
【Swagger】可能是目前最好的 Spring Boot 集成 swagger 的方案
-
详解可跨域的单点登录(SSO)实现方案【附.net代码】
-
laravel5集成支付宝alipay扫码支付流程(Laravel 支付解决方案)
-
什么是zabbix(高度集成的监控集成方案)
-
【Swagger】可能是目前最好的 Spring Boot 集成 swagger 的方案
-
sitemesh,myfaces,richfaces 的集成解决方案
-
SpringBoot集成Eureka后Controller返回结果为xml的解决方案
-
大数据需要全面集成开放的解决方案
-
安防视频监控系统视频上云解决方案EasyCVR集成海康EHome私有协议系列:设备录像流数据进行PS包分割
-
北峰9300BTX|港口码头集成化集群通信解决方案