QQ登录网站接入功能实现--非官方文档搬运

2024-05-24 02:48

本文主要是介绍QQ登录网站接入功能实现--非官方文档搬运,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

最近第一次使用QQ登录功能,期间遇到这种问题,在网上找了很多资料,大多都是官方的搬运,并没有真正的干料,可能是个人能力问题,遇到了各种麻烦,折腾了几天,最终弄好,在这里记录一下和大家分享,希望对大家有所帮助。

由于官方文档已经对如何使用API接口做出了很明确的说明,所以这里我只是记录我在开发过程中一些步骤中遇到的问题和注意的事项,详细步骤请参考官方文档:http://wiki.connect.qq.com/

开发环境:SpringMVC、QQ登录Java SDK版、Maven、IDEA

应用部署和常见问题解决

访问QQ互联的地址如下:http://connect.qq.com/,这里是需要开发者进行注册登录验证的网址

一、账户登录
注意:登录的账户尽量用公司专用的,以后用于测试的时候,只能这个账户进行测试,这样的话,可以减少不必要的麻烦或者泄露自己的QQ信息,另外在本地是可以进行测试的,网上流传的本地不可测试的说法是不正确的,官方Demo中已经明确给出了具体的本地测试方法:(本地测试未进行验证,不做研究)

如果在本机tomcat或其他服务器下部署请配置本地host文件:127.0.0.1 您的回调域名直接部署运行,将sdk4j_demo目录中的web目录直接放在tomcat服务器的webapp目录即可,配置conf/server.xml文件中的host的context<Context docBase="web" path="/" privileged="true" antiResourceLocking="false"></Context>
并将webapp目录下的Root 文件夹暂时移除。
其他服务器请参照服务器自身部署方法。**请将服务器的端口号配置至80端口**。
配置host:127.0.0.1 您的回调域名   访问首页  您的回调域名/index.jsp
网站首页 index.jsp 引导用户到 IndexServlet
IndexServlet 用到了 SDK中的 com.qq.connect.oauth.Oauth.getAuthorizeURL(..) 方法来获取应该引导用户跳转的地址
。。。(详见Demo中的ReadMe.txt文件)

二、注册开发者账户
如果已经注册,则不会有该步骤,没有的话根据相应的需求进行注册

三、创建应用

首先可以根据官方案例进行编写自己的项目,http://qzonestyle.gtimg.cn/qzone/vas/opensns/res/doc/qqConnect_Server_SDK_java_v2.0.zip
(或者可访问:http://wiki.connect.qq.com/sdk%E4%B8%8B%E8%BD%BD)

项目代码完成之后,可以继续下边的内容:

这里写图片描述

在这一块我相信是很多人遇到的问题,第一个是网站地址该如何填写,另一个是回调地址的填写,尽管填写完成还会有相应的报错,下边做一下我的分析:

1、网站地址的选取:首先是官网的首页或登陆界面,在域名解析的时候必须为80端口(我尝试过其他端口是不可以的)

这里写图片描述

上图是一个域名的解析,例如:www.abc.com是我的域名,111.23.23.244是我的主机,图中有三条记录

第一条是将域名和主机IP地址进行绑定;
第二条是将二级域名dubboadmin进行解析,即www.dubboadmin.abc.com;
第三条是将二级域名security进行解析,即www.security.abc.com;;

此时可以看出,dubboadmin和security都是二级域名,一个是通过A记录一个是通过隐形URL的类型,这是因为,在默认的域名解析中,如果通过A记录来绑定域名的话,默认的是绑定到IP的80端口,即111.23.23.244:80,但是一个IP绑定主机上的端口还有很多,一个IP主机机器上可以运行多个项目,这样的话我们就通过隐形URL的方式来将不同的二级域名绑定到不同的端口上,即是将dubboadmin绑定到http://www.abc.com:8012端口上.

我在做QQ登录的时候,亲测过使用其他端口的不可行,请看下图:

这里写图片描述

这个是我使用的该域名下绑定的8011端口,打开网页时的界面,这里边明显的是一个framenset将doucument中的内容进行包裹,在最外层的html下的head中并没有需要添加的meta信息,而真正的我们的网页信息,是document包裹内的东西,游览前只是根据非80端口的服务器网页内容做了一次封装显示在网页中,而qq在验证网站的时候,找的是该网页head中的meta信息,因此会验证失败。这一点也在官方Demo的ReadMe.txt文件中明确指出了使用80端口。

所以,如果你在开发的过程中也是出现使用IP非80端口绑定的域名遇到了验证失败这个问题,请使用80端口进行测试。

最后,根据80端口,我绑定的域名是www.security.abc.com,但是还要注意的是,对于任何一个用户在访问www.security.abc.com地址的时候,有可能会让用户登录,那么会跳转到一个登录界面,例如我的就是当访问www.security.abc.com的时候,他会自动跳到security.abc.com/login.do让用户登录,那我我最终的网站地址就是:http://security.abc.com/login.do,因为,这个网址也是QQ在验证网站的时候进行访问的链接(可以打log进行查看),因此使用http://security.abc.com/ 是不可以的。(虽然,我在服务器处理的是,访问www.security.abc.com如果用户没有登录会自动跳转到http://security.abc.com/login.do,但是直接写http://security.abc.com/验证是失败的,具体不清楚是不是qq在验证的时候直接找http://security.abc.com/下的界面,这个还需后边的测试去验证)

这里写图片描述

同样的在这个login界面,也就是http://security.abc.com/login.do所指向的界面中添加QQ登录按钮,和网站验证时需要填写在head标签中的meta信息,例如:

<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta property="qc:admins" content="1133201050a16asdfhjsdf7sd5f6567647"/><title>QQ登录测试</title>
</head>
<div class="form-group input-group" style="margin-top: 45px;"><a href="/qqlogin.do">请使用你的QQ账号登陆</a>
</div>

这里的/qqlogin.do是进行登录处理的controller地址,在后边会说

2、回调地址的选择

回调地址的选择是在用户通过QQ授权之后,我们需要对用户信息进行保存的地址,由于使用的是SpringMVC,所以后缀的时候有个do,例如我的回调操作如下:(代码为官方Demo案例,可以直接使用)

@Controller
@RequestMapping
public class LoginController {@Injectprivate Oauth qqOauth;/*** 进行QQ登录,这里就是前台界面中访问的那个超链接的处理Controller*/@RequestMapping(value = "/qqlogin")public void QQLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html;charset=utf-8");try {response.sendRedirect(qqOauth.getAuthorizeURL(request));} catch (QQConnectException e) {e.printStackTrace();}}/*** 用于QQ登录的回调,进行用户信息的保存操作等*/@RequestMapping(value = "/afterQQLogin")protected void afterQQLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html; charset=utf-8");PrintWriter out = response.getWriter();try {AccessToken accessTokenObj = (new Oauth()).getAccessTokenByRequest(request);String accessToken = null, openID = null;long tokenExpireIn = 0L;//这还有很多代码,是在上边的那个官方案例中给出的,或者在最下边的代码中,请参考使用} catch (QQConnectException e) {}}
}

因此我的回调地址是:http://security.abc.com/afterQQLogin.do

如果你的项目代码中完成了以上部分的话,可以将项目部署到真实的域名下,然后进行验证。

3、项目的配置文件

如果上述过程OK,则在官方demo中还有一个名为:qqconnectconfig.properties的配置文件,这个是需要配置的:

app_ID = 34234123 #自己申请的id
app_KEY = ecfd12341234qewrqwer69db492e1ca #自己申请的key
redirect_URI = http://security.abc.com/afterQQLogin.do #回调地址
scope=get_user_info,add_topic,add_one_blog,add_album,upload_pic,list_album,add_share,check_page_fans,add_t,add_pic_t,del_t,get_repost_list,get_info,get_other_info,get_fanslist,get_idollist,add_idol,del_ido,get_tenpay_addr
#下边还有很多,因为是不需要修改的,所以不具体列出

这里提示:别忘了引用qq登录的jar包

官方Demo中介绍的是该文件要放在src目录下,这是在传统的通过MyEclipse中创建的静态Web项目的做法,如果使用的是Spring框架的话,把该文件放在src目录下的话会出现bean加载失败的问题,则需要将该文件放在resource目录下,这样的话,在进行项目启动的时候就可以加载到。

这里写图片描述

另外,如果将该文件放在了正确的位置,还是出现bean未加载的问题的话,如果出现下边的错误:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.qq.connect.oauth.Oauth org.albert.security.controller.LoginController.qqOauth; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.qq.connect.oauth.Oauth] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.inject.Inject()}

我的解决方法是手动配置该Oauth,在spring配置文件的相应位置放入

<bean id="qqOauth" class="com.qq.connect.oauth.Oauth"/>

在使用的位置添加:

 @Injectprivate Oauth qqOauth;

并将案例中response.sendRedirect(new Oauth().getAuthorizeURL(request));的代码对应的改为response.sendRedirect(qqOauth.getAuthorizeURL(request));(可以将Demo中的代码和最下边我提供的代码进行比较,可以发现修改的地方,就是在Oauth类实例化的时候进行手动的装配bean)

这样的话,在我的项目中可以跑起来,QQ登录的功能也可以使用。

其他问题

1、官方对应错误返回码文档说明如下:http://wiki.connect.qq.com/公共返回码说明

2、如果使用IDEA+maven项目的开发方式的话,那么怎么引用qq的依赖jar哪?
过程如下:在WEB-INF 目录下创建一个lib目录,将该jar放进去,然后再对应的POM文件中加入:

<dependency><groupId>javabuilder</groupId><artifactId>javabuilder</artifactId><version>1.0</version><scope>system</scope><systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/Sdk4J.jar</systemPath>
</dependency>

systemPath为自己对应的位置,即可完成对jar的依赖,另外别忘了将在Sdk4J.jar上右键将改jar加入到classpath中。

3、javax.net.ssl.SSLKeyException

在使用QQ官方的api方法时,如果在QQ api代码中报出一下异常

javax.net.ssl.SSLKeyException: [Security:090477]Certificate chain received from ebanktest.95559.com.cn - 124.74.249.16 was not trusted causing SSL handshake failure

请将jdk的版本设置为sun jdk而不是openjdk

[root@VM_92_170_centos home]# java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
[root@VM_92_170_centos home]# 

—————————————————分割线——————————————————-
另附我在项目中是使用的Controller:

@Controller
@RequestMapping
public class LoginController {private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);@Injectprivate Oauth qqOauth;/*** 进行QQ登录* @param request* @param response* @throws IOException*/@RequestMapping(value = "/qqlogin")public void QQLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html;charset=utf-8");try {response.sendRedirect(qqOauth.getAuthorizeURL(request));} catch (QQConnectException e) {e.printStackTrace();}}/*** 用于QQ登录的回调*/@RequestMapping(value = "/afterQQLogin")protected void afterQQLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html; charset=utf-8");PrintWriter out = response.getWriter();try {AccessToken accessTokenObj = (new Oauth()).getAccessTokenByRequest(request);String accessToken = null, openID = null;long tokenExpireIn = 0L;if (accessTokenObj.getAccessToken().equals("")) {
//                我们的网站被CSRF攻击了或者用户取消了授权
//                做一些数据统计工作System.out.print("没有获取到响应参数");} else {accessToken = accessTokenObj.getAccessToken();tokenExpireIn = accessTokenObj.getExpireIn();request.getSession().setAttribute("demo_access_token", accessToken);request.getSession().setAttribute("demo_token_expirein", String.valueOf(tokenExpireIn));// 利用获取到的accessToken 去获取当前用的openid -------- startOpenID openIDObj = new OpenID(accessToken);openID = openIDObj.getUserOpenID();out.println("欢迎你,代号为 " + openID + " 的用户!");request.getSession().setAttribute("demo_openid", openID);
//                out.println("<a href=" + "/shuoshuoDemo.html" + " target=\"_blank\">去看看发表说说的demo吧</a>");// 利用获取到的accessToken 去获取当前用户的openid --------- endout.println("<p> start -----------------------------------利用获取到的accessToken,openid 去获取用户在Qzone的昵称等信息 ---------------------------- start </p>");UserInfo qzoneUserInfo = new UserInfo(accessToken, openID);UserInfoBean userInfoBean = qzoneUserInfo.getUserInfo();out.println("<br/>");if (userInfoBean.getRet() == 0) {out.println(userInfoBean.getNickname() + "<br/>");out.println(userInfoBean.getGender() + "<br/>");out.println("黄钻等级: " + userInfoBean.getLevel() + "<br/>");out.println("会员 : " + userInfoBean.isVip() + "<br/>");out.println("黄钻会员: " + userInfoBean.isYellowYearVip() + "<br/>");out.println("<image src=" + userInfoBean.getAvatar().getAvatarURL30() + "/><br/>");out.println("<image src=" + userInfoBean.getAvatar().getAvatarURL50() + "/><br/>");out.println("<image src=" + userInfoBean.getAvatar().getAvatarURL100() + "/><br/>");} else {out.println("很抱歉,我们没能正确获取到您的信息,原因是: " + userInfoBean.getMsg());}out.println("<p> end -----------------------------------利用获取到的accessToken,openid 去获取用户在Qzone的昵称等信息 ---------------------------- end </p>");out.println("<p> start ----------------------------------- 验证当前用户是否为认证空间的粉丝------------------------------------------------ start <p>");PageFans pageFansObj = new PageFans(accessToken, openID);PageFansBean pageFansBean = pageFansObj.checkPageFans("97700000");if (pageFansBean.getRet() == 0) {out.println("<p>验证您" + (pageFansBean.isFans() ? "是" : "不是") + "QQ空间97700000官方认证空间的粉丝</p>");} else {out.println("很抱歉,我们没能正确获取到您的信息,原因是: " + pageFansBean.getMsg());}out.println("<p> end ----------------------------------- 验证当前用户是否为认证空间的粉丝------------------------------------------------ end <p>");out.println("<p> start -----------------------------------利用获取到的accessToken,openid 去获取用户在微博的昵称等信息 ---------------------------- start </p>");com.qq.connect.api.weibo.UserInfo weiboUserInfo = new com.qq.connect.api.weibo.UserInfo(accessToken, openID);com.qq.connect.javabeans.weibo.UserInfoBean weiboUserInfoBean = weiboUserInfo.getUserInfo();if (weiboUserInfoBean.getRet() == 0) {//获取用户的微博头像----------------------startout.println("<image src=" + weiboUserInfoBean.getAvatar().getAvatarURL30() + "/><br/>");out.println("<image src=" + weiboUserInfoBean.getAvatar().getAvatarURL50() + "/><br/>");out.println("<image src=" + weiboUserInfoBean.getAvatar().getAvatarURL100() + "/><br/>");//获取用户的微博头像 ---------------------end//获取用户的生日信息 --------------------startout.println("<p>尊敬的用户,你的生日是: " + weiboUserInfoBean.getBirthday().getYear()+ "年" + weiboUserInfoBean.getBirthday().getMonth() + "月" +weiboUserInfoBean.getBirthday().getDay() + "日");//获取用户的生日信息 --------------------endStringBuffer sb = new StringBuffer();sb.append("<p>所在地:" + weiboUserInfoBean.getCountryCode() + "-" + weiboUserInfoBean.getProvinceCode() + "-" + weiboUserInfoBean.getCityCode()+ weiboUserInfoBean.getLocation());//获取用户的公司信息---------------------------startArrayList<Company> companies = weiboUserInfoBean.getCompanies();if (companies.size() > 0) {//有公司信息for (int i = 0, j = companies.size(); i < j; i++) {sb.append("<p>曾服役过的公司:公司ID-" + companies.get(i).getID() + " 名称-" +companies.get(i).getCompanyName() + " 部门名称-" + companies.get(i).getDepartmentName() + " 开始工作年-" +companies.get(i).getBeginYear() + " 结束工作年-" + companies.get(i).getEndYear());}} else {//没有公司信息}//获取用户的公司信息---------------------------endout.println(sb.toString());} else {out.println("很抱歉,我们没能正确获取到您的信息,原因是: " + weiboUserInfoBean.getMsg());}out.println("<p> end -----------------------------------利用获取到的accessToken,openid 去获取用户在微博的昵称等信息 ---------------------------- end </p>");}} catch (QQConnectException e) {}}
}

登录连接:

 <div class="form-group input-group" style="margin-top: 45px;"><a href="/qqlogin.do">请使用你的QQ账号登陆</a>
</div>

这篇关于QQ登录网站接入功能实现--非官方文档搬运的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/997155

相关文章

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

苹果macOS 26 Tahoe主题功能大升级:可定制图标/高亮文本/文件夹颜色

《苹果macOS26Tahoe主题功能大升级:可定制图标/高亮文本/文件夹颜色》在整体系统设计方面,macOS26采用了全新的玻璃质感视觉风格,应用于Dock栏、应用图标以及桌面小部件等多个界面... 科技媒体 MACRumors 昨日(6 月 13 日)发布博文,报道称在 macOS 26 Tahoe 中

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取