ASP.NET实现QQ、微信、新浪微博OAuth2.0授权登录 原创
不管是腾讯还是新浪,查看他们的API,PHP都是有完整的接口,但对C#支持似乎都不是那么完善,都没有,腾讯是完全没有,新浪是提供第三方的,而且后期还不一定升级,NND,用第三方的动辄就一个类库,各种配置还必须按照他们约定的写,烦而且乱,索性自己写,后期的扩展也容易,看过接口后,开始以为很难,参考了几个源码之后发现也不是那么难,无非是GET或POST请求他们的接口获取返回值之类的,话不多说,这里只提供几个代码共参考,抛砖引玉了。。。
我这个写法的特点是,用到了Session,使用对象实例化之后调用Login()跳转到登录页面,在回调页面调用Callback()执行之后,可以从Session也可以写独立的函数(如:GetOpenID())中获取access_token或用户的唯一标识,以方便做下一步的操作。所谓绑定就是把用户的唯一标识取出,插入数据库,和帐号绑定起来。
1.首先是所有OAuth类的基类,放一些需要公用的方法
publicabstractclassBaseOAuth { publicHttpRequestRequest=HttpContext.Current.Request; publicHttpResponseResponse=HttpContext.Current.Response; publicHttpSessionStateSession=HttpContext.Current.Session; publicabstractvoidLogin(); publicabstractstringCallback(); #region内部使用函数 ///<summary> ///生成唯一随机串防CSRF攻击 ///</summary> ///<returns></returns> protectedstringGetStateCode() { Randomrand=newRandom(); stringdata=DateTime.Now.ToString("yyyyMMddHHmmssffff")+rand.Next(1,0xf423f).ToString(); MD5CryptoServiceProvidermd5=newMD5CryptoServiceProvider(); byte[]md5byte=md5.ComputeHash(UTF8Encoding.Default.GetBytes(data)); returnBitConverter.ToString(md5byte).Replace("-",""); } ///<summary> ///GET请求 ///</summary> ///<paramname="url"></param> ///<returns></returns> protectedstringGetRequest(stringurl) { HttpWebRequesthttpWebRequest=System.Net.WebRequest.Create(url)asHttpWebRequest; httpWebRequest.Method="GET"; httpWebRequest.ServicePoint.Expect100Continue=false; StreamReaderresponseReader=null; stringresponseData; try { responseReader=newStreamReader(httpWebRequest.GetResponse().GetResponseStream()); responseData=responseReader.ReadToEnd(); } finally { httpWebRequest.GetResponse().GetResponseStream().Close(); responseReader.Close(); } returnresponseData; } ///<summary> ///POST请求 ///</summary> ///<paramname="url"></param> ///<paramname="postData"></param> ///<returns></returns> protectedstringPostRequest(stringurl,stringpostData) { HttpWebRequesthttpWebRequest=System.Net.WebRequest.Create(url)asHttpWebRequest; httpWebRequest.Method="POST"; httpWebRequest.ServicePoint.Expect100Continue=false; httpWebRequest.ContentType="application/x-www-form-urlencoded"; //写入POST参数 StreamWriterrequestWriter=newStreamWriter(httpWebRequest.GetRequestStream()); try { requestWriter.Write(postData); } finally { requestWriter.Close(); } //读取请求后的结果 StreamReaderresponseReader=null; stringresponseData; try { responseReader=newStreamReader(httpWebRequest.GetResponse().GetResponseStream()); responseData=responseReader.ReadToEnd(); } finally { httpWebRequest.GetResponse().GetResponseStream().Close(); responseReader.Close(); } returnresponseData; } ///<summary> ///解析JSON ///</summary> ///<paramname="strJson"></param> ///<returns></returns> protectedNameValueCollectionParseJson(stringstrJson) { NameValueCollectionmc=newNameValueCollection(); Regexregex=newRegex(@"(\s*\""?([^""]*)\""?\s*\:\s*\""?([^""]*)\""?\,?)"); strJson=strJson.Trim(); if(strJson.StartsWith("{")) { strJson=strJson.Substring(1,strJson.Length-2); } foreach(Matchminregex.Matches(strJson)) { mc.Add(m.Groups[2].Value,m.Groups[3].Value); } returnmc; } ///<summary> ///解析URL ///</summary> ///<paramname="strParams"></param> ///<returns></returns> protectedNameValueCollectionParseUrlParameters(stringstrParams) { NameValueCollectionnc=newNameValueCollection(); foreach(stringpinstrParams.Split('&')) { string[]ps=p.Split('='); nc.Add(ps[0],ps[1]); } returnnc; } #endregion }
2.QQ的OAuth类
publicclassQQOAuth:BaseOAuth { publicstringAppId=ConfigurationManager.AppSettings["OAuth_QQ_AppId"]; publicstringAppKey=ConfigurationManager.AppSettings["OAuth_QQ_AppKey"]; publicstringRedirectUrl=ConfigurationManager.AppSettings["OAuth_QQ_RedirectUrl"]; publicconststringGET_AUTH_CODE_URL="https://graph.qq.com/oauth2.0/authorize"; publicconststringGET_ACCESS_TOKEN_URL="https://graph.qq.com/oauth2.0/token"; publicconststringGET_OPENID_URL="https://graph.qq.com/oauth2.0/me"; ///<summary> ///QQ登录,跳转到登录页面 ///</summary> publicoverridevoidLogin() { //-------生成唯一随机串防CSRF攻击 stringstate=GetStateCode(); Session["QC_State"]=state;//state放入Session stringparms="?response_type=code&" +"client_id="+AppId+"&redirect_uri="+Uri.EscapeDataString(RedirectUrl)+"&state="+state; stringurl=GET_AUTH_CODE_URL+parms; Response.Redirect(url);//跳转到登录页面 } ///<summary> ///QQ回调函数 ///</summary> ///<paramname="code"></param> ///<paramname="state"></param> ///<returns></returns> publicoverridestringCallback() { stringcode=Request.QueryString["code"]; stringstate=Request.QueryString["state"]; //--------验证state防止CSRF攻击 if(state!=(string)Session["QC_State"]) { ShowError("30001"); } stringparms="?grant_type=authorization_code&" +"client_id="+AppId+"&redirect_uri="+Uri.EscapeDataString(RedirectUrl) +"&client_secret="+AppKey+"&code="+code; stringurl=GET_ACCESS_TOKEN_URL+parms; stringstr=GetRequest(url); if(str.IndexOf("callback")!=-1) { intlpos=str.IndexOf("("); intrpos=str.IndexOf(")"); str=str.Substring(lpos+1,rpos-lpos-1); NameValueCollectionmsg=ParseJson(str); if(!string.IsNullOrEmpty(msg["error"])) { ShowError(msg["error"],msg["error_description"]); } } NameValueCollectiontoken=ParseUrlParameters(str); Session["QC_AccessToken"]=token["access_token"];//access_token放入Session returntoken["access_token"]; } ///<summary> ///使用AccessToken来获取用户的OpenID ///</summary> ///<paramname="accessToken"></param> ///<returns></returns> publicstringGetOpenID() { stringparms="?access_token="+Session["QC_AccessToken"]; stringurl=GET_OPENID_URL+parms; stringstr=GetRequest(url); if(str.IndexOf("callback")!=-1) { intlpos=str.IndexOf("("); intrpos=str.IndexOf(")"); str=str.Substring(lpos+1,rpos-lpos-1); } NameValueCollectionuser=ParseJson(str); if(!string.IsNullOrEmpty(user["error"])) { ShowError(user["error"],user["error_description"]); } Session["QC_OpenId"]=user["openid"];//openid放入Session returnuser["openid"]; } ///<summary> ///显示错误信息 ///</summary> ///<paramname="code">错误编号</param> ///<paramname="description">错误描述</param> privatevoidShowError(stringcode,stringdescription=null) { if(description==null) { switch(code) { case"20001": description="<h2>配置文件损坏或无法读取,请检查web.config</h2>"; break; case"30001": description="<h2>Thestatedoesnotmatch.YoumaybeavictimofCSRF.</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类
publicclassSinaOAuth:BaseOAuth { publicstringAppKey=ConfigurationManager.AppSettings["OAuth_Sina_AppKey"]; publicstringAppSecret=ConfigurationManager.AppSettings["OAuth_Sina_AppSecret"]; publicstringRedirectUrl=ConfigurationManager.AppSettings["OAuth_Sina_RedirectUrl"]; publicconststringGET_AUTH_CODE_URL="https://api.weibo.com/oauth2/authorize"; publicconststringGET_ACCESS_TOKEN_URL="https://api.weibo.com/oauth2/access_token"; publicconststringGET_UID_URL="https://api.weibo.com/2/account/get_uid.json"; ///<summary> ///新浪微博登录,跳转到登录页面 ///</summary> publicoverridevoidLogin() { //-------生成唯一随机串防CSRF攻击 stringstate=GetStateCode(); Session["Sina_State"]=state;//state放入Session stringparms="?client_id="+AppKey+"&redirect_uri="+Uri.EscapeDataString(RedirectUrl) +"&state="+state; stringurl=GET_AUTH_CODE_URL+parms; Response.Redirect(url);//跳转到登录页面 } ///<summary> ///新浪微博回调函数 ///</summary> ///<returns></returns> publicoverridestringCallback() { stringcode=Request.QueryString["code"]; stringstate=Request.QueryString["state"]; //--------验证state防止CSRF攻击 if(state!=(string)Session["Sina_State"]) { ShowError("Thestatedoesnotmatch.YoumaybeavictimofCSRF."); } stringparms="client_id="+AppKey+"&client_secret="+AppSecret +"&grant_type=authorization_code&code="+code+"&redirect_uri="+Uri.EscapeDataString(RedirectUrl); stringstr=PostRequest(GET_ACCESS_TOKEN_URL,parms); NameValueCollectionuser=ParseJson(str); Session["Sina_AccessToken"]=user["access_token"];//access_token放入Session Session["Sina_UId"]=user["uid"];//uid放入Session returnuser["access_token"]; } ///<summary> ///显示错误信息 ///</summary> ///<paramname="description">错误描述</param> privatevoidShowError(stringdescription=null) { Response.Write("<h2>"+description+"</h2>"); Response.End(); } }
4.微信的OAuth类
publicclassWeixinOAuth:BaseOAuth { publicstringAppId=ConfigurationManager.AppSettings["OAuth_Weixin_AppId"]; publicstringAppSecret=ConfigurationManager.AppSettings["OAuth_Weixin_AppSecret"]; publicstringRedirectUrl=ConfigurationManager.AppSettings["OAuth_Weixin_RedirectUrl"]; publicconststringGET_AUTH_CODE_URL="https://open.weixin.qq.com/connect/qrconnect"; publicconststringGET_ACCESS_TOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token"; publicconststringGET_USERINFO_URL="https://api.weixin.qq.com/sns/userinfo"; ///<summary> ///微信登录,跳转到登录页面 ///</summary> publicoverridevoidLogin() { //-------生成唯一随机串防CSRF攻击 stringstate=GetStateCode(); Session["Weixin_State"]=state;//state放入Session stringparms="?appid="+AppId +"&redirect_uri="+Uri.EscapeDataString(RedirectUrl)+"&response_type=code&scope=snsapi_login" +"&state="+state+"#wechat_redirect"; stringurl=GET_AUTH_CODE_URL+parms; Response.Redirect(url);//跳转到登录页面 } ///<summary> ///微信回调函数 ///</summary> ///<paramname="code"></param> ///<paramname="state"></param> ///<returns></returns> publicoverridestringCallback() { stringcode=Request.QueryString["code"]; stringstate=Request.QueryString["state"]; //--------验证state防止CSRF攻击 if(state!=(string)Session["Weixin_State"]) { ShowError("30001"); } stringparms="?appid="+AppId+"&secret="+AppSecret +"&code="+code+"&grant_type=authorization_code"; stringurl=GET_ACCESS_TOKEN_URL+parms; stringstr=GetRequest(url); NameValueCollectionmsg=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 returnmsg["access_token"]; } ///<summary> ///显示错误信息 ///</summary> ///<paramname="code">错误编号</param> ///<paramname="description">错误描述</param> privatevoidShowError(stringcode,stringdescription=null) { if(description==null) { switch(code) { case"20001": description="<h2>配置文件损坏或无法读取,请检查web.config</h2>"; break; case"30001": description="<h2>Thestatedoesnotmatch.YoumaybeavictimofCSRF.</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配置信息
<appSettings> <!--QQ登录相关配置--> <addkey="OAuth_QQ_AppId"value="123456789"/> <addkey="OAuth_QQ_AppKey"value="25f9e794323b453885f5181f1b624d0b"/> <addkey="OAuth_QQ_RedirectUrl"value="http://www.domain.com/oauth20/qqcallback.aspx"/> <!--新浪微博登录相关配置--> <addkey="OAuth_Sina_AppKey"value="123456789"/> <addkey="OAuth_Sina_AppSecret"value="25f9e794323b453885f5181f1b624d0b"/> <addkey="OAuth_Sina_RedirectUrl"value="http://www.domain.com/oauth20/sinacallback.aspx"/> <!--微信登录相关配置--> <addkey="OAuth_Weixin_AppId"value="wx123456789123"/> <addkey="OAuth_Weixin_AppSecret"value="25f9e794323b453885f5181f1b624d0b"/> <addkey="OAuth_Weixin_RedirectUrl"value="http://www.domain.com/oauth20/weixincallback.aspx"/> </appSettings>