關于單點認證的一個簡單實現(結合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

posted on 2007-05-21 09:32 junky 閱讀(528) 評論(0) 編輯 收藏 所屬分類: security