那些年,我们开发的接口之:QQ登录(OAuth2.0)

时间:2022-12-13 00:28:47

那些年,我们开发的接口之:QQ登录(OAuth2.0)

吴剑 2013-06-14

原创文章,转载必须注明出处:http://www.cnblogs.com/wu-jian

前言

开发这些年,做过很多类型的接口。有对接保险公司的;有对接电信运营商的;有对接支付平台的;还有对接各个大小公司五花八门的接口。

最早大家用URL参数(当然现在也一直在用,因为这个最方便最轻量,并且是HTTP协议的一部分,具有高通用性);后来很多公司选择用XML来封装大一点的数据,封装数据逻辑;再后来通过接口传递的数据越来越复杂,于是有了在XML之上封装的SOAP;直到近些年,随着前端技术占据越来越重要的地位,JSON甚至脱离了JAVASCRIPT渗透到服务器端;最后,还有像GOOGLE这种追求极限的公司愿意在看似毫无技术含量的接口技术上花费人力财力,好比他们开源的Google Protocol Buffers,在传输和解析性能上比XML提升了一个数量级。

想起星爷电影里有句台词:能力越大责任也就越大。希望中国互联网的几位大佬不要光想着挣光中国网民的钱,而更应该为中国互联网的基础设施、中国互联网的生态环境尽自己该尽的责任。如果你们实在不愿意为中国互联网贡献啥,那也请你们也不要破坏啥吧。

OK,扯远了,《那些年,我们开发的接口》会是一个系列文章,后面也会陆续补充和完善,目前包含的目录如下:

那些年,我们开发的接口之:QQ登录(OAuth2.0)

那些年,我们开发的接口之:微信

那些年,我们开发的接口之:新浪微博(OAuth2.0)

QQ登录演示地址:www.paotiao.com

关于QQ登录

目前使用QQ登录后腾讯不允许马上让用户绑定网站帐号,这一点给网站带来了很大不便,至发文时止,该问题存在,不知后续腾讯是否会更进一步开放。

那些年,我们开发的接口之:QQ登录(OAuth2.0)

OAuth

那些年,我们开发的接口之:QQ登录(OAuth2.0)

OAuth(开放授权)是一个开放标准,由Twitter于2006年最早提出,得到各大网站广泛支持,如Google、Facebook、Microsoft等等。

它允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

关于OAuth的介绍:http://zh.wikipedia.org/wiki/OAuth

逻辑概述

那些年,我们开发的接口之:QQ登录(OAuth2.0)

准备工作

首先需要在QQ开放平台(http://open.qq.com/)上注册你的网站然后申请到APP ID与APP KEY,如下图所示:

那些年,我们开发的接口之:QQ登录(OAuth2.0)

临时令牌

临时令牌为一次性令牌,每次QQ登录第一步即申请一个临时令牌,同时它是一个异步接口(狭义),它的流程很单一,携带一堆参数,获取一个令牌。

那些年,我们开发的接口之:QQ登录(OAuth2.0)

请求:

using System;
using System.IO;
using System.Web;
using System.Collections.Generic; namespace WuJian.OAuth.Qq.Authorization
{
/// <summary>
/// 临时令牌(Authorization)请求
/// 注:临时令牌为一次性使用
/// 参考:http://wiki.open.qq.com/wiki/website/%E4%BD%BF%E7%94%A8Authorization_Code%E8%8E%B7%E5%8F%96Access_Token
/// </summary>
public class Request
{
#region 构造函数 /// <summary>
/// 构造函数
/// </summary>
/// <param name="state">
/// client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。
/// 请务必严格按照流程检查用户与state参数状态的绑定。
/// </param>
/// <param name="scopeArray">
/// 请求用户授权时向用户显示的可进行授权的列表。
/// 可填写的值是API文档中列出的接口,以及一些动作型的授权(目前仅有:do_like)
/// 不传则默认请求对接口get_user_info进行授权。
/// 建议控制授权项的数量,只传入必要的接口名称,因为授权项越多,用户越可能拒绝进行任何授权。
///</param>
///<param name="backURL">在回调地址中嵌入的backurl参数(传入前请进行UrlEncode)</param>
public Request(string state, string[] scopeArray = null, string backURL = "")
{
this.mUrl = Config.AuthorizationURI;
this.mClientID = Config.AppID;
this.mRedirectURI = Config.RedirectURI + "?backurl=" + backURL;
this.mState = state;
//默认为 get_user_info
if (scopeArray == null || scopeArray.Length == )
this.Scope = new string[] { APIs.get_user_info.ToString() };
else
this.mScope = scopeArray;
} #endregion #region 请求参数 private string mUrl;
private readonly string mResponseType = "code";
private string mClientID;
private string mRedirectURI;
private string mState;
private string[] mScope; /// <summary>
/// 接口请求地址(不包含参数)
/// 如:https://graph.qq.com/oauth2.0/authorize
/// </summary>
public string Url {
get { return this.mUrl; }
set { this.mUrl = value; }
} /// <summary>
/// 授权类型,此值固定为“code”。
/// </summary>
public string ResponseType {
get { return this.mResponseType; }
} /// <summary>
/// 申请QQ登录成功后,分配给应用的appid。
/// </summary>
public string ClientID {
get { return this.mClientID; }
set { this.mClientID = value; }
} /// <summary>
/// 成功授权后的回调地址,必须是注册appid时填写的主域名下的地址,建议设置为网站首页或网站的用户中心。
/// 注意需要将url进行URLEncode。
/// </summary>
public string RedirectURI {
get { return this.mRedirectURI; }
set { this.mRedirectURI = value; }
} /// <summary>
/// client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。
/// 请务必严格按照流程检查用户与state参数状态的绑定。
/// </summary>
public string State {
get { return this.mState; }
set { this.mState = value; }
} /// <summary>
/// 请求用户授权时向用户显示的可进行授权的列表。
/// 可填写的值是API文档中列出的接口,以及一些动作型的授权(目前仅有:do_like),如果要填写多个接口名称,请用逗号隔开。
/// 例如:scope=get_user_info,list_album,upload_pic,do_like
/// 不传则默认请求对接口get_user_info进行授权。
/// 建议控制授权项的数量,只传入必要的接口名称,因为授权项越多,用户越可能拒绝进行任何授权。
/// </summary>
public string[] Scope
{
get { return this.mScope; }
set { this.mScope = value; }
} #endregion #region 方法 /// <summary>
/// 获取请求URL(包含参数)
/// </summary>
public string GetUrl()
{
return string.Format("{0}?response_type={1}&client_id={2}&redirect_uri={3}&state={4}&scope={5}",
this.mUrl, this.mResponseType, this.mClientID, System.Web.HttpUtility.UrlEncode(this.mRedirectURI),
this.mState, string.Join(",", this.mScope));
} /// <summary>
/// 根据HttpContext获取Authorization.Response对象
/// 在回调中获取
/// </summary>
/// <param name="context">HttpContext</param>
/// <returns></returns>
public static Authorization.Response GetResponse(HttpContext context)
{
Authorization.Response obj = null;
if (context != null && context.Request.Params["code"] != null && context.Request.Params["state"] != null)
{
obj = new Authorization.Response();
obj.Code = context.Request.Params["code"];
obj.State = context.Request.Params["state"];
obj.BackURL = "";
if (context.Request.Params["backurl"] != null)
obj.BackURL = WuJian.Common.Security.UrlDecode(context.Request.Params["backurl"]); //临时令牌日志
if (WuJian.OAuth.Qq.Config.Log)
{
string path = Path.Combine(WuJian.OAuth.Qq.Config.LogDir, "auth", DateTime.Now.ToString("yyyyMMdd") + ".txt");
Log.Write(path, "response", "code:" + obj.Code + "\r\nstate:" + obj.State + "\r\nbackurl:" + obj.BackURL);
}
}
return obj;
} #endregion }//end class }

响应:

using System;
using System.Web;
using System.Collections.Generic; namespace WuJian.OAuth.Qq.Authorization
{
/// <summary>
/// 临时令牌(Authorization)响应
/// 注:成功时通过callback地址响应,失败时在QQ登录窗提示
/// 参考:http://wiki.open.qq.com/wiki/website/%E4%BD%BF%E7%94%A8Authorization_Code%E8%8E%B7%E5%8F%96Access_Token
/// </summary>
public class Response
{
private string mCode;
private string mState;
private string mBackURL; /// <summary>
/// 临时令牌
/// 此code会在10分钟内过期
/// </summary>
public string Code {
get { return this.mCode; }
set { this.mCode = value; }
} /// <summary>
/// 防止CSRF攻击,成功授权后原样带回Request中的state值
/// </summary>
public string State {
get { return this.mState; }
set { this.mState = value; }
} /// <summary>
/// 非接口参数
/// UrlDecode
/// </summary>
public string BackURL {
get { return this.mBackURL; }
set { this.mBackURL = value; }
} }
}

访问令牌

打个简单的比喻,好比现在我们的大多数小区,一般有个由保安看守的大门,这像临时令牌。然后进了小区拿钥匙打开自家房间门,这是访问令牌。

那些年,我们开发的接口之:QQ登录(OAuth2.0)

请求:

using System;
using System.IO;
using System.Collections.Generic; namespace WuJian.OAuth.Qq.AccessToken
{
/// <summary>
/// 访问令牌(Access Token)请求
/// 参考:http://wiki.open.qq.com/wiki/website/%E4%BD%BF%E7%94%A8Authorization_Code%E8%8E%B7%E5%8F%96Access_Token
/// </summary>
public class Request
{
#region 构造函数 /// <summary>
/// 构造函数
/// </summary>
/// <param name="code">临时令牌:Authorization Code</param>
public Request(string code)
{
this.mUrl = Config.TokenURI;
this.mClientID = Config.AppID;
this.mClientSecret = Config.AppKey;
this.mCode = code;
this.mRedirectURI = Config.RedirectURI;
} #endregion #region 请求参数 private string mUrl;
private readonly string mGrantType = "authorization_code";
private string mClientID;
private string mClientSecret;
private string mCode;
private string mRedirectURI; /// <summary>
/// 接口请求地址(不包含参数)
/// 如:https://graph.qq.com/oauth2.0/token
/// </summary>
public string Url {
get { return this.mUrl; }
set { this.mUrl = value; }
} /// <summary>
/// 授权类型,此值固定为“authorization_code”。
/// </summary>
public string GrantType {
get { return this.mGrantType; }
} /// <summary>
/// 申请QQ登录成功后,分配给网站的appid。
/// </summary>
public string ClientID {
get { return this.mClientID; }
set { this.mClientID = value; }
} /// <summary>
/// 申请QQ登录成功后,分配给网站的appkey。
/// </summary>
public string ClientSecret {
get { return this.mClientSecret; }
set { this.mClientSecret = value; }
} /// <summary>
/// 上一步返回的authorization code。
/// 如果用户成功登录并授权,则会跳转到指定的回调地址,并在URL中带上Authorization Code。
/// 例如,回调地址为www.qq.com/my.php,则跳转到:
/// http://www.qq.com/my.php?code=520DD95263C1CFEA087******
/// 注意此code会在10分钟内过期。
/// </summary>
public string Code {
get { return this.mCode; }
set { this.mCode = value; }
} /// <summary>
/// 与上一步中传入的redirect_uri保持一致。
/// </summary>
public string RedirectURI {
get { return this.mRedirectURI; }
set { this.mRedirectURI = value; }
} #endregion #region 方法 /// <summary>
/// 获取请求URL(包含参数)
/// </summary>
public string GetUrl()
{
return string.Format("{0}?grant_type={1}&client_id={2}&client_secret={3}&code={4}&redirect_uri={5}",
this.mUrl, this.mGrantType, this.mClientID, this.mClientSecret, this.mCode, System.Web.HttpUtility.UrlEncode(this.mRedirectURI));
} /// <summary>
/// 获取响应
/// </summary>
public WuJian.OAuth.Qq.AccessToken.Response GetResponse()
{
WuJian.OAuth.Qq.AccessToken.Response response = null; //如:access_token=FE04************************CCE2&expires_in=7776000
string responseText = WuJian.Common.Http.Get(GetUrl());
//日志
if (WuJian.OAuth.Qq.Config.Log)
{
string path = Path.Combine(WuJian.OAuth.Qq.Config.LogDir, "token", DateTime.Now.ToString("yyyyMMdd") + ".txt");
//请求日志
Log.Write(path, "request", GetUrl());
//响应日志
Log.Write(path, "response", responseText);
} //获取所有参数键值对
var parameterArray = WuJian.Common.Http.RequestQuery(responseText);
if (parameterArray.Count > )
{
string accessToken = "";
string expiresIn = ""; foreach (var para in parameterArray)
{
if (para.Key == "access_token")
{
accessToken = para.Value;
continue;
}
if (para.Key == "expires_in")
{
expiresIn = para.Value;
continue;
}
} if (accessToken != "" && expiresIn != "")
{
response = new Response();
response.AccessToken = accessToken;
response.ExpiresIn = int.Parse(expiresIn);
}
} return response;
} #endregion }//end class
}

响应:

using System;
using System.Collections.Generic; namespace WuJian.OAuth.Qq.AccessToken
{
/// <summary>
/// 访问令牌(Access Token)响应
/// 参考:http://wiki.open.qq.com/wiki/website/%E4%BD%BF%E7%94%A8Authorization_Code%E8%8E%B7%E5%8F%96Access_Token
/// </summary>
public class Response
{
private string mAccessToken;
private int mExpiresIn; /// <summary>
/// 授权令牌
/// </summary>
public string AccessToken {
get { return this.mAccessToken; }
set { this.mAccessToken = value; }
} /// <summary>
/// 令牌有效期(单位:秒)
/// </summary>
public int ExpiresIn {
get { return this.mExpiresIn; }
set { this.mExpiresIn = value; }
} }//end class
}

OPENID

出于安全考虑,腾讯不会给第三方QQ号,而它给了一个与QQ号唯一相对应OPENID,在QQ登录中,这个OPENID用于识别唯一的QQ用户。

OPENID是OAUTH的核心思想,不管是QQ、新浪微博,还是Fackbook、Twitter,只要基于OAUTH,都会存在OPENID。

请求:

using System;
using System.IO;
using System.Collections.Generic;
using LitJson; namespace WuJian.OAuth.Qq.OpenID
{
/// <summary>
/// OPEN_ID请求
/// 参考:http://wiki.open.qq.com/wiki/website/%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7OpenID_OAuth2.0
/// </summary>
public class Request
{
#region 构造函数 /// <summary>
/// 构造函数
/// </summary>
/// <param name="accessToken">授权令牌</param>
public Request(string accessToken)
{
this.mUrl = Config.OpenIdURI;
this.mAccessToken = accessToken;
} #endregion #region 请求参数 private string mUrl;
private string mAccessToken; /// <summary>
/// 接口请求地址(不包含参数)
/// 如:https://graph.qq.com/oauth2.0/me
/// </summary>
public string Url {
get { return this.mUrl; }
set { this.mUrl = value; }
} /// <summary>
/// 授权令牌
/// </summary>
public string AccessToken {
get { return this.mAccessToken; }
set { this.mAccessToken = value; }
} #endregion /// <summary>
/// 获取WEB请求URL
/// </summary>
public string GetUrl()
{
return string.Format("{0}?access_token={1}",
this.mUrl, this.mAccessToken);
} /// <summary>
/// 获取响应
/// </summary>
/// <returns></returns>
public WuJian.OAuth.Qq.OpenID.Response GetResponse()
{
WuJian.OAuth.Qq.OpenID.Response response = null; string responseText = WuJian.Common.Http.Get(GetUrl());
//日志
if (WuJian.OAuth.Qq.Config.Log)
{
string path = Path.Combine(WuJian.OAuth.Qq.Config.LogDir, "openid", DateTime.Now.ToString("yyyyMMdd") + ".txt");
//请求日志
Log.Write(path, "request", GetUrl());
//响应日志
Log.Write(path, "response", responseText);
} try
{
//过滤
int begin = responseText.IndexOf("{");
int end = responseText.LastIndexOf("}");
responseText = responseText.Substring(begin, end - begin + ); //构造JSON对象
JsonData jd = JsonMapper.ToObject(responseText);
if (jd != null)
{
response = new Response();
response.ClientID = (string)jd["client_id"];
response.OpenID = (string)jd["openid"];
}
}
catch
{
return null;
} return response;
} }//end class
}

响应:

using System;
using System.Collections.Generic; namespace WuJian.OAuth.Qq.OpenID
{
/// <summary>
/// OpenID 响应
/// 参考:http://wiki.open.qq.com/wiki/website/%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7OpenID_OAuth2.0
/// </summary>
public class Response
{
private string mClientID;
private string mOpenID; public string ClientID
{
get { return this.mClientID; }
set { this.mClientID = value; }
} public string OpenID {
get { return this.mOpenID; }
set { this.mOpenID = value; }
} }//end class
}

API调用

拿到访问令牌和OPENID,我们就可以调用QQ的系列API了,此处列举了get_user_info,其它接口调用模式也完全相同,如下代码所示。

请求与响应:

using System;
using System.IO;
using System.Collections.Generic;
using LitJson; namespace WuJian.OAuth.Qq.API
{
/// <summary>
/// 请求
/// 参考:http://wiki.open.qq.com/wiki/website/get_user_info
/// </summary>
public class GetUserInfoRequest : APIRequestBase
{
#region 构造函数 /// <summary>
/// 构造函数
/// </summary>
/// <param name="accessToken">授权凭证</param>
/// <param name="openID">OPENID</param>
public GetUserInfoRequest(string accessToken, string openID) {
this.mUrl = Config.ApiGetUserInfoURI;
this.mAccessToken = accessToken;
this.mOauthConsumerKey = Config.AppID;
this.mOpenID = openID;
} #endregion #region 请求参数 private string mUrl;
private string mAccessToken;
private string mOauthConsumerKey;
private string mOpenID;
private readonly string mFormat = "json"; /// <summary>
/// 接口请求地址(不包含参数)
/// 如:https://graph.qq.com/user/get_user_info
/// </summary>
public override string Url {
get { return this.mUrl; }
set { this.mUrl = value; }
} /// <summary>
/// 授权凭证
/// 可通过使用Authorization_Code获取Access_Token 或来获取。
/// access_token有3个月有效期。
/// </summary>
public override string AccessToken {
get { return this.mAccessToken; }
set { this.mAccessToken = value; }
} /// <summary>
/// 申请QQ登录成功后,分配给应用的appid
/// </summary>
public override string OauthConsumerKey
{
get { return this.mOauthConsumerKey; }
set { this.mOauthConsumerKey = value; }
} /// <summary>
/// 用户的ID,与QQ号码一一对应。
/// 可通过调用https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN 来获取。
/// </summary>
public override string OpenID
{
get { return this.mOpenID; }
set { this.mOpenID = value; }
} /// <summary>
/// 固定值json
/// </summary>
public string Format {
get { return this.mFormat; }
} #endregion /// <summary>
/// 获取请求URL(包含参数)
/// 如:https://graph.qq.com/user/get_user_info?access_token=*************&oauth_consumer_key=12345&openid=****************&format=json
/// </summary>
public override string GetUrl()
{
return string.Format("{0}?access_token={1}&oauth_consumer_key={2}&openid={3}&format={4}",
this.mUrl, this.mAccessToken, this.mOauthConsumerKey, this.mOpenID, this.mFormat);
} /// <summary>
/// 获取响应
/// </summary>
/// <param name="errorCode">返回错误信息(暂返回null,未处理)</param>
/// <returns></returns>
public override APIResponseBase GetResponse(out ErrorCode errorCode)
{
string logPath = Path.Combine(WuJian.OAuth.Qq.Config.LogDir, "get_user_info", DateTime.Now.ToString("yyyyMMdd") + ".txt");
WuJian.OAuth.Qq.API.GetUserInfoResponse response = null;
errorCode = null; //向接口发送请求并获取响应字符串
string responseText = WuJian.Common.Http.Get(GetUrl());
//日志
if (WuJian.OAuth.Qq.Config.Log)
{
//请求日志
Log.Write(logPath, "request", GetUrl());
//响应日志
Log.Write(logPath, "response", responseText);
} try
{
//过滤
int begin = responseText.IndexOf("{");
int end = responseText.LastIndexOf("}");
responseText = responseText.Substring(begin, end - begin + );
responseText = responseText.Replace("\\", ""); //构造JSON对象
JsonData jd = JsonMapper.ToObject(responseText);
if (jd != null)
{
response = new GetUserInfoResponse();
response.Ret = (int)jd["ret"];
response.Msg = (string)jd["msg"];
response.Nickname = (string)jd["nickname"];
response.Figureurl = (string)jd["figureurl"];
response.Figureurl1 = (string)jd["figureurl_1"];
response.Figureurl2 = (string)jd["figureurl_2"];
response.FigureurlQQ1 = (string)jd["figureurl_qq_1"];
response.FigureurlQQ2 = (string)jd["figureurl_qq_2"];
response.Gender = (string)jd["gender"];
response.IsYellowVip = (string)jd["is_yellow_vip"];
response.Vip = (string)jd["vip"];
response.YellowVipLevel = (string)jd["yellow_vip_level"];
response.Level = (string)jd["level"];
response.IsYellowYearVip = (string)jd["is_yellow_year_vip"];
}
}
catch(Exception error)
{
//结果转换失败日志
Log.Write(logPath, "response convert", error.ToString());
return null;
} return response;
}
} /// <summary>
/// 响应
/// </summary>
public class GetUserInfoResponse : APIResponseBase
{
private int mRet;
private string mMsg;
private string mNickname;
private string mFigureurl;
private string mFigureurl1;
private string mFigureurl2;
private string mFigureurlQQ1;
private string mFigureurlQQ2;
private string mGender;
private string mIsYellowVip;
private string mVip;
private string mYellowVipLevel;
private string mLevel;
private string mIsYellowYearVip; /// <summary>
/// 返回码
/// </summary>
public int Ret {
get { return this.mRet; }
set { this.mRet = value; }
} /// <summary>
/// 如果ret小于0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
/// </summary>
public string Msg{
get { return this.mMsg; }
set { this.mMsg = value; }
} /// <summary>
/// 用户在QQ空间的昵称
/// </summary>
public string Nickname {
get { return this.mNickname; }
set { this.mNickname = value; }
} /// <summary>
/// 大小为30×30像素的QQ空间头像URL。
/// </summary>
public string Figureurl {
get { return this.mFigureurl; }
set { this.mFigureurl = value; }
} /// <summary>
/// 大小为50×50像素的QQ空间头像URL。
/// </summary>
public string Figureurl1 {
get { return this.mFigureurl1; }
set { this.mFigureurl1 = value; }
} /// <summary>
/// 大小为100×100像素的QQ空间头像URL。
/// </summary>
public string Figureurl2 {
get { return this.mFigureurl2; }
set { this.mFigureurl2 = value; }
} /// <summary>
/// 大小为40×40像素的QQ头像URL。
/// </summary>
public string FigureurlQQ1 {
get { return this.mFigureurlQQ1; }
set { this.mFigureurlQQ1 = value; }
} /// <summary>
/// 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。
/// </summary>
public string FigureurlQQ2 {
get { return this.mFigureurlQQ2; }
set { this.mFigureurlQQ2 = value; }
} /// <summary>
/// 性别。 如果获取不到则默认返回"男"
/// </summary>
public string Gender {
get { return this.mGender; }
set { this.mGender = value; }
} /// <summary>
/// 标识用户是否为黄钻用户(0:不是;1:是)。
/// </summary>
public string IsYellowVip
{
get { return this.mIsYellowVip; }
set { this.mIsYellowVip = value; }
} /// <summary>
/// 标识用户是否为黄钻用户(0:不是;1:是)
/// </summary>
public string Vip
{
get { return this.mVip; }
set { this.mVip = value; }
} /// <summary>
/// 黄钻等级
/// </summary>
public string YellowVipLevel {
get { return this.mYellowVipLevel; }
set { this.mYellowVipLevel = value; }
} /// <summary>
/// 黄钻等级
/// </summary>
public string Level {
get { return this.mLevel; }
set { this.mLevel = value; }
} /// <summary>
/// 标识是否为年费黄钻用户(0:不是; 1:是)
/// </summary>
public string IsYellowYearVip
{
get { return this.mIsYellowYearVip; }
set { this.mIsYellowYearVip = value; }
}
} }

后记

本文详细介绍了基于OAUTH2.0的QQ登录原理和过程。同时将整个过程拆分为每个独立的单元并用代码进行了演示。可前往:www.paotiao.com 体验,希望给像我一样的小站站长带来便捷和帮助。

作者:吴剑
出处:http://www.cnblogs.com/wu-jian/
本文版权归作者和博客园共有,欢迎转载,但必需注明出处,并且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

那些年,我们开发的接口之:QQ登录(OAuth2.0)的更多相关文章

  1. 什么是&OpenCurlyDoubleQuote;QQ登录OAuth2&period;0”

    1. 什么是“QQ登录OAuth2.0 OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他 ...

  2. C&num;微信公众号开发-高级接口-之网页授权oauth2&period;0获取用户基本信息(二)

    C#微信公众号开发之网页授权oauth2.0获取用户基本信息(一) 中讲解了如果通过微信授权2.0snsapi_base获取已经关注用户的基本信息,然而很多情况下我们经常需要获取非关注用户的信息,方法 ...

  3. PHP 接入(第三方登录)QQ 登录 OAuth2&period;0 过程中遇到的坑

    前言 绝大多数网站都集成了第三方登录,降低了注册门槛,增强了用户体验.最近看了看 QQ 互联上 QQ 登录的接口文档.接入 QQ 登录的一般流程是这样的:先申请开发者 -> 然后创建应用(拿到一 ...

  4. QQ互联OAuth2&period;0 &period;NET SDK 发布以及网站QQ登陆示例代码&lpar;转&rpar;

    OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容. QQ登录OAuth2 ...

  5. QQ互联OAuth2&period;0 &period;NET SDK 发布以及网站QQ登陆示例代码

    OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容. QQ登录OAuth2 ...

  6. QQ第三方授权登录OAuth2&period;0实现(Java)

    准备材料 1.已经备案好的域名 2.服务器(域名和服务器为统一主体或域名已接入服务器) 3.QQ号 4.开发流程:https://wiki.connect.qq.com/%E5%87%86%E5%A4 ...

  7. phpcms V9实现QQ登陆OAuth2&period;0

    phpcmsV9使用的QQ登陆依然是OAuth1.0,但现在腾讯已经不审核使用OAuth1.0的网站了.这对于使用pc的站长来讲是一个无比巨大的坑.经过对phpcms论坛的一位同学做的插件进行修改,现 ...

  8. Java微信公众平台开发&lpar;十六&rpar;--微信网页授权&lpar;OAuth2&period;0授权&rpar;获取用户基本信息

    转自:http://www.cuiyongzhi.com/post/78.html 好长时间没有写文章了,主要是最近的工作和生活上的事情比较多而且繁琐,其实到现在我依然还是感觉有些迷茫,最后还是决定静 ...

  9. PHP 开发API接口 注册,登录,查询用户资料

    服务端 <?php require 'conn.php'; header('Content-Type:text/html;charset=utf-8'); $action = $_GET['ac ...

随机推荐

  1. 新技能get——斜率优化

    好久没写博客了……我终于回来了…… dp总是令我很头疼的问题之一,然而我还是要学一下怎么优化它. 下面请看一道题吧: [bzoj3675][Apio2014]序列分割 试题描述 小H最近迷上了一个分割 ...

  2. C&num;学习笔记(二)——变量和表达式

    Ps:使用这两个关键字可以很方便的把头文件收起来(虽然VS已经集成这个功能= =) 但是可以一下子收起来很多个函数 一.变量 1.简单类型 (1)变量类型 (2)示例一 static void Mai ...

  3. Flash AS 响应双击事件MouseEvent&period;DOUBLE&lowbar;CLICK

    没想到在WinForm简简单单的一个问题,在AS里会成为一个坑. 我遇到的这个问题是由于Loader没有设置doubleClickEnabled=true而导致的. 因此出现这个问题,请思考是否由于此 ...

  4. Flowers(二分水过。。。)

    Flowers Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Sub ...

  5. 细说tomcat之应用监控

    官网:http://tomcat.apache.org/tomcat-7.0-doc/monitoring.html Java应用程序的监控通过JMX实现,详见:https://docs.oracle ...

  6. 2018年东北农业大学春季校赛 I-wyh的物品&lpar;二分查找&rpar;

    链接:https://www.nowcoder.com/acm/contest/93/I来源:牛客网 题目描述 wyh学长现在手里有n个物品,这n个物品的重量和价值都告诉你,然后现在让你从中选取k个, ...

  7. 梯度、散度、旋度、Jacobian、Hessian、Laplacian 的关系图

    转自松鼠的窝 一.入门

  8. (转)Nginx学习

    (二期)15.负载均衡nginx [课程15]nginx安装.xmind0.2MB [课程15]Nginx能做什么.xmind0.1MB [课程15]负载均衡nginx.xmind96.7KB [课程 ...

  9. C&plus;&plus;中公有继承、保护继承、私有继承的区别

    公有继承时基类中各成员属性保持不变,基类中private成员被隐藏.派生类的成员只能访问基类中的public/protected成员,而不能访问private成员:派生类的对象只能访问基类中的publ ...

  10. Selenium2&plus;python自动化62-jenkins持续集成环境搭建

    前言 selenium脚本写完之后,一般是集成到jenkins环境了,方便一键执行. 一.环境准备 小编环境: 1.win10 64位 2.JDK 1.8.0_66 3.tomcat 9.0.0.M4 ...