QQ第三方授权登录OAuth2.0实现(Java)

准备材料

  • 1.已经备案好的域名
  • 2.服务器(域名和服务器为统一主体或域名已接入服务器)
  • 3.QQ号
  • 4.开发流程:https://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0

创建应用

  • 1.访问 https://connect.qq.com/manage.html ,登录。
  • 2.创建网站应用,填写网站基本信息以及平台信息,提交审核。注:网站回调域后续会用到,是点击授权登录时回调地址,需要与后续开发一致。

程序开发

1. 添加QQ登录按钮,用于点击跳转至QQ授权登录页

<a href="/account/qqConnect" class="blog-user"> <i
                class="fa fa-qq"></i>
            </a>

2. Java后台实现页面跳转

2.1 编写一个工具类

QQUtil

package cn.zwqh.springboot.common.qq;
import java.io.IOException;
import java.net.URLEncoder;

import org.apache.http.client.ClientProtocolException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

import cn.zwqh.springboot.common.http.HttpClientUtils;
import cn.zwqh.springboot.entity.sys.User;

public class QQUtil {
    private static final Logger log = LoggerFactory.getLogger(QQUtil.class);

    private static final String QQ_APP_ID="XXX";//改成自己的
    private static final String QQ_APP_SECRET="XXX";//改成自己的
    private static final String LOGIN_REDIRECT_URI="https://www.zwqh.top/account/qqLogin";  //改成自己的
    private static final String BIND_REDIRECT_URI="https://www.zwqh.top/account/qqBind";                                      //改成自己的
    private static final String AUTH_CODE_URL="https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id="+QQ_APP_ID+"&redirect_uri=REDIRECT_URI&state=STATE";
    private static final String ACCESS_TOKEN_URL="https://graph.qq.com/oauth2.0/token?client_id="+QQ_APP_ID+"&client_secret="+QQ_APP_SECRET+"&code=CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI";
    private static final String REFRESH_TOKEN_URL="https://graph.qq.com/oauth2.0/token?client_id="+QQ_APP_ID+"&client_secret="+QQ_APP_SECRET+"&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";
    private static final String OPEN_ID_URL="https://graph.qq.com/oauth2.0/me?access_token=ACCESS_TOKEN";
    private static final String USER_INFO_URL="https://graph.qq.com/user/get_user_info?access_token=ACCESS_TOKEN&oauth_consumer_key="+QQ_APP_ID+"&openid=OPENID";

    public static JSONObject getJsonStrByQueryUrl(String paramStr){
        String[] params = paramStr.split("&");
        JSONObject obj = new JSONObject();
        for (int i = 0; i < params.length; i++) {
            String[] param = params[i].split("=");
            if (param.length >= 2) {
                String key = param[0];
                String value = param[1];
                for (int j = 2; j < param.length; j++) {
                    value += "=" + param[j];
                }
                try {
                    obj.put(key,value);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }
        return obj;
    }
    /**
     * 获取授权登录页码url
     * @return
     */
    public static String getLoginConnectUrl(String state) {
        String url=null;
        try{
            url=AUTH_CODE_URL.replace("REDIRECT_URI", URLEncoder.encode(LOGIN_REDIRECT_URI, "utf-8")).replace("STATE", state);
        }catch (Exception e) {
            log.error(e.toString());
        }
        return url;
    }

    /**
     * 获取授权绑定页码url
     * @return
     */
    public static String getBindConnectUrl() {
        String url=null;
        try{
            url=AUTH_CODE_URL.replace("REDIRECT_URI", URLEncoder.encode(BIND_REDIRECT_URI, "utf-8"));
        }catch (Exception e) {
            log.error(e.toString());
        }
        return url;
    }

    /**
     * 获取AccessToken
     * @return 返回拿到的access_token及有效期
     */
    public static QQAccessToken getQQLoginAccessToken(String code) throws ClientProtocolException, IOException{
        QQAccessToken token = new QQAccessToken();
        String url = ACCESS_TOKEN_URL.replace("CODE", code).replace("REDIRECT_URI", URLEncoder.encode(LOGIN_REDIRECT_URI, "utf-8"));
        log.info("这是请求路径:"+url);
        String result = HttpClientUtils.doGet(url);
        JSONObject jsonObject=getJsonStrByQueryUrl(result);
        log.info("这是返回结果:"+jsonObject);
        if(jsonObject!=null){ //如果返回不为空,将返回结果封装进AccessToken实体类
            token.setAccessToken(jsonObject.getString("access_token"));//接口调用凭证
            token.setExpiresIn(jsonObject.getInteger("expires_in"));//access_token接口调用凭证超时时间,单位(秒)
            token.setRefreshToken(jsonObject.getString("refresh_token"));
        }
        return token;
    }

    /**
     * 获取AccessToken
     * @return 返回拿到的access_token及有效期
     */
    public static QQAccessToken getQQBindAccessToken(String code) throws ClientProtocolException, IOException{
        QQAccessToken token = new QQAccessToken();
        String url = ACCESS_TOKEN_URL.replace("CODE", code).replace("REDIRECT_URI", URLEncoder.encode(BIND_REDIRECT_URI, "utf-8"));
        log.info("这是请求路径:"+url);
        String result = HttpClientUtils.doGet(url);
        JSONObject jsonObject=getJsonStrByQueryUrl(result);
        log.info("这是返回结果:"+jsonObject);
        if(jsonObject!=null){ //如果返回不为空,将返回结果封装进AccessToken实体类
            token.setAccessToken(jsonObject.getString("access_token"));//接口调用凭证
            token.setExpiresIn(jsonObject.getInteger("expires_in"));//access_token接口调用凭证超时时间,单位(秒)
            token.setRefreshToken(jsonObject.getString("refresh_token"));
        }
        return token;
    }

    /**
     * 刷新或续期access_token使用
     * @return 返回拿到的access_token及有效期
     */
    public static QQAccessToken refreshQQAccessToken(String refreshToken) throws ClientProtocolException, IOException{
        QQAccessToken token = new QQAccessToken();
        String url = REFRESH_TOKEN_URL.replace("REFRESH_TOKEN",refreshToken);
        log.info("这是请求路径:"+url);
        String result = HttpClientUtils.doGet(url);
        log.info("这是返回结果:"+result);
        JSONObject jsonObject=getJsonStrByQueryUrl(result);
        log.info("这是转为json的结果:"+jsonObject);
        if(jsonObject!=null){ //如果返回不为空,将返回结果封装进AccessToken实体类
            token.setAccessToken(jsonObject.getString("access_token"));//接口调用凭证
            token.setExpiresIn(jsonObject.getInteger("expires_in"));//access_token接口调用凭证超时时间,单位(秒)
            token.setRefreshToken(jsonObject.getString("refresh_token"));
        }
        return token;
    }
    /**
     * 获取QQopenId
     * @return QQopenId
     */
    public static String getQQOpenId(String accessToken) throws ClientProtocolException, IOException{
        String url = OPEN_ID_URL.replace("ACCESS_TOKEN",accessToken);
        log.info("这是请求路径:"+url);
        String result = HttpClientUtils.doGet(url).replace("callback(", "").replace(");", "");
        log.info("这是返回结果:"+result);
        JSONObject jsonObject=JSON.parseObject(result);
        log.info("这是转为json的结果:"+jsonObject);
        if(jsonObject!=null&&jsonObject.getString("openid")!=null){ //如果返回不为空
            return jsonObject.getString("openid");
        }
        return null;
    }
    /**
     * 获取QQ用户信息
     * @param accessToken
     * @param openId
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    public static JSONObject getUserInfo(String accessToken, String openId) throws ClientProtocolException, IOException {
        // 拼接请求地址
        String url = USER_INFO_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
        log.info("这是请求路径:"+url);
        String result = HttpClientUtils.doGet(url);
        log.info("这是返回结果:"+result);
        JSONObject jsonObject=JSONObject.parseObject(result);
        log.info("这是转为json的结果:"+jsonObject);
        JSONObject json=new JSONObject();
        if (jsonObject!=null&&jsonObject.getInteger("ret").equals(0)) {
            try {
                User user= new User();
                // 用户的标识
                user.setQqId(openId);
                // 昵称
                user.setNickname(jsonObject.getString("nickname"));
                if(jsonObject.getString("figureurl_2")!=null&&!jsonObject.getString("figureurl_2").isEmpty()) {
                      // 用户头像
                    user.setAvatar(jsonObject.getString("figureurl_qq_2"));
                }else {
                      // 用户头像
                    user.setAvatar(jsonObject.getString("figureurl_qq_1"));
                }
                json.put("success", true);
                json.put("msg", "success");
                json.put("user", user);
            } catch (Exception e) {
                int errorCode = jsonObject.getInteger("ret");
                String errorMsg = jsonObject.getString("msg");
                log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, e.toString());
                json.put("success", false);
                json.put("msg", errorMsg);
                json.put("user", null);
            }
        }else {
              json.put("success", false);
              json.put("msg", "请先登录");
              json.put("user", null);
        }
        return json;
    }
}

QQAccessToken

package cn.zwqh.springboot.common.qq;

import java.io.Serializable;

public class QQAccessToken implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 5258435811207021018L;
    private String accessToken;//接口调用凭证
    private int expiresIn;//access_token接口调用凭证超时时间,单位(秒)
    private String openid;//授权用户唯一标识
    private String refreshToken;//用户刷新access_token
    private String scope;//用户授权的作用域,使用逗号(,)分隔

    public String getOpenid() {
        return openid;
    }

    public void setOpenid(String openid) {
        this.openid = openid;
    }

    public String getAccessToken() {
        return accessToken;
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public int getExpiresIn() {
        return expiresIn;
    }

    public void setExpiresIn(int expiresIn) {
        this.expiresIn = expiresIn;
    }

    public String getRefreshToken() {
        return refreshToken;
    }

    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

}
2.2 Controller层实现
package cn.zwqh.springboot.action.web;

import java.io.IOException;
import java.util.Date;
import java.util.UUID;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;

import cn.zwqh.springboot.action.BaseAction;
import cn.zwqh.springboot.common.CookieUtil;
import cn.zwqh.springboot.common.DateUtil;
import cn.zwqh.springboot.common.EscapeUnescape;
import cn.zwqh.springboot.common.qq.QQAccessToken;
import cn.zwqh.springboot.common.qq.QQUtil;
import cn.zwqh.springboot.common.redis.RedisHandle;
import cn.zwqh.springboot.entity.SessionUser;
import cn.zwqh.springboot.entity.sys.User;
import cn.zwqh.springboot.service.UserService;

@Controller
@RequestMapping("/account")
public class AccountAction extends BaseAction {

    /**
     *
     */
    private static final long serialVersionUID = 1729415442021645693L;

    @Resource
    private RedisHandle redisHandle;

    @Autowired
    private UserService userService;

    /**
     * 跳转至QQ登录界面
     */
    @RequestMapping("/qqConnect")
    @ResponseBody
    public void qqConnect() {
        try {
            String referer = getRequest().getHeader("REFERER");
            String state = DateUtil.formatUserDefineDate(new Date(), "yyyyMMddHHmmssSSS");
            redisHandle.set(state, referer, 60 * 30L);
            getResponse().sendRedirect(QQUtil.getLoginConnectUrl(state));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * QQ第三方登录
     *
     * @throws Exception
     */
    @RequestMapping("/qqLogin")
    @ResponseBody
    public void qqLogin() throws Exception {
        String code = getRequest().getParameter("code");
        String state = getRequest().getParameter("state");
        System.out.println("code = " + code + ", state = " + state);
        if (code != null && !"".equals(code)) {
            QQAccessToken qqAccessToken = QQUtil.getQQLoginAccessToken(code);
            if (qqAccessToken.getAccessToken().equals("")) {
                // 我们的网站被CSRF攻击了或者用户取消了授权
                // 做一些数据统计工作
                System.out.print("没有获取到响应参数");
                // 跳转返回地址
                outJsonFailure("未获取到AccessToken,请重新进行QQ授权登录");
            } else {
                QQAccessToken qqAccessToken2 = QQUtil.refreshQQAccessToken(qqAccessToken.getRefreshToken());
                String accessToken = qqAccessToken2.getAccessToken();
                String referer = redisHandle.get(state).toString();
                redisHandle.set(accessToken, referer, 60 * 30L);
                redisHandle.remove(state);
                getResponse().sendRedirect("https://www.zwqh.top/account/getQQUserInfo?qqAccessToken=" + accessToken);

            }
        } else {
            outJsonFailure("缺少code参数");
        }
    }

    /**
     * 获取QQ用户信息
     *
     * @param qqAccessToken
     * @throws Exception
     */
    @GetMapping("/getQQUserInfo")
    public String getQQUserInfo(String qqAccessToken) throws Exception {
        System.out.println("accessToken = " + qqAccessToken);
        String referer = redisHandle.get(qqAccessToken).toString();
        if (qqAccessToken != null && !"".equals(qqAccessToken)) {
            try {
                String qqOpenId = QQUtil.getQQOpenId(qqAccessToken);
                if (qqOpenId != null) {
                    System.out.println("**************qq登录成功 qqOpenId = " + qqOpenId);
                    // 获取QQ用户信息
                    JSONObject object = QQUtil.getUserInfo(qqAccessToken, qqOpenId);
                    // 数据库中判断qqOpenId是否存在,存在则登录,不存在则注册
                    User user = userService.getUserByQQOpenId(qqOpenId);
                    if (user != null) {
                        user.setAvatar(object.getJSONObject("user").getString("avatar"));
                        user.setNickname(object.getJSONObject("user").getString("nickname"));
                        user.setLastLoginTime(DateUtil.formatDateTime(new Date()));
                        userService.updateUser(user);
                        SessionUser suser = SessionUser.getInstance(user);
                        String token = UUID.randomUUID().toString();
                        redisHandle.set(token, suser, 60 * 60L * 24 * 7);// 设置用户缓存及过期时间(一星期)
                        JSONObject data = new JSONObject();
                        data.put("userId", user.getId());
                        data.put("nickname", user.getNickname());
                        data.put("avatar", user.getAvatar());
                        data.put("token", token);
                        CookieUtil.setValue(getResponse(), "loginUser", data.toString());
                    } else {
                        user = new User();
                        user.setAvatar(object.getJSONObject("user").getString("avatar"));
                        user.setNickname(object.getJSONObject("user").getString("nickname"));
                        user.setLastLoginTime(DateUtil.formatDateTime(new Date()));
                        user.setRegisterTime(DateUtil.formatDateTime(new Date()));
                        user.setQqId(qqOpenId);
                        userService.insertUser(user);
                        SessionUser suser = SessionUser.getInstance(user);
                        String token = UUID.randomUUID().toString();
                        redisHandle.set(token, suser, 60 * 60L * 24 * 7);// 设置用户缓存及过期时间(一星期)
                        JSONObject data = new JSONObject();
                        data.put("userId", user.getId());
                        data.put("nickname", user.getNickname());
                        data.put("avatar", user.getAvatar());
                        data.put("token", token);
                        CookieUtil.setValue(getResponse(), "loginUser", data.toString());
                    }

                } else {
                    putInRequest("error", "未获取到用户openid,请重新QQ授权登录");
                }
            } catch (Exception e) {
                e.printStackTrace();
                putInRequest("error", "登录异常");
            }
        } else {
            putInRequest("error", "缺少code参数");
        }
        return "redirect:" + referer;
    }
    /**
     * 退出登录
     * @return
     */
    @RequestMapping("/logout")
    public String logout() {
        String referer = getRequest().getHeader("REFERER");
        String data= CookieUtil.getCookieValue(getRequest(), "loginUser");
        if(data!=null&&data!="") {
            JSONObject user=JSONObject.parseObject(EscapeUnescape.unescape(data));
            String token=user.getString("token");
            redisHandle.remove(token);
            CookieUtil.deleteValue("loginUser",getResponse());
        }
        return "redirect:" + referer;
    }
}
2.3 JavaScript 处理页面
var data=eval('('+unescape(getCookie("loginUser"))+')');
    var a = document.getElementsByClassName("blog-user")[0];
    if(data!=null){
        a.setAttribute("href","/account/logout");
        a.innerHTML='<img alt="'+data.nickname+'" title="'+data.nickname+'" src="'+data.avatar+'" class="layui-circle" width="40px" height="40px">';
    }else{
        a.setAttribute("href","/account/qqConnect");
        a.innerHTML='<i class="fa fa-qq"></i>';
    }

总结

总的来说QQ授权登录还是很简单的,该方法使用web端以及wap端。

原文地址:https://www.cnblogs.com/zwqh/p/11579275.html

时间: 2024-11-06 13:37:21

QQ第三方授权登录OAuth2.0实现(Java)的相关文章

springboot开发qq第三方授权登录

前几天随手写了一个qq第三方授权登录功能,现总结一下(这里以个人开发网站应用为例): 首先要成为qq互联开发者:https://connect.qq.com/index.html申请步骤请参考文档和百度:https://wiki.connect.qq.com/%E6%88%90%E4%B8%BA%E5%BC%80%E5%8F%91%E8%80%85等待审核通过,通过之后会看到(示例): 然后开始创应用,我这边是网站应用,创建流程请参考文档https://wiki.connect.qq.com/_

基于OAuth2.0协议的QQ第三方授权登录iOS代码分析

简要说明: 授权登录已经成为注册方式里的主流,目前授权登录方式主要SSO跳转授权登录和OAuth2.0两种,前者好处无需用户再次输入密码就可以直接授权成功,但前提是必须用户手机端安装了该软件,比如QQ,后者的优势就是是否安装无关紧要,是一个HTML的页面呈现,麻烦就在于要输入用户名和密码,这就非常不爽了,但是有时候偏偏必须这么做,理由嘛,自行想想就好,接下来我们就看看如果利用OAuth2.0的方式来做QQ授权登录,如果想了解QQ的SSO授权登录,可以看我(博客主页)之前的博客:基于第三方QQ授权

什么是“QQ登录OAuth2.0”

1. 什么是“QQ登录OAuth2.0 OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容. QQ登录OAuth2.0:对于用户相关的OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权.QQ登录OAuth2.0采用OAuth2.0标准协议来进行用户身份验证和获取用户授权,相对于

【Android应用开发详解】实现第三方授权登录、分享以及获取用户资料

由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折,查阅了一番资料,做了一个Demo.实现起来的效果还是不错的,不敢独享,决定写一个总结的教程,供大家互相交流.学习和参考,博主只求能和大家共同进步.希望能多多支持! 这篇文章中,我们使用到了Share SDK,它是为iOS.Android.WP8的APP提供社会化功能的一个组件,目前支持如QQ.微信.新浪微博.腾讯微博.开心网.人人网.豆瓣.网易微博.搜狐微博.facebook.twitter.googl

【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料

转载请注明出处:http://blog.csdn.net/yangyu20121224/article/details/9057257 由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折,查阅了一番资料,做了一个Demo.实现起来的效果还是不错的,不敢独享,决定写一个总结的教程,供大家互相交流.学习和参考,博主只求能和大家共同进步.希望能多多支持! 这篇文章中,我们使用到了Share SDK,它是为iOS.Android.WP8的APP提供社会化功能的一

【转】【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料

转载请注明出处:http://blog.csdn.net/yangyu20121224/article/details/9057257 由于公司项目的需要,要实现在项目中使用第三方授权登录以及分享文字和图片等这样的效果,几经波折,查阅了一番资料,做了一个Demo.实现起来的效果还是不错的,不敢独享,决定写一个总结的教程,供大家互相交流.学习和参考,博主只求能和大家共同进步.希望能多多支持! 这篇文章中,我们使用到了Share SDK,它是为iOS.Android.WP8的APP提供社会化功能的一

开放授权协议OAuth2.0简介

原文地址: http://stackvoid.com/introduce-to-oath2.0/ 可能你跟我一样,使用过各种第三方开放授权库(如在你的 APP 中获取 QQ 照片或微博评论等)来获取用户的一些资源,今天跟大家总结分享一下开放授权(OAuth2.0,1.0太复杂已经被弃用)的概念和原理,在以后使用开放授权SDK时能快速高效完成. OAuth解决了什么问题? OAuth 产生主要解决了第三方应用访问用户在某网站网络资源的问题.例如,上图中,用户在36氪登陆时,可以用 QQ 来登陆,点

SPA+.NET Core3.1 GitHub第三方授权登录 使用AspNet.Security.OAuth.GitHub

使用SPA+.NET Core3.1实现 GitHub第三方授权登录 类似使用AspNet.Security.OAuth.GitHub,前端使用如下:VUE+Vue-Router+axios AspNet.Security.OAuth.GitHub GitHub https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers GitHub授权登录 什么配置的过程不说了..有一推. GitHub 第三方登录 给你的网站添加第三方登

Github 第三方授权登录教程

Github 第三方授权登录教程 ####大致流程图 ####1.首先注册一个github帐号,Applications>Developer applications>Register a new application. ####2.填入参数 Application name--应用名称,随意填 Homepage URL--如上图可以填本地地址进行测试,127.0.0.1:port/xx/xx Application description--应用描述,随意填 Authorization c