关于单点认证的一个简单实现(结合Form认证)

关于单点认证的一个简单实现(结合Form认证)(转)

最近在做一个无敌长的项目,从五一休假做到十一休假!有一个需求就是要求单点登陆(SSO)


解决思路如下:

请求认证的网站 :用一个HttpModule 截取所有请求,判断HttpContext.User是不是Null,如为空,判断Url上是不是有签名过的认证信息, 如果有,判断签名信息是否有效,如果有效,将认证信息写入Cookie中.认证完成

认证的网站: 如果登陆页Url中有要求认证的网址,判断用户是不是已授权,如果已授权,将用户信息签名,写入Url中

二个网站都使用的Form认证

代码
请求认证网站,HttpMoudle如下

  1 using  System;
  2 using  System.Collections.Generic;
  3 using  System.Text;
  4 using  System.Web;
  5 using  System.Web.Security;
  6 using  System.Security.Principal;
  7
  8 namespace  SSO
  9 {
 10    /**//// <summary>
 11    /// 单点认证的HttpModule
 12    /// </summary>

 13    public class SSOAuthenticateHttpModule:IHttpModule
 14    {
 15        public String ModuleName
 16        {
 17            get return "SSOAuthenticateHttpModule"; }
 18        }

 19
 20        IHttpModule 成员#region IHttpModule 成员
 21
 22        public void Dispose()
 23        {
 24            
 25        }

 26
 27        public void Init(HttpApplication context)
 28        {
 29            context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);
 30            context.BeginRequest += new EventHandler(context_BeginRequest);
 31        }

 32
 33        void context_BeginRequest(object sender, EventArgs e)
 34        {
 35            HttpApplication application = (HttpApplication)sender;
 36
 37            HttpContext context = application.Context;
 38
 39            HttpResponse Response = context.Response;
 40            Response.AddHeader("P3P""CP=CAO PSA OUR");//加上这个,防止在Iframe的时间Cookie丢失
 41        }

 42
 43
 44
 45        void context_AuthenticateRequest(object sender, EventArgs e)
 46        {
 47
 48
 49            HttpApplication application = (HttpApplication)sender;
 50
 51            HttpContext context = application.Context;
 52
 53            HttpRequest Request = context.Request;
 54            HttpResponse Response = context.Response;
 55
 56            //如果不是网页,就不管他了
 57            if(!Request.Url.AbsolutePath.EndsWith(".aspx",StringComparison.OrdinalIgnoreCase))
 58                return ;
 59
 60            //好像加不加都行
 61            FormsAuthentication.Initialize();
 62
 63            //如果当前用户为空
 64            if (context.User == null)
 65            {
 66                //s表示签名后的信息
 67                String s = Request.QueryString["s"]; 
 68                //表示真正要传的信息 如果需要,可以把此部分信息也加密
 69                String v =  application.Server.UrlDecode(Request.QueryString["v"]);
 70
 71
 72                if (!String.IsNullOrEmpty(s) && !String.IsNullOrEmpty(v))
 73                {
 74                    //UrlDecode会把 + 号变成 '' 不知道为啥,这里再换回来,
 75                    s = s.Replace(' ''+');
 76
 77                    通过之前存的Cookie判断是不是最近的验证信息,防止别人Copy Url 地址#region 通过之前存的Cookie判断是不是最近的验证信息,防止别人Copy Url 地址
 78                      HttpCookie ticksCookie = application.Request.Cookies["Ticks"];
 79                    String AuthTicks = String.Empty;
 80
 81                    if (ticksCookie != null)
 82                    {
 83                        AuthTicks = ticksCookie.Value;
 84                    }

 85                    //加到认证信上,签名过的信息包含此内容,只是在V中没有传过来
 86                    v = v + "$" + AuthTicks;
 87                    #endregion

 88                  
 89                    //判断签名
 90                    if (SSOClient.ValidataData(v, s))
 91                    {
 92                        //取出用户
 93                        String userName = SSOClient.SplitUserName(v);
 94                        //Token信息
 95                        String tokenValue = SSOClient.SplitToken(v);
 96
 97                         FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
 98                            1// Ticket version
 99                            userName, // Username associated with ticket
100                            DateTime.Now, // Date/time issued
101                            DateTime.Now.AddMinutes(30), // Date/time to expire
102                            false// "true" for a persistent user cookie
103                            tokenValue , // User-data, in this case the roles
104                            FormsAuthentication.FormsCookiePath);// Path cookie valid for
105
106
107                        //写入自己的Cookie和用户信息
108                         context.User = new GenericPrincipal(new FormsIdentity(ticket), new String[1]);
109                        SSOClient.SetAuthCookie(context, userName, tokenValue, false);
110    
111                    }

112                    else
113                    {
114                        //签名无效,重定向到登陆页
115                        Response.Redirect(FormsAuthentication.LoginUrl); 
116                    }

117                }

118                else
119                {
120                    //在这里存一个Cookie信息,在验证后,由Server传回,以判断是不是自己发出的请求
121                    String ticks = System.DateTime.Now.Ticks.ToString();
122                    HttpCookie cookie = new HttpCookie("Ticks",ticks);
123
124                    application.Response.Cookies.Add(cookie);
125
126                    //请服务端认证
127                    Response.Redirect(FormsAuthentication.LoginUrl + "?site=" + HttpUtility.UrlEncode(Request.Url.ToString()) + "&ticks=" + ticks);
128                }

129
130            }

131           
132        }

133
134        #endregion

135    }

136}

137


SSOClient 是一个助手类主要负责认证签名,设置Cookie,从Url中分离出认证信息
 1 using  System;
 2 using  System.Collections.Generic;
 3 using  System.Text;
 4 using  System.Security.Cryptography;
 5 using  System.Security.Principal;
 6 using  System.Web;
 7 using  System.Web.Security;
 8
 9
10 namespace  SSO
11 {
12    internal class SSOClient
13    {
14        private static String PublicKey = "公钥信息,我用的是RSA,你可以自己生成"
15        internal static bool ValidataData(String data, String signedData)
16        {
17            try
18            {
19                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(512);
20                RSA.FromXmlString(PublicKey);
21
22                UnicodeEncoding ByteConverter = new UnicodeEncoding();
23
24                SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
25                return RSA.VerifyData(ByteConverter.GetBytes(data), new SHA1CryptoServiceProvider(), Convert.FromBase64String(signedData));
26            }

27            catch
28            {
29                return false;
30            }

31
32        }

33
34        internal static String SplitUserName(String data)
35        {
36            UnicodeEncoding ByteConverter = new UnicodeEncoding();
37
38            return data.Split('$')[0];
39        }

40
41
42        internal static String SplitToken(String data)
43        {
44            UnicodeEncoding ByteConverter = new UnicodeEncoding();
45
46
47            return data.Split('$')[1];
48        }

49
50        internal static void SetAuthCookie(HttpContext context, String userName, String token, bool isPersistent)
51        {
52
53
54            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
55            1// Ticket version
56            userName, // Username associated with ticket
57            DateTime.Now, // Date/time issued
58            DateTime.Now.AddMinutes(30), // Date/time to expire
59            isPersistent, // "true" for a persistent user cookie
60            token, // User-data, in this case the roles
61            FormsAuthentication.FormsCookiePath);// Path cookie valid for
62
63            // Encrypt the cookie using the machine key for secure transport
64            string hash = FormsAuthentication.Encrypt(ticket);
65
66            HttpCookie cookie = new HttpCookie(
67               FormsAuthentication.FormsCookieName, // Name of auth cookie
68               hash); // Hashed ticket
69
70            // Set the cookie's expiration time to the tickets expiration time
71            if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
72
73            // Add the cookie to the list for outgoing response
74            context.Response.Cookies.Add(cookie);
75        }

76
77
78    }

79}

80

被认证的网站的WebConfig文件
<? xml version="1.0" ?>
<!--  
    注意: 除了手动编辑此文件以外,您还可以使用 
    Web 管理工具来配置应用程序的设置。可以使用 Visual Studio 中的
     “网站”->“Asp.Net 配置”选项。
    设置和注释的完整列表在 
    machine.config.comments 中,该文件通常位于 
    \Windows\Microsoft.Net\Framework\v2.x\Config 中
-->
< configuration >
    
< appSettings />
    
< connectionStrings />
    
< system .web >
        
<!--  
            设置 compilation debug="true" 将调试符号插入
            已编译的页面中。但由于这会 
            影响性能,因此只在开发过程中将此值 
            设置为 true。
        
-->
        
< compilation  debug ="true" >
            
< assemblies >
                
< add  assembly ="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" /></ assemblies ></ compilation >
        
<!--
            通过 <authentication> 节可以配置 ASP.NET 使用的 
            安全身份验证模式,
            以标识传入的用户。 
        
-->
    
< authentication  mode ="Forms" >
      
< forms  loginUrl ="http://localhost/TestSSOServer/Default.aspx"  name =".ASPXFORMSAUTH"  protection ="All"  path ="/"  timeout ="30"  enableCrossAppRedirects ="true" />
    
</ authentication >
      
        
<!-- <authorization>
            <deny users="?"/>
        </authorization>
-->
            
        
        
< httpModules >
            
< add  name ="SSOAuthenticateHttpModule"  type ="SSO.SSOAuthenticateHttpModule" />
        
</ httpModules >
        
<!--
            如果在执行请求的过程中出现未处理的错误,
            则通过 <customErrors> 节可以配置相应的处理步骤。具体说来,
            开发人员通过该节可以配置
            要显示的 html 错误页
            以代替错误堆栈跟踪。

        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
            <error statusCode="403" redirect="NoAccess.htm" />
            <error statusCode="404" redirect="FileNotFound.htm" />
        </customErrors>
        
-->
    
</ system.web >
</ configuration >


认证网站,比较简单,有一个负责登陆的页面,我就在上面放了一个Button和一个TxtBox
 1 using  System;
 2 using  System.Data;
 3 using  System.Configuration;
 4 using  System.Web;
 5 using  System.Web.Security;
 6 using  System.Web.UI;
 7 using  System.Web.UI.WebControls;
 8 using  System.Web.UI.WebControls.WebParts;
 9 using  System.Web.UI.HtmlControls;
10
11 public  partial  class  _Default : System.Web.UI.Page 
12 {
13    protected void Page_Load(object sender, EventArgs e)
14    {
15        if (!Page.IsPostBack)
16        {   
17            //判断是不是已认证通过--这里只是简单写了,具体你是怎么认证的,就怎么写
18            if (Request.IsAuthenticated)
19            {
20                //回到请求认证的网站;
21                ReturnUrl();
22            }

23        
24        }

25        
26    }

27    protected void Button1_Click(object sender, EventArgs e)
28    {
29        FormsAuthentication.SetAuthCookie(TextBox1.Text,false);
30
31        ReturnUrl();
32
33    }

34
35    /**//// <summary>
36    /// 取得认证信息
37    /// </summary>
38    /// <returns></returns>

39    private String getAuthInfo()
40    {
41        String userName = User.Identity.Name;
42        String tokenValue = "这是一些附加信息,你可以写入角色什么的";//在我的应用中,我写的是一个Token
43        String time = System.DateTime.Now.ToString();
44
45        String v = userName + "$" + tokenValue ;
46
47        return v;
48
49
50    }

51
52    /**//// <summary>
53    /// 返回请求认证的网站
54    /// </summary>

55    private void ReturnUrl()
56    {
57     
58        String strUrl = Request.QueryString["site"];
59
60        if (String.IsNullOrEmpty(strUrl))
61        {
62            return;
63        }

64
65        strUrl = HttpUtility.UrlDecode(strUrl);
66
67        //取得认证信息
68        String data = getAuthInfo();
69
70        写入签名信息#region 写入签名信息
71        if (strUrl.IndexOf('?'== -1)
72        {
73
74            strUrl = strUrl + "?s=" + SSO.SSOServer.SignatueData(data + "$" + Request.QueryString["ticks"]);
75            strUrl = strUrl + "&v=" + Server.UrlEncode(data);
76
77        }

78        else
79        {
80            strUrl = strUrl + "&s=" + SSO.SSOServer.SignatueData(data + "$" + Request.QueryString["ticks"]);
81            strUrl = strUrl + "&v=" + Server.UrlEncode(data);
82        }

83        #endregion

84
85        Response.Redirect(strUrl);
86  
87    
88    }

89}

90


认证助手类
 1 using  System;
 2 using  System.Collections.Generic;
 3 using  System.Text;
 4 using  System.Security.Cryptography;
 5
 6 namespace  SSO
 7 {
 8    public class SSOServer
 9    {
10        private static String PrivateKey = "私钥信息,这个很重要,";
11        
12        /**//// <summary>
13        /// 签 名用户信息
14        /// </summary>
15        /// <param name="data"></param>
16        /// <returns></returns>

17        public static String SignatueData(String data)
18        {
19            try
20            {
21                //Create a UnicodeEncoder to convert between byte array and string.
22                UnicodeEncoding ByteConverter = new UnicodeEncoding();
23
24                byte[] dataToSignatue = ByteConverter.GetBytes(data);
25
26                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(512);
27                RSA.FromXmlString(PrivateKey);
28
29                String SignadString = Convert.ToBase64String(RSA.SignData(dataToSignatue, new SHA1CryptoServiceProvider()));
30                return SignadString;
31
32            }

33            catch
34            {
35                return String.Empty;
36            }

37        }

38        //把一个字符串Base64编码
39        public static String Base64String(String data)
40        {
41            try
42            {
43                //Create a UnicodeEncoder to convert between byte array and string.
44                UnicodeEncoding ByteConverter = new UnicodeEncoding();
45
46                byte[] dataToBase = ByteConverter.GetBytes(data);
47
48                return Convert.ToBase64String(dataToBase);
49            }

50            catch
51            {
52                return String.Empty;
53            }

54            
55        }

56
57    }

58}

59

你可能感兴趣的:(关于单点认证的一个简单实现(结合Form认证))