Java 实现 QQ 登陆

  1. 前言

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

  2. 后台设计在真正开始对接之前,我们先来聊一聊后台的方案设计。既然是对接第三方登录,那就免不了如何将用户信息保存。首先需要明确一点的是,用户在第三方登录成功之后,我们能拿到的仅仅是一个代表用户唯一身份的ID以及用来识别身份的accessToken,当然还有昵称、头像、性别等有限资料,对接第三方登录的关键就是如何确定用户是合法登录,如果确定这次登录的和上次登录的是同一个人并且不是假冒的。其实这个并不用我们特别操心,就以微博登录为例,用户登录成功之后会回调一个code 给我们,然后我们再拿code去微博那换取accessToken,如果这个code是用户乱填的,那这一关肯定过不了,所以,前面的担心有点多余。另外一个问题就是如何和现有用户系统打通,有的网站在用户已经登录成功之后还要用户输入手机号和验证码,或者要用户重新注册账号和密码来绑定第三方账户,感觉这种实现用户体验非常差,碰到这种网站我一般都是直接关掉,都已经登录了还让用户注册,什么鬼!由于我做的是评论功能,我并不希望评论用户和现有用户表打通,所以就不存在这件事了,如果想打通的话,我觉得无非就是登录成功之后默认往老用户表插入一条数据,然后和OpenUser表关联起来,判断用户是否登录时把OpenUser的鉴权也加进去就OK了。

  本文的后台以Java为例。

  2.1. 数据库设计再来说说数据库设计,为了系统的扩展性,我有一个专门的OpenUser表用来存放第三方登录用户,主要字段如下:

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

  2.2. 鉴权流程这里我只是说说我的方案,把accessToken写入cookie肯定是不安全的,因为accessToken相当于是第三方网站的临时密码,被别人窃取了就可以随意拿来干坏事了。可以在用户登录成功之后我们自己生成一个token,这样的token即使泄露了顶多就是被人拿来随意评论,损失不大,但是如果accessToken被泄露了,以微博为例,人家可以利用这个accessToken随意发微博、删微博、加关注等等,很危险。当然,如果不想token泄露的话也可以通过绑定IP等方式来限制。

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

  QQ登陆

  3.1. 实名认证

  QQ登录我们对接的是QQ互联,地址:connect.qq ,首先需要注册成为开发者并实名认证,需要手持身份证照片,具体就不讲了。

  3.2. 创建应用

  进入应用管理页面(connect.qq/manage.html#/)创建应用,根据实际需要是创建网站应用还是移动应用,我这里是网站应用:

  

  第一步:

  

  第二步:

  

  提交完之后会自动提交审核,基本上就是审核你的资料和备案的资料是否一致,所有资料必须和备案资料一模一样,否则审核不会通过:

  

  当然,这些资料后面还是可以修改的。申请成功之后你会得到appId和appKey。

  3.3. 引导用户登录

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

  

  代码:

  function openWindow(url, width, Height</span>){ 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='test/auth'; // 前面设置的回调地址 var state='fjdslfjsdlkfd'; // 防止CSRF攻击的随机参数,必传,登录成功之后会回传,最好后台自己生成然后校验合法性 openWindow(`graph.qq/oauth2.0/authorize?response_type=token&client_id=${qqAppId}&redirect_uri=${encodeURIComponent(qqAuthPath)}&state=${state}`);}

  然后会打开一个授权页面,这个页面大家应该都熟悉:

  

  然后到了这里我就碰到一个问题了,官方文档(wiki.connect.qq)写的是登录成功之后首先会回传一个code,然后再拿code调接口换取accessToken,然后我试了很多次也换过2个账号发现每次都是直接返回了accessToken,帮我省了一步了,不知道是什么情况,郁闷。微信搜索 Web项目聚集地 获取更多实战教程。

  3.4. 拿到accessToken现在假设我们都是直接拿到accessToken(因为我暂时还没搞明白QQ为啥会直接返回,跟文档说的不一样),但是授权回调时accessToken会被放在# 后面,URL地址中的hash值好像不会被传到后台(貌似是这样,如有不正确欢迎评论指正),所以只能写一个下面这样的临时页面:

  @RequestMapping("/authqq")public void authQQ(HttpServletRequest request, HttpServletResponse response) throws Exception{ // QQ登录有点特殊,参数放在#后面,后台无法获取#后面的参数,只能用JS做中间转换 String html="" + "" + "" + " " + " " + "" + "" + " " + "" + ""; response.getWriter.print(html);}

  3.5. 获取openId

  根据accessToken调接口获取用户的openId,特别注意这个openId是相对于QQ号+appId唯一的,换句话说同一个QQ号登录2个不同appId时获取到的openId是不同的。顺便说一句,QQ登录的相关接口做的还真够“随便”的,全部都是最简单的get请求,所以对接起来非常顺利。微信搜索 Web项目聚集地 获取更多实战教程。

  直接看代码:

  // 根据accessToken换取openId// 富贵论坛错误示例:callback( {"error":100016,"error_description":"access token check failed"} );// 正确示例:callback( {"client_id":"10XXXXX49","openid":"CF2XXXXXXXX9F4C"} );String result=HttpsUtil.get("graph.qq/oauth2.0/me?access_token=" + accessToken);Map resp=parseQQAuthResponse(result); // 这个方法就是把结果转Map// 欢迎关注 Web项目聚集地 获取更多实战教程Integer errorCode=(Integer)resp.get("error");String errorMsg=(String)resp.get("error_description");String openId=(String)resp.get("openid");if(errorCode !=) return new ErrorResult(errorCode, "获取QQ用户openId失败:"+errorMsg);

  3.6. 获取用户头像昵称等信息

  // 获取用户昵称、头像等信息,{ret: 0, msg: '', nickname: '', ...} ret不为0表示失败result=HttpsUtil.get("graph.qq/user/get_user_info?access_token="+accessToken+"&oauth_consumer_key="+appId+"&openid="+openId);resp=JsonUtil.parseJsonToMap(result);// 欢迎关注 Web项目聚集地 获取更多实战教程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");

  3.7. 注意事项

  到了这一步基本上涉及第三方的就结束了,是不是很简单?后面无非就是如何插入数据库、如何保存token、写入session等。

  有几点注意事项:

  需要注意数据库中是否已经有改用户,没有的添加,有的修改,不要重复添加了;

  QQ昵称昵称有各种奇奇怪怪的字符,包括emoji,MySQL默认没有开启utf8mb4,直接插入会报错,所以需要过滤掉;

  需要做好对各种错误的兼容;

  接口会同时返回QQ头像和空间头像,QQ头像不一定有,空间头像一定有;

  回调地址必须和申请的域名一致,否则会报错。

  QQ互联有个特大的bug,有时候显示已登录但是点击授权管理一直报错,此时只需要退出重新登录即可;

  授权之后用户可能会在过期之前提前取消授权;

  微信搜索 Web项目聚集地 获取更多实战教程。

  相关文档官网已经写得比较细了,但是比较乱:wiki.connect.qq/

  对接微博登陆4.1. 实名认证

  这个我就不具体讲了,登录 open.weibo/ 很容易找到相关入口,注册成为开发者,实名认证,一模一样的。

  4.2. 创建应用

  点击链接 open.weibo/apps/new?sort=web 创建web应用:

  

  创建成果后完善相关信息,主要是下面这些:

  

  我就不一一介绍了,都看得懂。

  微博登录不需要网站一定要备案,但对网站本身有一定要求,不能弄一个空壳网站让人家去审核,肯定审核不通过的。

  有关微博的对接可以参考我好几年前写的一篇文章:

  cnblogs/liuxianan/archive/2012/11/11/2765123.html

  4.3. 引导用户登录

  微博视觉素材(open.weibo/wiki/微博标识下载)下载在这里,页面合适位置放一个登录按钮:

  function weiboLogin{ let weiboAppId='432432'; let weiboAuthPath='test/authweibo'; openWindow(`api.weibo/oauth2/authorize?client_id=${weiboAppId}&response_type=code&redirect_uri=${encodeURIComponent(weiboAuthPath)}`);}

  微博登录有一个好处,第一次登录需要授权,后面第二次登录时只会一闪而过自动就登录成功了,都不需要点一下,用户体验非常好,看下图:

  

  4.4. 获取accessToken

  登录成功会返回一个code,根据code换取accessToken:

  String params="client_id=" + appId + "&client_secret=" + appSecret + "&grant_type=authorization_code" + "&redirect_uri=" + URLUtil.encode(authPath) + "&code=" + code;// 用code换取accessTokenString result=HttpsUtil.post("api.weibo/oauth2/access_token", params);Map resp=JsonUtil.toObject(result, new TypeReference>{});

  Integer errorCode=(Integer)resp.get("error_code");String error=(String)resp.get("error");String errorMsg=(String)resp.get("error_description");if(errorCode !=&& errorCode !=0) return new ErrorResult(errorCode, error + (errorMsg==?"":errorMsg));String accessToken=(String)resp.get("access_token");String uid=(String)resp.get("uid"); // 这个uid就是微博用户的唯一用户ID,可以通过这个id直接访问到用户微博主页int expires=(Integer)resp.get("expires_in"); // 有效期,单位秒

  4.5. 获取用户头像等信息

  // 用uid和accessToken换取用户信息String result=HttpsUtil.get("api.weibo/2/users/show.json?access_token="+accessToken+"&uid="+uid);Map resp=JsonUtil.toObject(result, new TypeReference>{});

  errorCode=(Integer)resp.get("error_code");error=(String)resp.get("error");errorMsg=(String)resp.get("error_description");if(errorCode !=&& errorCode !=0) return new ErrorResult(errorCode, error + (errorMsg==?"":errorMsg));

  String nickname=(String)resp.get("screen_name");// 微博180*180高清头像String avatar=(String)resp.get("avatar_large");String gender=(String)resp.get("gender");gender="m".equals(gender) ? "男" : ("f".equals(gender) ? "女" : "");

  至此涉及第三方的东西都完了,剩下的就是用户自己保存到数据库、写入token

  保存 session 以及鉴权接口开发了。

  4.6. 注意事项

  微博接口都有频率限制,不过一般不会超过;

  需做好错误兼容;

  微博直接返回的uid,可以根据这个uid直达用户微博主页 weibo/u/xxxxx ,所以可以把用户头像链接到这里;

  其实也有现成的js-sdk,可以根据自己实际需要选择是否使用;

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 QQ 登录需要使用 QQ 提供的开放平台 API。具体步骤如下: 1. 注册成为 QQ 开放平台开发者,创建应用,获取应用 ID 和密钥。 2. 使用 Java 编写程序,调用 QQ 提供的 OAuth2 授权 API,引导用户授权登录并获取用户的 OpenID。 3. 根据用户的 OpenID,查询数据库获取用户的账户信息(例如用户名、密码等),并在用户登录界面显示多个账户供用户选择。 4. 用户选择账户后,使用选中的账户信息进行登录。 以下是一个简单的示例代码: ```java public class QQLogin { private static final String APP_ID = "your_app_id"; private static final String APP_KEY = "your_app_key"; private static final String REDIRECT_URI = "http://your_redirect_uri"; private static final String SCOPE = "get_user_info"; public static void main(String[] args) throws IOException { // Step 1: 获取授权码 String authCode = getAuthCode(); // Step 2: 获取访问令牌 String accessToken = getAccessToken(authCode); // Step 3: 获取用户 OpenID String openId = getOpenId(accessToken); // Step 4: 查询数据库,获取账户列表 List<Account> accounts = getAccounts(openId); // Step 5: 显示账户列表供用户选择 Account selectedAccount = showAccountSelection(accounts); // Step 6: 使用选中的账户信息进行登录 login(selectedAccount); } private static String getAuthCode() throws IOException { String authorizeUrl = String.format("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=%s", APP_ID, REDIRECT_URI, SCOPE); System.out.println("请在浏览器中打开以下网址并授权登录:"); System.out.println(authorizeUrl); System.out.println("授权成功后,请在浏览器地址栏中复制 code 参数的值并粘贴到程序中:"); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); return reader.readLine(); } private static String getAccessToken(String authCode) throws IOException { String accessTokenUrl = String.format("https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s", APP_ID, APP_KEY, authCode, REDIRECT_URI); String response = HttpUtils.get(accessTokenUrl); String accessToken = StringUtils.substringBetween(response, "access_token=", "&"); System.out.println("Access Token: " + accessToken); return accessToken; } private static String getOpenId(String accessToken) { String openIdUrl = String.format("https://graph.qq.com/oauth2.0/me?access_token=%s", accessToken); String response = HttpUtils.get(openIdUrl); String openId = StringUtils.substringBetween(response, "\"openid\":\"", "\"}"); System.out.println("OpenID: " + openId); return openId; } private static List<Account> getAccounts(String openId) { // 查询数据库,获取账户列表 ... } private static Account showAccountSelection(List<Account> accounts) { // 显示账户列表供用户选择 ... } private static void login(Account account) { // 使用选中的账户信息进行登录 ... } } ``` 注意:以上示例代码中的 getAuthCode()、HttpUtils.get()、StringUtils.substringBetween()、getAccounts()、showAccountSelection()、login() 等方法需要根据实际情况进行实现。同时,为了简化示例代码,省略了异常处理、日志记录等必要的功能。实际项目中应该加上这些功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值