如何从零开始对接第三方登录(Java版):QQ登录和微博登录

电脑杂谈  发布时间:2019-08-27 15:05:39  来源:网络整理

qq java_qq java_java软件下载qq

个人网页最近增加了评论功能,为了便于客户不用注册就可以评论,对接了QQ和微博这2大常用工具的一键登录,总的来说其实都挺简单的,可能会有一点小坑,但不算多,完整记录出来方便后来人快速对接。

在真正开始对接之前,我们先来聊一聊后台的方案设计。既然是对接第三方登录,那就免不了如何将客户信息保存。首先必须确立一点的是,用户在第三方登录失败以后,我们能获得的只是是一个代表用户唯一身份的ID(微博是真实uid,QQ是加密的openId)以及用来识别身份的accessToken,当然也有名字、头像、性别等有限资料,对接第三方登录的关键就是如何确认客户是合法注册,如果确认此次登陆的和以前登录的是同一个人并且不是假冒的。其实这个并不用我们非常操心,就以微博注册为例,用户登陆失败以后会回调一个code给我们,然后我们再拿code去微博那换取accessToken,如果这个code是用户乱填的,那这一关肯定过不了,所以,前面的害怕有点多余,哈哈。

另外一个问题就是如何和现有用户平台拓宽,有的网页在客户终于登陆失败以后需要客户输入手机号和验证码,或者要客户再次登录帐号和密钥来绑定第三方账户,感觉这样实现客户感受特别差,碰到这样网站我大概都是直接打开,都早已登陆了还让用户登录,什么鬼!由于我做的是评论功能,我并不希望评论用户和现有用户表打通,所以就不存在这件事了,如果想打通的话,我认为仅仅就是登录失败以后默认往老用户表插入一条数据,然后和OpenUser表关联起来,判断客户能否登录时把OpenUser的鉴权也加上去就OK了。

本文的后台以Java为例。

再来看看设计,为了平台的扩展性,我有一个专门的OpenUser表拿来存放第三方登录客户,主要字段如下:

字段名字段含义

主键

openType

第三方类型,比如qq、weibo

openId

代表用户唯一身份的ID

accessToken

qq java_qq java_java软件下载qq

调用接口必须用到的token,比如运用accessToken发表微博等,如果仅仅对接登录的话,这个貌似没啥用

expiredTime

授权过期时间,第三方登录授权都是有过期时间的,比如3个月

nickname

昵称

avatar

头像

这样设计理论上就可以无限扩展了。

这里我也是看看我的方案,把accessToken写入cookie肯定是不安全的,因为accessToken相当于是第三方网站的临时密码,被对方抢走了就可以随便拿去干好事了。可以在客户登录失败以后我们自己生成一个token,这样的token即使泄露了顶多就是被人用来随意评论,损失不大,但是一旦accessToken被泄露了,以微博为例,人家可以借助这个accessToken随意发微博、删微博、加关注之类,很危险。当然,如果不想token泄露的话也可以借助绑定IP等形式来限制。

鉴权的话就是首先判定cookie中能否有我们自己的token,然后判定是否合法,合法再判定第三方授权是否已过期等等。

QQ登录我们对接的是QQ互联,地址: ,首先必须登录作为开发者并实名认证,需要手持照片,具体就不讲了。

进入构建应用,根据实际应该是建立网站应用而是移动应用,我这儿是网站应用:

qq java_qq java_java软件下载qq

第一步:

第二步:

提交完以后会手动提交审核,基本上就是审核你的资料和备案的资料能否一致,所有资料需要和备案资料一模一样,否则审核不会通过:

当然,这些资料里面还是可以设置的。申请失败以后你会受到appId和aey。

这里可以下载一些视觉素材,在页面合适位置放一个QQ登录图标,点击时引导用户开启授权页面:

代码:

/**
 * 封装一个居中打开新窗口的方法
 */
function openWindow(url, width, height)
{
    width = width || 600;
    height = height || 400;
    var left = (window.screen.width - width) / 2;
    var top = (window.screen.height - height) / 2;
    window.open(url, "_blank", "toolbar=yes, location=yes, directories=no, status=no, menubar=yes, scrollbars=yes, resizable=no, copyhistory=yes, left="+left+", top="+top+", width="+width+", height="+height);
}
function qqLogin()
{
    var qqAppId = '424323422'; // 上面申请得到的appid
    var qqAuthPath = 'http://www.test.com/auth'; // 前面设置的回调地址
    var state = 'fjdslfjsdlkfd'; // 防止CSRF攻击的随机参数,必传,登录成功之后会回传,最好后台自己生成然后校验合法性
    openWindow(`https://graph.qq.com/oauth2.0/authorize?response_type=token&client_id=${qqAppId}&redirect_uri=${encodeURIComponent(qqAuthPath)}&state=${state}`);
}

java软件下载qq_qq java_qq java

然后会开启一个授权页面,这个页面大家需要都熟悉:

然后到了这儿我就看见一个问题了,官方文档写的是登陆失败以后首先会回传一个code,然后再拿code调接口换取accessToken,然后我试了好多次也换过2个账号发现每天都是直接返回了accessToken,帮我省了一步了,不知道是哪个状况,郁闷。

现在假如我们都是直接获得accessToken(因为我暂时还没搞清楚QQ为啥会直接返回,跟文档说的不一样),但是授权回调时accessToken会被放在#后面,URL地址中的hash值似乎不会被放到后台(貌似是这么,如有不正确欢迎评论指正),所以没法写一个下面这种的临时页面:

/**
 * QQ鉴权
 * @param request
 * @param response
 * @throws Exception
 */
@RequestMapping("/authqq")
public void authQQ(HttpServletRequest request, HttpServletResponse response) throws Exception
{
    // QQ登录有点特殊,参数放在#后面,后台无法获取#后面的参数,只能用JS做中间转换
    String html =   "<!DOCTYPE html>" +
                    "<html lang=\"zh-cn\">" +
                    "<head>" +
                    "   <title>QQ登录重定向页</title>" +
                    "   <meta charset=\"utf-8\"/>" +
                    "</head>" +
                    "<body>" +
                    "   <script type=\"text/javascript\">" +
                    "   location.href = location.href.replace('#', '&').replace('auth_qq', 'auth_qq_redirect');" +
                    "   </script>" +
                    "</body>" +
                    "</html>";
    response.getWriter().print(html);
}

根据accessToken调接口获得用户的openId,特别注意这个openId是相对于QQ号+appId唯一的,换句话说同一个QQ号注册2个不同appId时获得到的openId是不同的。顺便说一句,QQ登录的相关接口做的还真够“随便”的,全部都是最简单的get请求,所以对接上去十分成功。

直接看代码:

// 根据accessToken换取openId
// 错误示例:callback( {"error":100016,"error_description":"access token check failed"} );
// 正确示例:callback( {"client_id":"10XXXXX49","openid":"CF2XXXXXXXX9F4C"} );
String result = HttpsUtil.get("https://graph.qq.com/oauth2.0/me?access_token=" + accessToken);
Map<String, Object> resp = parseQQAuthResponse(result); // 这个方法就是把结果转Map
Integer errorCode = (Integer)resp.get("error");
String errorMsg = (String)resp.get("error_description");
String openId = (String)resp.get("openid");
if(errorCode != null) return new ErrorResult(errorCode, "获取QQ用户openId失败:"+errorMsg);
// 获取用户昵称、头像等信息,{ret: 0, msg: '', nickname: '', ...} ret不为0表示失败
result = HttpsUtil.get("https://graph.qq.com/user/get_user_info?access_token="+accessToken+"&oauth_consumer_key="+appId+"&openid="+openId);
resp = JsonUtil.parseJsonToMap(result);
Integer ret = (Integer)resp.get("ret");
String msg = (String)resp.get("msg");
if(ret != 0) return new ErrorResult("获取用户QQ信息失败:"+msg);
// 用户昵称可能存在4个字节的utf-8字符,MySQL默认不支持,直接插入会报错,所以过滤掉
String nickname = StringUtil.filterUtf8Mb4((String)resp.get("nickname")).trim(); // 这个方法可以自行百度
// figureurl_qq_2=QQ的100*100头像,figureurl_2=QQ 100&100空间头像,QQ头像不一定有,空间头像一定有
String avatar = (String)resp.get("figureurl_qq_2");
if(StringUtil.isBlank(avatar)) avatar = (String)resp.get("figureurl_2");
String gender = (String)resp.get("gender");


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/sanxing/article-120739-1.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...