很久之前在一个Web项目中本来打算想接入QQ登录的,然后在网上找了一些相关demo,然后由于比较菜改了半天还是没有运行起来。后来由于时间原因就先搁置了,最近自己写了一个demo实现了接入QQ登录教程,顺便写一个 Spring Boot项目接入QQ登录教程 ,记录一下QQ OAuth2.0接入过程。

Step 1 :创建项目

使用IDEA创建一个Spring Boot项目,依赖下面会给出

重要依赖

  1. okhttp(用于后台发起get或者post请求,官方地址
  2. fastjson
  3. thymeleaf
  4. lombok(IDEA也要装插件,没有请手动设置User实体类的setter和getter)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>live.yremp</groupId>
    <artifactId>qqconnection</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>qqconnection</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
<!--        okhttp依赖-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.14.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties 配置信息

#QQ互联申请的应用id
QQAPPID=101813698
#QQ互联申请的应用key
QQAPPKEY=7265da646c6cf33f6326f6844c1c4ef3
#应用回调地址 ':' 和 '/'等特殊字符需要转义 原本的url是 http://127.0.0.1:8080/login
redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Flogin

Step 2 :Controller

IndexController.class

@Controller
public class IndexController {
    @Autowired
    private QQConnection qqConnection;
    @RequestMapping("/")
    public String index(){
        //        第一步 获取QQ登录按钮url 自己拼接,这是只是演示步骤,无实际意义
        qqConnection.getUrl();
        return "index.html";
    }

    @RequestMapping("/login")
    public Object qqLogin(@RequestParam("code")String code , @RequestParam("state") String state , Model model){
        User user = new User();
        System.out.println("第二步:获取QQ互联返回的code="+code);
//        第三步 获取access token
        String accessToken = qqConnection.getAccessToken(code);
//        第四步 获取登陆后返回的 openid、appid 以JSON对象形式返回
        JSONObject userInfo = qqConnection.getUserOpenID(accessToken);
//        第五步获取用户有效(昵称、头像等)信息  以JSON对象形式返回
        String oauth_consumer_key = userInfo.getString("client_id");
        String openid = userInfo.getString("openid");
        JSONObject userRealInfo = qqConnection.getUserInfo(accessToken,oauth_consumer_key,openid);
        user.setOpenid(openid);
        user.setNickName(userRealInfo.getString("nickname"));
        user.setAvatar(userRealInfo.getString("figureurl_qq"));
        model.addAttribute("User",user);
        return "index.html";
    }
}

Step 3 :QQConnection.class

编写工具类QQConnection.class 配合controller 完成整个登录认证流程,具体我觉得博客上没必要再写了。请配合QQ互联给的文档 和下面代码注释看,应该是没任何问题的,基本和官方文档保持一致。有问题请首页扫码加我QQ或者微信。(要是久了可能我也会忘


@Component
public class QQConnection {
//    读取appid
@Value("${QQAPPID}")
private String QQAppID;
@Value("${QQAPPKEY}")
//    读取appkey
private String QQAppKEY;
//读取redirect_uri
@Value("${redirect_uri}")
private String redirect_uri;

//    第一步:获取 QQ登录按钮url 几乎等于手动拼接 无太大意义

    public String getUrl(){
        String url = "https://graph.qq.com/oauth2.0/authorize?display=pc&response_type=code&client_id=" + QQAppID+"&redirect_uri="+redirect_uri+"&state=200";
                System.out.println("第一步:获取QQ登录按钮的url="+url);
            return null;
    }
/*
第三步:根据前一步得到的 Authorization Code 获取access token
 */
    public String getAccessToken(String code){
       String accessTaken="";
       String url = "https://graph.qq.com/oauth2.0/token?display=pc&grant_type=authorization_code&client_id="+QQAppID+"&client_secret="+QQAppKEY+"&redirect_uri="+redirect_uri+"&code="+code;
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .build();
        try (Response response = client.newCall(request).execute()) {
            assert response.body() != null;
            String responseString = response.body().string();
            accessTaken = responseString.split("=")[1].split("&")[0];
            System.out.println("第三步:获取QQ互联返回的accessTaken="+accessTaken);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return accessTaken;
    }

/*
第四步:根据得到的access token 获取用户的 openID 和 oauth_consumer_key:申请QQ登录成功后,分配给应用的openid(对当前网站应用唯一,可用于检测是否为同一用户的凭证)
 */
    public JSONObject getUserOpenID(String accessToken){
        JSONObject userInfo = new JSONObject();
        String urlProvideByQQConnection = "https://graph.qq.com/oauth2.0/me";
        String requestUrl =urlProvideByQQConnection+"?access_token="+accessToken;
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(requestUrl)
                .build();
        try (Response response = client.newCall(request).execute()) {
            assert response.body() != null;
            String UserInfoString = response.body().string().split(" ")[1];
            userInfo = JSONObject.parseObject(UserInfoString);
            System.out.println("第四步:获取QQ互联返回的openid和分配给应用的appid:"+userInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return userInfo;
    }

/*
第五步:根据 access_token、oauth_consumer_key(上一步返回的client_id)、openid 获取用户有效信息 (昵称、头像等) 返回json对象
 */
    public JSONObject getUserInfo(String access_token , String oauth_consumer_key , String openid ){
        JSONObject userRealInfo = new JSONObject();
        String urlProvideByQQConnection= "https://graph.qq.com/user/get_user_info?";
        String requestUrl = urlProvideByQQConnection+"access_token="+access_token+"&oauth_consumer_key="+oauth_consumer_key+"&openid="+openid;
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(requestUrl)
                .build();
        try (Response response = client.newCall(request).execute()) {
            assert response.body() != null;
            String UserRealInfoString = response.body().string();
            userRealInfo = JSONObject.parseObject(UserRealInfoString);
            System.out.println("第五步:获取QQ互联返回的用户有效信息:"+userRealInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return userRealInfo;
    }
}

Step 4 : User.class

这里随便写了一个实体类对应得到QQ的信息的一部分属性

@Data
public class User {
    private String nickName;
    private String avatar;
    private String openid;
}

Step 5 :前端展示

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>Spring Boot接入QQ登录</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="/layui/css/layui.css">
    <style>
        #box-success{
            margin-top: 200px;
            box-shadow: 0px 5px 40px -1px rgba(96, 118, 243, 0.31);
            text-align: center;
            height: 400px;
        }
        #box-fail{
            margin-top: 200px;
            box-shadow: 0px 5px 40px -1px rgba(209, 134, 148, 0.31);
            text-align: center;
            height: 400px;
        }
        #qqicon{
            margin-top: 0px;
            width: 200px;
            height: 200px;
        }
        #login{
            width: 40%;
            text-decoration: none;
            margin-top: 50px;

        }
    </style>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script src="/layer/layer.js"></script>
    <script src="/layui/layui.js"></script>
</head>
<body>
<div class="row">
    <div class="row">
        <div class="col-md-3">

        </div>
        <div class="col-md-6" id="container">
            <div id="box-fail" th:if="${User == null}">
                <div class="alert alert-danger" role="alert">当前未登录</div>
                    <img id="qqicon" src="/img/user.png">
                <br>
                <a  id="login" class="layui-btn layui-btn-normal"  href="https://graph.qq.com/oauth2.0/show?which=Login&display=pc&display=pc&response_type=code&client_id=101813698&redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Flogin&state=200">
                    QQ登录
                </a>
            </div>
            <div id="box-success" th:if="${User != null}">
                <div class="alert alert-success" role="alert" th:text="'登陆成功,欢迎:'+${User.nickName}+'!'"></div>
                <img id="qqicon" class="img-circle" th:src="${User.avatar}">
                <br>
            </div>
        </div>
        <div class="col-md-3">

        </div>
    </div>
</div>
</body>
</html>

Demo演示效果

未登录

跳转登录

登录之后返回

登录之后显示头像和昵称

后台输出

上面大概就是全部步骤了,我的测试应用 appid 和 appkey 也是公开的,如果你没申请到也可以直接使用(回调地址:http://127.0.0.1:8080/login ,仅用于测试使用)请合理使用。

Demo地址

Github地址: https://github.com/yremp/qq-connection

标签云

ajax AOP Bootstrap cdn Chevereto CSS Docker Editormd Hexo IDEA JavaScript jsDeliver JS樱花特效 Linux markdown Maven MyBatis MyBatis-plus MySQL Pictures QQ Sakura SEO Spring Boot SpringMVC SSR Thymeleaf V2ray Vue Web WebSocket Wechat Social WordPress Yoast SEO 代理 分页 图床 小幸运 通信原理