时间:2023-05-20 18:16:02 | 来源:网站运营
时间:2023-05-20 18:16:02 来源:网站运营
Java实现QQ登录和微博登录:uid
,QQ是加密的openId
)以及用来识别身份的accessToken
,当然还有昵称、头像、性别等有限资料,对接第三方登录的关键就是如何确定用户是合法登录,如果确定这次登录的和上次登录的是同一个人并且不是假冒的。其实这个并不用我们特别操心,就以微博登录为例,用户登录成功之后会回调一个code给我们,然后我们再拿code去微博那换取accessToken
,如果这个code是用户乱填的,那这一关肯定过不了,所以,前面的担心有点多余,哈哈。OpenUser
表关联起来,判断用户是否登录时把OpenUser
的鉴权也加进去就OK了。OpenUser
表用来存放第三方登录用户,主要字段如下:accessToken
写入cookie肯定是不安全的,因为accessToken
相当于是第三方网站的临时密码,被别人窃取了就可以随意拿来干坏事了。可以在用户登录成功之后我们自己生成一个token,这样的token即使泄露了顶多就是被人拿来随意评论,损失不大,但是如果accessToken被泄露了,以微博为例,人家可以利用这个accessToken
随意发微博、删微博、加关注等等,很危险。当然,如果不想token泄露的话也可以通过绑定IP等方式来限制。appId
和appKey
。/** * 封装一个居中打开新窗口的方法 */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}`);}
然后会打开一个授权页面,这个页面大家应该都熟悉:#
后面,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);}
3.5. 获取openIdQQ号+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); // 这个方法就是把结果转MapInteger 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);
3.6. 获取用户头像昵称等信息// 获取用户昵称、头像等信息,{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");
3.7. 注意事项utf8mb4
,直接插入会报错,所以需要过滤掉;function weiboLogin(){ let weiboAppId = '432432'; let weiboAuthPath = 'http://www.test.com/authweibo'; openWindow(`https://api.weibo.com/oauth2/authorize?client_id=${weiboAppId}&response_type=code&redirect_uri=${encodeURIComponent(weiboAuthPath)}`);}
微博登录有一个好处,第一次登录需要授权,后面第二次登录时只会一闪而过自动就登录成功了,都不需要点一下,用户体验非常好,看下图:String params = "client_id=" + appId + "&client_secret=" + appSecret + "&grant_type=authorization_code" + "&redirect_uri=" + URLUtil.encode(authPath) + "&code=" + code;// 用code换取accessTokenString result = HttpsUtil.post("https://api.weibo.com/oauth2/access_token", params);Map<String, Object> resp = JsonUtil.toObject(result, new TypeReference<Map<String, Object>>(){});Integer errorCode = (Integer)resp.get("error_code");String error = (String)resp.get("error");String errorMsg = (String)resp.get("error_description");if(errorCode != null && errorCode != 0) return new ErrorResult(errorCode, error + (errorMsg==null?"":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("https://api.weibo.com/2/users/show.json?access_token="+accessToken+"&uid="+uid);Map<String, Object> resp = JsonUtil.toObject(result, new TypeReference<Map<String, Object>>(){});errorCode = (Integer)resp.get("error_code");error = (String)resp.get("error");errorMsg = (String)resp.get("error_description");if(errorCode != null && errorCode != 0) return new ErrorResult(errorCode, error + (errorMsg==null?"":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、以及鉴权接口开发了。作者:我是小茗同学
链接:https://www.cnblogs.com/liuxianan/p/java-qq-weibo-login.html
来源:博客园
关键词:实现