十六、【适合中小企业的.Net轻量级开源框架】EnterpriseFrameWork框架核心类库之单点登录SSO
回《【开源】EnterpriseFrameWork框架系列文章索引》
EFW框架源代码下载:http://pan.baidu.com/s/1qWJjo3U
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

如上图,当用户第一次访问应用系统1的时候,因为还没有登录,会被引导到认证系统中进行登录;根据用户提供的登录信息,认证系统进行身份校验,如果通过校验,应该返回给用户一个认证的凭据--ticket;用户再访问别的应用的时候,就会将这个ticket带上,作为自己认证的凭据,应用系统接受到请求之后会把ticket送到认证系统进行校验,检查ticket的合法性。如果通过校验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。
上面这段文字描叙是从网上摘抄的,觉得基本上把单点登录的原理讲得很清晰了;本章讲解EFW框架中是如何实现单点登录的以及框架中是如何使用的;
本文要点:
1.什么情况下会用到单点登录
2.框架中的三种模式Web、Winform和WCF,分别是怎样进行用户验证
3.单点登录样例
4.单点登录SSO实现代码
1.什么情况下会用到单点登录
刚开始框架中也是没有单点登录此模块的,有一次需要在Winform系统中嵌入Web页面,整合两个系统。Web页面的用户信息验证一直没找到什么好的解决办法,刚开始的办法是通过往网页地址后面自动加上登录用户名和密码,发送到后台进行登录;这样也达到了整合的目的,但是总感觉比较别扭,直接把用户名和密码暴露在地址栏肯定存在安全隐患。后来经过一番波折在网上找到类似的解决办法,利用单点登录的方案达到了比较好的效果;
除了上面说的Winform系统中嵌入Web页面这种情况,还有经常在自己公司系统中整合一些合作伙伴的系统,如此打包销售更有市场竞争力,这样首要解决的问题也是登录入口统一;如今行业软件现状,不像十年前了只有那么一两套系统,讲究着用就行了,现在没有用上十来个系统就不叫信息化了,所以你能提供一个单点登录的解决方案也是一大卖点;再就是现在的软件公司不管大小靠一个产品就能生存的很难了,基本都是最大化的挖掘客户的需求,最好能提供一整套的解决方案,这些系统能整体销售更好,而单个产品销售也得支持。所以不管是客户的需求还是内部的产品都会存在系统间整合的问题,而利用单点登录至少能解决用户统一验证的问题;
2.框架中的三种系统模式Web、Winform和WCF,分别是怎样进行用户验证
先分析一下框架中的三种系统模式的用户验证是如何实现的,然后才能正确运用单点登录的功能;
1)Web系统用户验证,输入用户名密码登录后,登录界面向后台发送登录请求调用LoggingController执行用户密码验证代码,验证正确后把用户信息存入Session;之后所有界面操作向后台发送请求,APIHttpHandler对象接收请求后,先判断Session中是否存在用户信息,只有存在才执行对应的控制器代码,否则再返回错误信息给前台;

这里增加了一个系统配置参数TurnOnLoginRight用来是否打开验证用户登录,这在我们开发系统中调试后台控制器很有用;

2)Winform系统用户验证,这个比较简单,用户登录后根据后台配置的用户权限,动态加载系统的菜单,所以后面的操作也不用再进行用户验证了;
3)WCF系统用户验证,就是上面两种的结合了,客户端如Winform系统,WCF中间件在WCFHandlerService服务中进行用户验证;
3.单点登录样例
用上面讲过的Winform系统嵌入Web页面这种情况下如何使用单点登录的功能;
在登录成功后调用框架中的SsoHelper对象的SignIn方法生成TokenKey;

然后把TokenKey值加入Web页面的URL地址之后,然后web页面向后台发送Ajax请求的时候把TokenKey当成参数传入后台,后台进行单点登录验证。


4.单点登录SSO实现代码
框架源代码目录结构:

其中外部调用SSO功能只需要调用SsoHelper对象就可以了,SsoHelper把SSO封装成外部调用的类;包括SignIn、SignOut、ValidateToken等方法;TokenManager类存储所有登录用户的信息,TokenInfo类封装的用户信息结构;
SsoHelper文件
/// <summary>
/// 单点登录辅助类
/// </summary>
public class SsoHelper
{
/// <summary>
/// 登录
/// </summary>
/// <param name="userId"></param>
/// <param name="tokenid"></param>
/// <returns></returns>
public static bool SignIn(string userId,string userName, out Guid tokenid)
{
TokenInfo existToken = TokenManager.GetToken(userId);
if (existToken != null)
{
tokenid = existToken.tokenId;
return true;
} TokenInfo token = new TokenInfo()
{
tokenId = Guid.NewGuid(),
IsValid = true,
CreateTime = DateTime.Now,
ActivityTime=DateTime.Now,
UserId = userId,
UserName=userName//,
//RemoteIp = Utility.RemoteIp
};
tokenid = token.tokenId;
return TokenManager.AddToken(token);
}
/// <summary>
/// 注销
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public static bool SignOut(Guid token)
{
return TokenManager.RemoveToken(token);
}
/// <summary>
/// 是否有效登录
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public static AuthResult ValidateToken(string token)
{
Guid guid= ConvertHelper.GetGuid(token, Guid.NewGuid()); AuthResult result = new AuthResult() { ErrorMsg = "Token不存在" };
TokenInfo existToken = TokenManager.GetToken(guid); if (existToken != null)
{
#region 客户端IP不一致
//if (existToken.RemoteIp != entity.RemoteIp)
//{
// result.ErrorMsg = "客户端IP不一致";
//}
#endregion if (existToken.IsValid == false)
{
result.ErrorMsg = "Token已过期" + existToken.ActivityTime.ToLongTimeString() + ":" + DateTime.Now.ToLocalTime();
TokenManager.RemoveToken(existToken.tokenId);//移除
}
else
{
result.User = new UserInfo() { UserId = existToken.UserId,UserName=existToken.UserName, CreateDate = existToken.CreateTime };
result.ErrorMsg = string.Empty;
}
} return result;
} /// <summary>
/// 定时触发登录码的活动时间,频率必须小于4分钟
/// </summary>
/// <param name="token"></param>
public static void UserActivity(Guid token)
{
TokenInfo existToken = TokenManager.GetToken(token);
existToken.ActivityTime = DateTime.Now;
} /// <summary>
/// 用户是否在线
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static bool IsUserOnline(string userId)
{
TokenInfo existToken = TokenManager.GetToken(userId);
if (existToken != null) return true;
return false;
}
}
TokenManager文件
public class TokenManager
{
private const int _TimerPeriod = ;//60秒
private static Timer thTimer; static List<TokenInfo> tokenList = null; static TokenManager()
{
tokenList = new List<TokenInfo>();
thTimer = new Timer(_ThreadTimerCallback, null, _TimerPeriod, _TimerPeriod);
} public static bool AddToken(TokenInfo entity)
{
tokenList.Add(entity);
return true;
} public static bool RemoveToken(Guid token)
{
TokenInfo existToken = tokenList.SingleOrDefault(t => t.tokenId ==token);
if (existToken != null)
{
tokenList.Remove(existToken);
return true;
} return false;
} public static TokenInfo GetToken(Guid token)
{
TokenInfo existToken = tokenList.SingleOrDefault(t => t.tokenId == token);
return existToken;
} public static TokenInfo GetToken(string userId)
{
TokenInfo existToken = tokenList.SingleOrDefault(t => (t.UserId == userId && t.IsValid==true));
return existToken;
} private static void _ThreadTimerCallback(Object state)
{
DateTime now = DateTime.Now; Monitor.Enter(tokenList);
try
{
// Searching for expired users
foreach (TokenInfo t in tokenList)
{
if (((TimeSpan)(now - t.ActivityTime)).TotalMilliseconds > _TimerPeriod)
{
t.IsValid = false;//失效
}
}
}
finally
{
Monitor.Exit(tokenList);
}
}
}
TokenInfo文件
public class TokenInfo
{
public Guid tokenId { get; set; } public DateTime CreateTime { get; set; } public DateTime ActivityTime { get; set; } public string RemoteIp { get; set; } public string UserId { get; set; } public string UserName { get; set; } public bool IsValid { get; set; }
}
参考资料:
编写你自己的单点登录(SSO)服务
http://blog.csdn.net/javachannel/article/details/752437
十六、【适合中小企业的.Net轻量级开源框架】EnterpriseFrameWork框架核心类库之单点登录SSO的更多相关文章
- 十八、【开源】EnterpriseFrameWork框架核心类库之Winform控制器
回<[开源]EnterpriseFrameWork框架系列文章索引> EFW框架源代码下载:http://pan.baidu.com/s/1qWJjo3U EFW框架中的WinContro ...
- SpringCloud微服务实战——搭建企业级开发框架(四十):使用Spring Security OAuth2实现单点登录(SSO)系统
一.单点登录SSO介绍 目前每家企业或者平台都存在不止一套系统,由于历史原因每套系统采购于不同厂商,所以系统间都是相互独立的,都有自己的用户鉴权认证体系,当用户进行登录系统时,不得不记住每套系统的 ...
- Jmeter(二十六) - 从入门到精通 - 搭建开源论坛JForum(详解教程)
1.简介 今天这篇文章主要是给大家讲解一下,如何部署测试环境,这里宏哥部署一个开源测论坛,后边的文章中会用到这个论坛,并且也看到童鞋们在群里讨论如何在开发将测试包发给你以后,你如何快速地部署测试环境. ...
- HTML5学习笔记(十六):原型、类和继承【JS核心知识点】
理解原型 在JavaScript中,只要声明了一个函数,就会为该函数创建一个名为prototype的属性,该属性指向当前函数的原型对象. 而函数的原型对象有一个constructor属性,该属性指向刚 ...
- 【开源】EFW框架系列文章索引
开源轻量级.Net框架EnterpriseFrameWork详解 ——自己动手写框架 ——适合中小企业的开发框架 ——Ajax+JqueryEasyUI+NotNetBar+MVC+WebServic ...
- 六、EnterpriseFrameWork框架基础功能之权限管理
回<[开源]EnterpriseFrameWork框架系列文章索引> 从本章开始进入框架的第二块内容“EnterpriseFrameWork框架的基础功能”,包括:权限管理.字典数据管理. ...
- 二十二、【轻量级开源框架】EFW框架Web前端开发之JqueryEasyUI
回<[开源]EFW框架系列文章索引> EFW框架源代码下载V1.2:http://pan.baidu.com/s/1hcnuA EFW框架实例源代码下载:http://pan ...
- 二十六、【开源框架】EFW框架Winform前端开发之Grid++Report报表、条形码、Excel导出、图表控件
回<[开源]EFW框架系列文章索引> EFW框架源代码下载V1.2:http://pan.baidu.com/s/1hcnuA EFW框架实例源代码下载:http://pan ...
- 十六款值得关注的NoSQL与NewSQL数据库--转载
原文地址:http://tech.it168.com/a2014/0929/1670/000001670840_all.shtml [IT168 评论]传统关系型数据库在诞生之时并未考虑到如今如火如荼 ...
随机推荐
- nginx upstream模块--负载均衡
Module ngx_http_upstream_module英文文档 upstream模块相关说明1.upstream模块应放于nginx.conf配置的http{}标签内2.upstream模块默 ...
- windows装了双系统设置默认启动系统
我们装了双系统后,在开机设置时会自动启动其中一个系统,有时提示时间会很短,以至于不能判断自己是否装了双系统,以下,当电脑打开后,我们可以观察是否装了双系统 1.按组合键<Win+R>,打开 ...
- O2O已死?不!美团点评们迎来新风口
当年的千团大战,巅峰时期曾涌入了5000多家团购网站,刘旷本人也参与了此次团购大战.而就在当时很多人都唱衰团购的时候,美团和大众点评却最终脱颖而出,市值一路飙升,人人网旗下的糯米网因为卖给了百度,也得 ...
- iOS开发-UITextView根据内容自适应高度
UITextView作为内容文本输入区域,有的时候我们需要根据内容动态改变文本区域的高度,效果如下: 定义UITextView,实现UITextViewDelegate: -(UITextView * ...
- Leetcode 189 Rotate Array stl
题意:将数组旋转k次,如将数组[1,2,3,4,5]旋转1次得到[2,3,4,5,1],将数组[1,2,3,4,5]旋转2次得到[3,4,5,1,2]..... 本质是将数组分成两部分a1,a2,.. ...
- WindowsPhone App如何扩展能够使用的内存
目前手机系统中对App的内存使用都是有限制的,尤其是对于Android和WindowsPhone这样的平台,因为机型很多,配置高低不同因此对于同一个App在不同的手机上运行的效果也不同. WP上通常对 ...
- 转:LAV Filter 源代码分析
1: 总体结构 LAV Filter 是一款视频分离和解码软件,他的分离器封装了FFMPEG中的libavformat,解码器则封装了FFMPEG中的libavcodec.它支持十分广泛的视音频格式. ...
- Jquery easyui开启行编辑模式增删改操作
Jquery easyui开启行编辑模式增删改操作 Jquery easyui开启行编辑模式增删改操作先上图 Html代码: <table id="dd"> </ ...
- IOS中CocoaPods安装与使用
网上有很多文章关于 cocoaPods配置文章,其实cocoaPods配置并不难,只是比较繁琐而已,我也是照着网站文章一步步搭建配置成功.写这篇文章的目的就是像做笔记一样,便于以后用的时候好回忆.废话 ...
- 基于apt实现的Android快速持久化框架:AptPreferences
AptPreferences是基于面向对象设计的快速持久化框架,目的是为了简化SharePreferences的使用,减少代码的编写.可以非常快速地保存基本类型和对象.AptPreferences是基 ...