springboot集成qq第三方登录及获取用户信息(无自己封装的工具都写在controller里了超级易懂)_qq_41911762的博客-程序员秘密

技术标签: qq登录  第三方服务  第三方登录  oauth  

申请网站应用并不用多说了,下面是我应用的平台信息:

整个代码流程是这样的:

1.用户在login.html页面点击qq登录图标,浏览器访问我们Controller的/qqOauth

2.我们的服务器向浏览器发送重定向到qq后台

浏览器访问qq后台:"https://graph.qq.com/oauth2.0/authorize"
        + "?response_type=code"
        + "&client_id=" + APP_ID
        + "&state=" + qqState
        + "&redirect_uri=" + "http://snownihao.cn/callback";

3.qq后台向浏览器发送重定向,浏览器访问我们的回调http://snownihao.cn/callback并附带了必要的参数

4.我们服务器后台与qq后台交互,也就是callback方法里边的那三个url,具体为

4.1用第3步的参数获取accessToken 

4.2用4.1中的accessToken获取用户在此应用的openID

4.3用上面两部获取到的accessToken和openID获取用户信息

5.跳转到信息展示页面

这是获取到用户信息后的页面:

Controller:

package noon.demo_qq.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Random;


@Controller
public class test {

    final static String APP_ID = "你的appID";
    final static String APP_KEY = "你的appKey";

    @RequestMapping("/qqOauth")
    public String qqOauth(HttpSession session) {
        int rand = 10000 + new Random().nextInt(10000);
        String qqState = String.valueOf(rand);
        session.setAttribute("qqState", qqState);

        String qqAuthUrl = "https://graph.qq.com/oauth2.0/authorize"
                + "?response_type=code"
                + "&client_id=" + APP_ID
                + "&state=" + qqState
                + "&redirect_uri=" + "http://snownihao.cn/callback";
        return "redirect:" + qqAuthUrl;
    }

    @RequestMapping("callback")
    public String callback(HttpSession session
            , HttpServletRequest request
            , HttpServletResponse response
            , String code
            , String state
            , @RequestParam(defaultValue = "0") int usercancel
            , Model model) {

        String qqState = (String) session.getAttribute("qqState");
        if (!qqState.equals(state))
            return "login_failed";
        if (usercancel != 0)
            return "redirect:login.html";

/* ------------------获取用户的 accessToken------------------------ */
        String url2 = "https://graph.qq.com/oauth2.0/token"
                + "?grant_type=authorization_code"
                + "&client_id=" + APP_ID
                + "&client_secret=" + APP_KEY
                + "&code=" + code
                + "&redirect_uri=http://snownihao.cn/callback";
        HttpGet httpGet = new HttpGet(url2);
        String accessToken;
        try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();
             CloseableHttpResponse response1 = httpClient.execute(httpGet)) {

            HttpEntity entity = response1.getEntity();

                System.out.println("响应内容长度为:" + entity.getContentLength());
                String respStr = EntityUtils.toString(entity);
                System.out.println("响应内容为:" + respStr);//access_token=8*********************************0&expires_in=7126000&refresh_token=8**************************1

                accessToken = respStr.split("&")[0].split("=")[1];
                System.out.println("accessToken:" + accessToken);
                model.addAttribute("accessToken", accessToken);

        } catch (Exception e) {
            e.printStackTrace();
            return "login_failed";
        }


	    /* ------------------获取用户的 OpenID------------------------ */
        String url3 = "https://graph.qq.com/oauth2.0/me"
                + "?access_token=" + accessToken;
        String openID;
        httpGet = new HttpGet(url3);
        try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();
             CloseableHttpResponse response1 = httpClient.execute(httpGet)) {

            HttpEntity entity = response1.getEntity();
            String respStr = EntityUtils.toString(entity);
            System.out.println("响应内容为:" + respStr); // callback( {"client_id":"1*******6","openid":"F****************************5"} );

            //去掉外面的callback()
            int p0 = respStr.indexOf('(');
            int p1 = respStr.indexOf(')');
            System.out.println("p0:" + p0+" p1:" + p1);
            String jsonStr = respStr.substring(p0 + 1, p1 - 1);

            //转成json对象
            JSONObject jsonObject = JSON.parseObject(jsonStr);
            openID = jsonObject.getString("openid");
            System.out.println(openID);
            model.addAttribute("openID", openID);

        } catch (Exception e) {
            e.printStackTrace();
            return "login_failed";
        }


        /*-------------------获取用户信息----------------------*/
        String url4 = "https://graph.qq.com/user/get_user_info?" +
                "access_token=" + accessToken +
                "&oauth_consumer_key=" + APP_ID +
                "&openid=" + openID;
        httpGet = new HttpGet(url4);
        try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();
             CloseableHttpResponse response1 = httpClient.execute(httpGet)) {

            HttpEntity entity = response1.getEntity();
            String respStr = EntityUtils.toString(entity);
            System.out.println("响应内容为:" + respStr);

            //转为json对象
            JSONObject jresp = JSON.parseObject(respStr);

            //获取信息
            String nickname = jresp.getString("nickname");
            String qzoneHeader = jresp.getString("figureurl_1");
            String qHeader = jresp.getString("figureurl_qq_1");
            String gender = jresp.getString("gender");

            model.addAttribute("qHeader", qHeader);
            model.addAttribute("qzoneHeader", qzoneHeader);
            model.addAttribute("nickname", nickname);
            model.addAttribute("gender", gender);
        } catch (Exception e) {
            e.printStackTrace();
            return "login_failed";
        }


        return "after_login";
    }
}

 

 

整个案例用到的页面及配置:

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
用户名:<br>
密码:<br>
<button>登录</button>
其他登录方式:<a href="qqOauth"><img src="img/Connect_logo_3.png"></a>
</body>
</html>

素材:

after_login.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录成功</title>
</head>
<body>
accessToken: [[${accessToken}]] <br>
openID:[[${openID}]]<br>
昵称[[${nickname}]]<br>
性别:[[${gender}]]<br>
空间头像:<img th:src='${qzoneHeader}'><br><br>
QQ头像:<img th:src='${qHeader}'><br>
</body>
</html>

application.properties:

#热部署生效
spring.devtools.restart.enabled=true 
#设置重启的目录
#spring.devtools.restart.additional-paths: src/main/java
#classpath目录下的WEB-INF文件夹内容修改不重启
spring.devtools.restart.exclude= static/**,templates/**
#前缀
spring.thymeleaf.prefix=classpath:/templates/
#编码
spring.thymeleaf.encoding=UTF-8
#类型
spring.thymeleaf.servlet.content-type=text/html
#名称的后缀
spring.thymeleaf.suffix=.html
#开发时关闭缓存,不然没法看到实时页面
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML5

server.servlet.context-path=/
server.port=80

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.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>Noon</groupId>
	<artifactId>demo_qq</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo_qq</name>
	<packaging>jar</packaging>

	<description>Demo project for Spring Boot</description>
	<repositories>
		<repository>
			<id>alimaven</id>
			<name>aliyun maven</name>
			<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
		</repository>
	</repositories>
	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</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>
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.10</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.62</version>
		</dependency>
	</dependencies>

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

</project>

整个功能都写在controller里的,比较臃肿,可以自行封装

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41911762/article/details/103619429

智能推荐

Slf4j之MDC机制_org.slf4j.mdc_Bingo_xu的博客-程序员秘密

什么是MDC机制MDC(Mapped Diagnostic Contexts)映射诊断上下文,主要用在做日志链路跟踪时,动态配置用户自定义的一些信息,比如requestId、sessionId等等。MDC使用的容器支持多线程操作,满足线程安全。MDC的使用pom.xml依赖&lt;!-- 日志log4j2 --&gt;&lt;dependency&gt; &lt;groupI...

BZOJ 2120 带修改莫队_meopass的博客-程序员秘密

简略题意:单点修改,区间查询不同颜色个数。带修改莫队的板题,学习了一个。 和普通莫队的区别在于块的大小需要设定为n2/3n^{2/3},这样可以确保复杂度为O(n3/5)O(n^{3/5})。 每次询问之前需要把在当前时间点之前的所有修改用上,这之后的所有修改删掉。#define others#ifdef poj#include <iostream>#include <cstring>#

mac sudo npm install 安装依赖依旧报Error: EACCES: permission denied权限不够的话_放空di自己的博客-程序员秘密

添加--unsafe-perm=true --allow-rootsudo npm install xxxx--unsafe-perm=true --allow-root

社交推荐系统中的用户交互_文文学霸的博客-程序员秘密

文章作者:查鲁·C.阿加沃尔编辑整理:Hoh内容来源:《推荐系统原理与实践》注:文末有赠书活动,欢迎参与~导读:近年来,社会性标签系统使得用户能够以一种自由的描述方法对网络上的资源进行协...

element---组件--Data_:data=_wyk.wyk的博客-程序员秘密

Data一、table表格基础表格&lt;template&gt; &lt;el-table :data="tableData" style="width: 100%"&gt; &lt;el-table-column prop="date" label="日期" width="180"&gt;...

通过 Remote Desktop (远程桌面)在 Hyper-V 下为虚拟机安装操作系统_weixin_34301132的博客-程序员秘密

关注 Hyper-V 的朋友都知道,Hyper-V 下的虚拟机在如果没有安装 Integation Services,通过远程桌面登录到 Hyper-V 上时,是无法通过鼠标对该虚拟机进行操作的。 那么当我们必须通过远程桌面登录到 Hyper-V,并为新建立的虚拟机安装操作系统是否就不可能呢?!其实不然,虽然鼠标此刻在虚拟机中无法正常捕获...

随便推点

python入门学习--2017.9.3_weixin_30767835的博客-程序员秘密

一 编程与编程语言 python是一门编程语言,作为学习python的开始,需要事先搞明白:编程的目的是什么?什么是编程语言?什么是编程? 编程的目的:#计算机的发明,是为了用机器取代/解放人力,而编程的目的则是将人类的思想流程按照某种能够被计算机识别的表达方式传递给计算机,从而达到让计算机能够像人脑/电脑一样自动执行的效果。 什么是编程...

css面试点-CSS预处理器(Sass/Less/Postcss)_wflynn的博客-程序员秘密

CSS预处理器的原理: 是将类 CSS 语言通过Webpack 编译转成浏览器可读的真正 CSS。在这层编译之上,便可以赋予 CSS 更多更强大的功能,常用功能:嵌套 变量 循环语句 条件语句 自动前缀 单位转换 mixin复用...

Java程序员面试宝典笔记——第九章(多线程)_RiceVan的博客-程序员秘密

Java线程状态参考链接:https://www.cnblogs.com/fengli9998/p/8926096.htmlJava多线程总结: 线程实现方式: 一继承Thread类 二实现Runnable接 线程中start()和run()区别: run()是线程启动入口。是线程启动后要进行回调的方法。只是普通方法调用。还在主线程中。 start()启动线程,会...

【JMeter 菜鸟实操之五】ant+jenkins 完善html结果报告_深圳-雄少的博客-程序员秘密

实现目的:ant+jenkins 完善html结果报告(展示关键指标图、服务资源图,以及个性化设置是否显示等)备注:1、相关资料 ,请到如下地址进行获取  http://pan.baidu.com/s/1miBgd1A   密码:r7re2、具体部署,请参考另外一篇文章  ttp://blog.csdn.net/zouxiongqqq/article/d

.NET(C#)常用数据加密和解密方法汇总_.net 加密解密_幻世界的博客-程序员秘密

欢迎加入Unity业内qq交流群:956187480qq扫描二维码加群一、数据加密的概念1、 基本概念2、 基本功能3、 加密形式二、 数据加密的项目应用和学习1、 媒体加密:DRM2、 文件加密:文本加密、pdf、word3、 数据加密:ASP.NET(C#)中的数据加密4、 硬件加密:加密狗三、 数据加密的发展趋势四、 网络...

linux c ubuntu fcntl.h 文件的位置_fcntl在linux哪个文件夹中_wowocpp的博客-程序员秘密

ubuntu 16.04 64位#include &lt;fcntl.h&gt;那么fcntl.h 文件的具体位置是哪里呢?sudo grep -r “define O_NONBLOCK”include/asm-generic/fcntl.h:#define O_NONBLOCK 00004000include/x86_64-linux-gnu/bits/fcntl-linux.h:# define O_NONBLOCK 04000有没有命令可以定位一下...

推荐文章

热门文章

相关标签