不管是腾讯还是新浪,查看他们的API,PHP都是有完整的接口,但对C#支持似乎都不是那么完善,都没有,腾讯是完全没有,新浪是提供第三方的,而且后期还不一定升级,NND,用第三方的动辄就一个类库,各种配置还必须按照他们约定的写,烦而且乱,索性自己写,后期的扩展也容易,看过接口后,开始以为很难,参考了几个源码之后发现也不是那么难,无非是GET或POST请求他们的接口获取返回值之类的,话不多说,这里只提供几个代码共参考,抛砖引玉了。。。
我这个写法的特点是,用到了Session,使用对象实例化之后调用 Login() 跳转到登录页面,在回调页面调用Callback() 执行之后,可以从Session也可以写独立的函数(如:GetOpenID())中获取access_token或用户的唯一标识,以方便做下一步的操作。所谓绑定就是把用户的唯一标识取出,插入数据库,和帐号绑定起来。
1.首先是所有OAuth类的基类,放一些需要公用的方法
1
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
|
public abstract class BaseOAuth { public HttpRequest Request = HttpContext.Current.Request; public HttpResponse Response = HttpContext.Current.Response; public HttpSessionState Session = HttpContext.Current.Session; public abstract void Login(); public abstract string Callback(); #region 内部使用函数 /// <summary> /// 生成唯一随机串防CSRF攻击 /// </summary> /// <returns></returns> protected string GetStateCode() { Random rand = new Random(); string data = DateTime.Now.ToString( "yyyyMMddHHmmssffff" ) + rand.Next(1, 0xf423f).ToString(); MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); byte [] md5byte = md5.ComputeHash(UTF8Encoding.Default.GetBytes(data)); return BitConverter.ToString(md5byte).Replace( "-" , "" ); } /// <summary> /// GET请求 /// </summary> /// <param name="url"></param> /// <returns></returns> protected string GetRequest( string url) { HttpWebRequest httpWebRequest = System.Net.WebRequest.Create(url) as HttpWebRequest; httpWebRequest.Method = "GET" ; httpWebRequest.ServicePoint.Expect100Continue = false ; StreamReader responseReader = null ; string responseData; try { responseReader = new StreamReader(httpWebRequest.GetResponse().GetResponseStream()); responseData = responseReader.ReadToEnd(); } finally { httpWebRequest.GetResponse().GetResponseStream().Close(); responseReader.Close(); } return responseData; } /// <summary> /// POST请求 /// </summary> /// <param name="url"></param> /// <param name="postData"></param> /// <returns></returns> protected string PostRequest( string url, string postData) { HttpWebRequest httpWebRequest = System.Net.WebRequest.Create(url) as HttpWebRequest; httpWebRequest.Method = "POST" ; httpWebRequest.ServicePoint.Expect100Continue = false ; httpWebRequest.ContentType = "application/x-www-form-urlencoded" ; //写入POST参数 StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream()); try { requestWriter.Write(postData); } finally { requestWriter.Close(); } //读取请求后的结果 StreamReader responseReader = null ; string responseData; try { responseReader = new StreamReader(httpWebRequest.GetResponse().GetResponseStream()); responseData = responseReader.ReadToEnd(); } finally { httpWebRequest.GetResponse().GetResponseStream().Close(); responseReader.Close(); } return responseData; } /// <summary> /// 解析JSON /// </summary> /// <param name="strJson"></param> /// <returns></returns> protected NameValueCollection ParseJson( string strJson) { NameValueCollection mc = new NameValueCollection(); Regex regex = new Regex( @"(\s*\""?([^""]*)\""?\s*\:\s*\""?([^""]*)\""?\,?)" ); strJson = strJson.Trim(); if (strJson.StartsWith( "{" )) { strJson = strJson.Substring(1, strJson.Length - 2); } foreach (Match m in regex.Matches(strJson)) { mc.Add(m.Groups[2].Value, m.Groups[3].Value); } return mc; } /// <summary> /// 解析URL /// </summary> /// <param name="strParams"></param> /// <returns></returns> protected NameValueCollection ParseUrlParameters( string strParams) { NameValueCollection nc = new NameValueCollection(); foreach ( string p in strParams.Split( '&' )) { string [] ps = p.Split( '=' ); nc.Add(ps[0], ps[1]); } return nc; } #endregion } |
2.QQ的OAuth类
1
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
|
public class QQOAuth : BaseOAuth { public string AppId = ConfigurationManager.AppSettings[ "OAuth_QQ_AppId" ]; public string AppKey = ConfigurationManager.AppSettings[ "OAuth_QQ_AppKey" ]; public string RedirectUrl = ConfigurationManager.AppSettings[ "OAuth_QQ_RedirectUrl" ]; public const string GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize" ; public const string GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token" ; public const string GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me" ; /// <summary> /// QQ登录,跳转到登录页面 /// </summary> public override void Login() { //-------生成唯一随机串防CSRF攻击 string state = GetStateCode(); Session[ "QC_State" ] = state; //state 放入Session string parms = "?response_type=code&" + "client_id=" + AppId + "&redirect_uri=" + Uri.EscapeDataString(RedirectUrl) + "&state=" + state; string url = GET_AUTH_CODE_URL + parms; Response.Redirect(url); //跳转到登录页面 } /// <summary> /// QQ回调函数 /// </summary> /// <param name="code"></param> /// <param name="state"></param> /// <returns></returns> public override string Callback() { string code = Request.QueryString[ "code" ]; string state = Request.QueryString[ "state" ]; //--------验证state防止CSRF攻击 if (state != ( string )Session[ "QC_State" ]) { ShowError( "30001" ); } string parms = "?grant_type=authorization_code&" + "client_id=" + AppId + "&redirect_uri=" + Uri.EscapeDataString(RedirectUrl) + "&client_secret=" + AppKey + "&code=" + code; string url = GET_ACCESS_TOKEN_URL + parms; string str = GetRequest(url); if (str.IndexOf( "callback" ) != -1) { int lpos = str.IndexOf( "(" ); int rpos = str.IndexOf( ")" ); str = str.Substring(lpos + 1, rpos - lpos - 1); NameValueCollection msg = ParseJson(str); if (! string .IsNullOrEmpty(msg[ "error" ])) { ShowError(msg[ "error" ], msg[ "error_description" ]); } } NameValueCollection token = ParseUrlParameters(str); Session[ "QC_AccessToken" ] = token[ "access_token" ]; //access_token 放入Session return token[ "access_token" ]; } /// <summary> /// 使用Access Token来获取用户的OpenID /// </summary> /// <param name="accessToken"></param> /// <returns></returns> public string GetOpenID() { string parms = "?access_token=" + Session[ "QC_AccessToken" ]; string url = GET_OPENID_URL + parms; string str = GetRequest(url); if (str.IndexOf( "callback" ) != -1) { int lpos = str.IndexOf( "(" ); int rpos = str.IndexOf( ")" ); str = str.Substring(lpos + 1, rpos - lpos - 1); } NameValueCollection user = ParseJson(str); if (! string .IsNullOrEmpty(user[ "error" ])) { ShowError(user[ "error" ], user[ "error_description" ]); } Session[ "QC_OpenId" ] = user[ "openid" ]; //openid 放入Session return user[ "openid" ]; } /// <summary> /// 显示错误信息 /// </summary> /// <param name="code">错误编号</param> /// <param name="description">错误描述</param> private void ShowError( string code, string description = null ) { if (description == null ) { switch (code) { case "20001" : description = "<h2>配置文件损坏或无法读取,请检查web.config</h2>" ; break ; case "30001" : description = "<h2>The state does not match. You may be a victim of CSRF.</h2>" ; break ; case "50001" : description = "<h2>可能是服务器无法请求https协议</h2>可能未开启curl支持,请尝试开启curl支持,重启web服务器,如果问题仍未解决,请联系我们" ; break ; default : description = "<h2>系统未知错误,请联系我们</h2>" ; break ; } Response.Write(description); Response.End(); } else { Response.Write( "<h3>error:<h3>" + code + "<h3>msg:<h3>" + description); Response.End(); } } } |
3.新浪微博的OAuth类
1
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
|
public class SinaOAuth : BaseOAuth { public string AppKey = ConfigurationManager.AppSettings[ "OAuth_Sina_AppKey" ]; public string AppSecret = ConfigurationManager.AppSettings[ "OAuth_Sina_AppSecret" ]; public string RedirectUrl = ConfigurationManager.AppSettings[ "OAuth_Sina_RedirectUrl" ]; public const string GET_AUTH_CODE_URL = "https://api.weibo.com/oauth2/authorize" ; public const string GET_ACCESS_TOKEN_URL = "https://api.weibo.com/oauth2/access_token" ; public const string GET_UID_URL = "https://api.weibo.com/2/account/get_uid.json" ; /// <summary> /// 新浪微博登录,跳转到登录页面 /// </summary> public override void Login() { //-------生成唯一随机串防CSRF攻击 string state = GetStateCode(); Session[ "Sina_State" ] = state; //state 放入Session string parms = "?client_id=" + AppKey + "&redirect_uri=" + Uri.EscapeDataString(RedirectUrl) + "&state=" + state; string url = GET_AUTH_CODE_URL + parms; Response.Redirect(url); //跳转到登录页面 } /// <summary> /// 新浪微博回调函数 /// </summary> /// <returns></returns> public override string Callback() { string code = Request.QueryString[ "code" ]; string state = Request.QueryString[ "state" ]; //--------验证state防止CSRF攻击 if (state != ( string )Session[ "Sina_State" ]) { ShowError( "The state does not match. You may be a victim of CSRF." ); } string parms = "client_id=" + AppKey + "&client_secret=" + AppSecret + "&grant_type=authorization_code&code=" + code + "&redirect_uri=" + Uri.EscapeDataString(RedirectUrl); string str = PostRequest(GET_ACCESS_TOKEN_URL, parms); NameValueCollection user = ParseJson(str); Session[ "Sina_AccessToken" ] = user[ "access_token" ]; //access_token 放入Session Session[ "Sina_UId" ] = user[ "uid" ]; //uid 放入Session return user[ "access_token" ]; } /// <summary> /// 显示错误信息 /// </summary> /// <param name="description">错误描述</param> private void ShowError( string description = null ) { Response.Write( "<h2>" + description + "</h2>" ); Response.End(); } } |
4.微信的OAuth类
1
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
|
public class WeixinOAuth : BaseOAuth { public string AppId = ConfigurationManager.AppSettings[ "OAuth_Weixin_AppId" ]; public string AppSecret = ConfigurationManager.AppSettings[ "OAuth_Weixin_AppSecret" ]; public string RedirectUrl = ConfigurationManager.AppSettings[ "OAuth_Weixin_RedirectUrl" ]; public const string GET_AUTH_CODE_URL = "https://open.weixin.qq.com/connect/qrconnect" ; public const string GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token" ; public const string GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo" ; /// <summary> /// 微信登录,跳转到登录页面 /// </summary> public override void Login() { //-------生成唯一随机串防CSRF攻击 string state = GetStateCode(); Session[ "Weixin_State" ] = state; //state 放入Session string parms = "?appid=" + AppId + "&redirect_uri=" + Uri.EscapeDataString(RedirectUrl) + "&response_type=code&scope=snsapi_login" + "&state=" + state + "#wechat_redirect" ; string url = GET_AUTH_CODE_URL + parms; Response.Redirect(url); //跳转到登录页面 } /// <summary> /// 微信回调函数 /// </summary> /// <param name="code"></param> /// <param name="state"></param> /// <returns></returns> public override string Callback() { string code = Request.QueryString[ "code" ]; string state = Request.QueryString[ "state" ]; //--------验证state防止CSRF攻击 if (state != ( string )Session[ "Weixin_State" ]) { ShowError( "30001" ); } string parms = "?appid=" + AppId + "&secret=" + AppSecret + "&code=" + code + "&grant_type=authorization_code" ; string url = GET_ACCESS_TOKEN_URL + parms; string str = GetRequest(url); NameValueCollection msg = ParseJson(str); if (! string .IsNullOrEmpty(msg[ "errcode" ])) { ShowError(msg[ "errcode" ], msg[ "errmsg" ]); } Session[ "Weixin_AccessToken" ] = msg[ "access_token" ]; //access_token 放入Session Session[ "Weixin_OpenId" ] = msg[ "openid" ]; //access_token 放入Session return msg[ "access_token" ]; } /// <summary> /// 显示错误信息 /// </summary> /// <param name="code">错误编号</param> /// <param name="description">错误描述</param> private void ShowError( string code, string description = null ) { if (description == null ) { switch (code) { case "20001" : description = "<h2>配置文件损坏或无法读取,请检查web.config</h2>" ; break ; case "30001" : description = "<h2>The state does not match. You may be a victim of CSRF.</h2>" ; break ; case "50001" : description = "<h2>接口未授权</h2>" ; break ; default : description = "<h2>系统未知错误,请联系我们</h2>" ; break ; } Response.Write(description); Response.End(); } else { Response.Write( "<h3>error:<h3>" + code + "<h3>msg:<h3>" + description); Response.End(); } } } |
5.web.config配置信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< appSettings > <!--QQ登录相关配置--> < add key = "OAuth_QQ_AppId" value = "123456789" /> < add key = "OAuth_QQ_AppKey" value = "25f9e794323b453885f5181f1b624d0b" /> < add key = "OAuth_QQ_RedirectUrl" value = "http://www.domain.com/oauth20/qqcallback.aspx" /> <!--新浪微博登录相关配置--> < add key = "OAuth_Sina_AppKey" value = "123456789" /> < add key = "OAuth_Sina_AppSecret" value = "25f9e794323b453885f5181f1b624d0b" /> < add key = "OAuth_Sina_RedirectUrl" value = "http://www.domain.com/oauth20/sinacallback.aspx" /> <!--微信登录相关配置--> < add key = "OAuth_Weixin_AppId" value = "wx123456789123" /> < add key = "OAuth_Weixin_AppSecret" value = "25f9e794323b453885f5181f1b624d0b" /> < add key = "OAuth_Weixin_RedirectUrl" value = "http://www.domain.com/oauth20/weixincallback.aspx" /> </ appSettings > |