关于单点认证的一个简单实现(结合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

2

3

4

5

6

7

8

9



10


11

12

13

14



15

16



17



18

19

20


21

22

23



24

25

26

27

28



29

30

31

32

33

34



35

36

37

38

39

40

41

42

43

44

45

46



47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65



66

67

68

69

70

71

72

73



74

75

76

77


78

79

80

81

82



83

84

85

86

87

88

89

90

91



92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113



114

115

116

117

118

119



120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

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

2

3

4

5

6

7

8

9

10

11



12

13



14

15

16



17

18



19

20

21

22

23

24

25

26

27

28



29

30

31

32

33

34

35



36

37

38

39

40

41

42

43



44

45

46

47

48

49

50

51



52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

被认证的网站的WebConfig文件






















































认证网站,比较简单,有一个负责登陆的页面,我就在上面放了一个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

2

3

4

5

6

7

8

9

10

11

12



13

14



15

16



17

18

19



20

21

22

23

24

25

26

27

28



29

30

31

32

33

34

35


36

37

38

39

40



41

42

43

44

45

46

47

48

49

50

51

52


53

54

55

56



57

58

59

60

61



62

63

64

65

66

67

68

69

70


71

72



73

74

75

76

77

78

79



80

81

82

83

84

85

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

2

3

4

5

6

7



8

9



10

11

12


13

14

15

16

17

18



19

20



21

22

23

24

25

26

27

28

29

30

31

32

33

34



35

36

37

38

39

40



41

42



43

44

45

46

47

48

49

50

51



52

53

54

55

56

57

58

59
