• 十六、【适合中小企业的.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文件

      1 /// <summary>
      2     /// 单点登录辅助类
      3     /// </summary>
      4     public class SsoHelper
      5     {        
      6         /// <summary>
      7         /// 登录
      8         /// </summary>
      9         /// <param name="userId"></param>
     10         /// <param name="tokenid"></param>
     11         /// <returns></returns>
     12         public static bool SignIn(string userId,string userName, out Guid tokenid)
     13         {
     14             TokenInfo existToken = TokenManager.GetToken(userId);
     15             if (existToken != null)
     16             {
     17                 tokenid = existToken.tokenId;
     18                 return true;
     19             }
     20 
     21             TokenInfo token = new TokenInfo()
     22             {
     23                 tokenId = Guid.NewGuid(),
     24                 IsValid = true,
     25                 CreateTime = DateTime.Now,
     26                 ActivityTime=DateTime.Now,
     27                 UserId = userId,
     28                 UserName=userName//,
     29                 //RemoteIp = Utility.RemoteIp
     30             };
     31             tokenid = token.tokenId;
     32             return TokenManager.AddToken(token);
     33         }
     34         /// <summary>
     35         /// 注销
     36         /// </summary>
     37         /// <param name="token"></param>
     38         /// <returns></returns>
     39         public static bool SignOut(Guid token)
     40         {
     41             return TokenManager.RemoveToken(token);
     42         }
     43         /// <summary>
     44         /// 是否有效登录
     45         /// </summary>
     46         /// <param name="token"></param>
     47         /// <returns></returns>
     48         public static AuthResult ValidateToken(string token)
     49         {
     50             Guid guid= ConvertHelper.GetGuid(token, Guid.NewGuid());
     51 
     52             AuthResult result = new AuthResult() { ErrorMsg = "Token不存在" };
     53             TokenInfo existToken = TokenManager.GetToken(guid);
     54 
     55             if (existToken != null)
     56             {
     57                 #region 客户端IP不一致
     58                 //if (existToken.RemoteIp != entity.RemoteIp)
     59                 //{
     60                 //    result.ErrorMsg = "客户端IP不一致";
     61                 //}
     62                 #endregion
     63 
     64                 if (existToken.IsValid == false)
     65                 {
     66                     result.ErrorMsg = "Token已过期" + existToken.ActivityTime.ToLongTimeString() + ":" + DateTime.Now.ToLocalTime();
     67                     TokenManager.RemoveToken(existToken.tokenId);//移除
     68                 }
     69                 else  
     70                 {
     71                     result.User = new UserInfo() { UserId = existToken.UserId,UserName=existToken.UserName, CreateDate = existToken.CreateTime };
     72                     result.ErrorMsg = string.Empty;
     73                 }
     74             }
     75 
     76             return result;
     77         }
     78 
     79         /// <summary>
     80         /// 定时触发登录码的活动时间,频率必须小于4分钟
     81         /// </summary>
     82         /// <param name="token"></param>
     83         public static void UserActivity(Guid token)
     84         {
     85             TokenInfo existToken = TokenManager.GetToken(token);
     86             existToken.ActivityTime = DateTime.Now;
     87         }
     88 
     89         /// <summary>
     90         /// 用户是否在线
     91         /// </summary>
     92         /// <param name="userId"></param>
     93         /// <returns></returns>
     94         public static bool IsUserOnline(string userId)
     95         {
     96             TokenInfo existToken = TokenManager.GetToken(userId);
     97             if (existToken != null) return true;
     98             return false;
     99         }
    100     }
    View Code

    TokenManager文件

     1  public class TokenManager
     2     {
     3         private const int _TimerPeriod = 60000;//60秒
     4         private static Timer thTimer;
     5 
     6         static List<TokenInfo> tokenList = null;
     7 
     8         static TokenManager()
     9         {
    10             tokenList = new List<TokenInfo>();
    11             thTimer = new Timer(_ThreadTimerCallback, null, _TimerPeriod, _TimerPeriod);
    12         }
    13 
    14         public static bool AddToken(TokenInfo entity)
    15         {
    16             tokenList.Add(entity);
    17             return true;
    18         }
    19 
    20         public static bool RemoveToken(Guid token)
    21         {
    22             TokenInfo existToken = tokenList.SingleOrDefault(t => t.tokenId ==token);
    23             if (existToken != null)
    24             {
    25                 tokenList.Remove(existToken);
    26                 return true;
    27             }
    28 
    29             return false;
    30         }
    31 
    32         public static TokenInfo GetToken(Guid token)
    33         {
    34             TokenInfo existToken = tokenList.SingleOrDefault(t => t.tokenId == token);
    35             return existToken;
    36         }
    37 
    38         public static TokenInfo GetToken(string userId)
    39         {
    40             TokenInfo existToken = tokenList.SingleOrDefault(t => (t.UserId == userId && t.IsValid==true));
    41             return existToken;
    42         }
    43 
    44         private static void _ThreadTimerCallback(Object state)
    45         {
    46             DateTime now = DateTime.Now;
    47 
    48             Monitor.Enter(tokenList);
    49             try
    50             {
    51                 // Searching for expired users
    52                 foreach (TokenInfo t in tokenList)
    53                 {
    54                     if (((TimeSpan)(now - t.ActivityTime)).TotalMilliseconds > _TimerPeriod)
    55                     {
    56                         t.IsValid = false;//失效
    57                     }
    58                 }
    59             }
    60             finally
    61             {
    62                 Monitor.Exit(tokenList);
    63             }
    64         }
    65     }
    View Code

    TokenInfo文件

     1  public class TokenInfo
     2     {
     3         public Guid tokenId { get; set; }
     4 
     5         public DateTime CreateTime { get; set; }
     6 
     7         public DateTime ActivityTime { get; set; }
     8 
     9         public string RemoteIp { get; set; }
    10 
    11         public string UserId { get; set; }
    12 
    13         public string UserName { get; set; }
    14 
    15         public bool IsValid { get; set; }
    16     }
    View Code

    参考资料:

    编写你自己的单点登录(SSO)服务

    http://blog.csdn.net/javachannel/article/details/752437

  • 相关阅读:
    24-反转链表
    23-链表中环的入口节点
    22-链表中倒数第k个节点
    21-调整数组顺序使奇数位于偶数前面
    18-删除链表的节点
    17-打印从1到最大的n位数
    16-数值的整数次方
    15-二进制中1的个数
    14-剪绳子
    13-机器人的运动范围
  • 原文地址:https://www.cnblogs.com/kakake/p/3976083.html
Copyright © 2020-2023  润新知