本文主要跟大家分享下我的个人博客 陈新的个人博客 站点在接入QQ登录的过程中所遇到的一些问题,在这里分享给大家,方便准备想接入QQ登录功能的或者接入过程中正遇到一些问题的小伙伴快速解决问题,避免踩我已经踩过的坑。
跳转到QQ登录授权页面的地址应该用:https://graph.qq.com/oauth2.0/authorize ,而不是用https://graph.qq.com/oauth2.0/show 这个前缀,后者这个是PC端的QQ授权页面,如果你的网站跳转到的是后面这个地址,将会导致手机端使用的QQ登录授权页面也是PC端的,这样手机端就无法使用QQ一键登录功能了,正确的应该使用前者这个地址,腾讯会自动判断跳转到PC端还是手机端的QQ登录授权页面。
手机端的QQ登录界面应该类似于如下:
不要往QQ授权页面的地址中传入参数display=pc,这个参数告诉QQ说我当前正在PC端进行QQ一件登录,因此也会造成和上述一样的问题。
以上就是本人在对接QQ登录过程中遇到的一些问题,下面附上本人的个人站中用到的QQ登录的代码,方便大家快速集成QQ登录功能,本人的个人站使用asp.net core 3.1.
接口对接代码
using BlogSys.Models; using BlogSys.Models.QQLogin; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; namespace BlogSys.Services { /// <summary> /// QQ登录功能。 /// </summary> public class QQLoginService { const string callbackUrl = "这里填写你提供给腾讯的你的网站的回调地址"; const string clientId = "这里填写网站应用ID"; const string clientSecret = "这里填写网站应用秘钥"; HttpClient client; ILogger<QQLoginService> logger; public QQLoginService(HttpClient client, ILogger<QQLoginService> logger) { this.client = client; this.logger = logger; } /// <summary> /// QQ 登录 /// </summary> /// <param name="code"></param> /// <param name="redirect_url"></param> /// <returns></returns> public async Task<QQUserInfo> Login(string code) { QQUserInfo userInfo = null; QQApiAccessToken accessToken = await this.GetAccessToken(code, callbackUrl); QQOpenIDInfo openIDInfo = await this.GetOpenID(accessToken.access_token); userInfo = await this.GetUserInfo(accessToken.access_token, clientId, openIDInfo.openid); userInfo.OpenID = openIDInfo.openid; return userInfo; } //根据不同页面点击QQ登录时,生成QQ授权登录页面地址,方面登录前在哪个页面,登录后就跳回哪个页面。 public string GetQQLoginUrl(string redirectUrl) { return $"https://graph.qq.com/oauth2.0/authorize?which=Login&response_type=code&redirect_uri={callbackUrl}&client_id={clientId}&state={redirectUrl}"; } /// <summary> /// 获取API访问Token. /// </summary> /// <param name="code"></param> /// <param name="redirect_uri"></param> /// <param name="fmt"></param> /// <returns></returns> public async Task<QQApiAccessToken> GetAccessToken(string code, string redirect_uri, string fmt = "json") { string grant_type = "authorization_code"; string apiUrl = $"/oauth2.0/token?grant_type={grant_type}&client_id={clientId}&client_secret={clientSecret}&code={code}&redirect_uri={redirect_uri}&fmt={fmt}"; HttpResponseMessage responseMessage = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, apiUrl)); if (!responseMessage.IsSuccessStatusCode) return null; string resultStr = await responseMessage.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(resultStr)) { return null; } return JsonSerializer.Deserialize<QQApiAccessToken>(resultStr); } /// <summary> /// 根据刷新Token获取访问Token. /// </summary> /// <param name="refresh_token"></param> /// <param name="fmt"></param> /// <returns></returns> public async Task<QQApiAccessToken> GetAccessTokenByRefreshToken(string refresh_token, string fmt = "json") { string grant_type = "refresh_token"; string apiUrl = $"/oauth2.0/token?grant_type={grant_type}&client_id={clientId}&client_secret={clientSecret}&refresh_token={refresh_token}&fmt={fmt}"; HttpResponseMessage responseMessage = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, apiUrl)); if (!responseMessage.IsSuccessStatusCode) return null; string resultStr = await responseMessage.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(resultStr)) { return null; } return JsonSerializer.Deserialize<QQApiAccessToken>(resultStr); } /// <summary> /// 获取QQ OpenId信息 /// </summary> /// <param name="access_token">API访问Token</param> /// <param name="fmt"></param> /// <returns></returns> public async Task<QQOpenIDInfo> GetOpenID(string access_token, string fmt = "json") { string apiUrl = $"oauth2.0/me?access_token={access_token}&fmt={fmt}"; HttpResponseMessage responseMessage = await this.client.SendAsync(new HttpRequestMessage(HttpMethod.Get, apiUrl)); if (!responseMessage.IsSuccessStatusCode) return null; string apiResult = await responseMessage.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(apiResult)) return null; return JsonSerializer.Deserialize<QQOpenIDInfo>(apiResult); } /// <summary> /// 获取用户信息 /// </summary> /// <param name="access_token"></param> /// <param name="oauth_consumer_key"></param> /// <param name="openid"></param> /// <returns></returns> public async Task<QQUserInfo> GetUserInfo(string access_token, string oauth_consumer_key, string openid) { string apiUrl = $"user/get_user_info?access_token={access_token}&oauth_consumer_key={oauth_consumer_key}&openid={openid}"; HttpResponseMessage responseMessage = await this.client.SendAsync(new HttpRequestMessage(HttpMethod.Get, apiUrl)); if (!responseMessage.IsSuccessStatusCode) return null; string apiResult = await responseMessage.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(apiResult)) return null; return JsonSerializer.Deserialize<QQUserInfo>(apiResult); } } }
QQ登录相关实体模型
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace BlogSys.Models.QQLogin { public class QQUserInfo { /// <summary> /// 返回码 0:成功 其他:失败 /// </summary> public int ret { get; set; } /// <summary> /// 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。 /// </summary> public string msg { get; set; } /// <summary> /// 用户在QQ空间的昵称。 /// </summary> public string nickname { get; set; } /// <summary> /// 大小为30×30像素的QQ空间头像URL。 /// </summary> public string figureurl { get; set; } /// <summary> /// 大小为50×50像素的QQ空间头像URL。 /// </summary> public string figureurl_1 { get; set; } /// <summary> /// 大小为100×100像素的QQ空间头像URL。 /// </summary> public string figureurl_2 { get; set; } /// <summary> /// 大小为40×40像素的QQ头像URL。 /// </summary> public string figureurl_qq_1 { get; set; } /// <summary> /// 大小为100×100像素的QQ头像URL。 /// 需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。 /// </summary> public string figureurl_qq_2 { get; set; } /// <summary> /// 性别。 如果获取不到则默认返回"男" /// </summary> public string gender { get; set; } public string is_yellow_vip { get; set; } public string vip { get; set; } public string yellow_vip_level { get; set; } public string level { get; set; } public string is_yellow_year_vip { get; set; } /// <summary> /// QQ用户唯一标识。 /// </summary> public string OpenID { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace BlogSys.Models.QQLogin { public class QQApiAccessToken { /// <summary> /// 授权令牌,Access_Token。 /// </summary> public string access_token { get; set; } /// <summary> /// 该access token的有效期,单位为秒。 /// </summary> public string expires_in { get; set; } /// <summary> /// 在授权自动续期步骤中,获取新的Access_Token时需要提供的参数。 /// 注:refresh_token仅一次有效 /// </summary> public string refresh_token { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace BlogSys.Models.QQLogin { public class QQOpenIDInfo { public string client_id { get; set; } public string openid { get; set; } } }
以下代码为asp.net core 中注入HttpClient 的部分。
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient<QQLoginService>(httpClient => { httpClient.BaseAddress = new Uri("https://graph.qq.com"); }); }
好了,以上就是我今天要和小伙伴们分享的内容,如果你在对接QQ登录过程中遇到了一些困惑,那么请登录我的 个人博客 并在我的这篇文章中留言,因为我的个人博客收到用户留言,会立刻通知我这边,以便我快速帮你们解答。