博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
网站实现qq登录(springboot后台)
阅读量:3889 次
发布时间:2019-05-23

本文共 10629 字,大约阅读时间需要 35 分钟。

成果展示:

参考:

1.申请qq互联权限:

①:打开qq互联官网:登录qq,然后点击登陆后的头像(当时找入口找了半天?)进行个人开发者信息认证。

这个认证大概要半天时间。认证成功后就可以申请应用了。

②:点击应用管理,创建应用,填写信息。可以先随便写一个,过两个小时就显示就申请失败了。不过没关系的,因为我们这时候失败的申请也能用。不过只能登陆自己的qq,用来做测试。

我写的地址和回调地址:(其他随便写,反正是用来做测试的)

③:找到自己应用的app ID和app KEY。之后会用到。

2.前端页面设置按钮:

<button type="button" class="" οnclick="login()" >

          <img src="/image/qq.jpg" style="height:30px">          

         <span class="">登陆</span>            

</button>

js:login()函数代码:

//在新标签页打开网站function login(){  window.open("/logincheck","TencentLogin",   "width=450,height=320,menubar=0,scrollbars=1,resizable=1,status=1,titlebar=0,toolbar=0,location=1");}

即点击后打开如下页面

logincheck页面完成的操作会在之后提到,注意这种操作在手机端打开失败,暂时还没有处理。

3.完成后台:

qq互联只提供jsSDK和PHPSDK,没有javaSDK,可是使用jsSDK自我定制度低,而且不使用SDK,按照流程开发也很简单:

QQ登录OAuth2.0总体处理流程如下:

Step1:申请接入,获取appid和apikey;
Step2:开发应用,并设置协作者帐号进行测试联调;
Step3:放置QQ登录按钮;
Step4:通过用户登录验证和授权,获取Access Token;

参考官方文档:,过程描述已经很详细了。我叙述一下我的完成过程。

①:生成state值,保存到session,然后携带此state值跳转到qq互联获取Authorization Code。

上面的js代码访问的是logincheck页面。代码为:

@GetMapping("/logincheck")    public String loginUrl(HttpServletRequest request){        //获取当前sesion        HttpSession sessoin=request.getSession();        //随机产生字符串        String state=getRandomString(10);        sessoin.setAttribute("state",state);        //重定向        return "redirect:https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id="      + 101533009 + "&redirect_uri=" + "http://127.0.0.1:8080/recall" + "&state=" + state;    }

这一步主要是获取一个state值加入到session,state值是用来防止CSRF攻击的,自己随机产生一段字符串此值。

产生随机字符串代码:

//length用户要求产生字符串的长度    public String getRandomString(int length){        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";        Random random=new Random();        StringBuffer sb=new StringBuffer();        for(int i=0;i

跳转到qq互联的请求网址:

url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" + appid + "&redirect_uri=" + redirectURI + "&state=" + state

参数 是否必须 含义
response_type 必须 授权类型,此值固定为“code”。
client_id 必须 申请QQ登录成功后,分配给应用的appid。
redirect_uri 必须 成功授权后的回调地址,必须是注册appid时填写的主域名下的地址,建议设置为网站首页或网站的用户中心。注意需要将url进行URLEncode。
state 必须 client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。请务必严格按照流程检查用户与state参数状态的绑定。

 

此时用户得到这样的登录页面。

如果用户成功登录并授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code和原始的state值。

Step5:通过Access Token获取用户的OpenID;

参考官方文档:

Step6:调用OpenAPI,来请求访问或修改用户授权的资源。

参考官方文档:

注意我用的是springboot后台,这个getData函数中用到了usrService。需要在类中。

@Autowiredprivate UsrService usrService;

因为获取qq互联认证还是比较快的,且返回数据格式简单,所以直接在服务器进行申请,而不是通过js代码在浏览器进行qq互联申请了。

讲解回调函数流程:

1.因为上面带着state值和Authorization Code值来到的回调页面/recall,所以先进行state值的验证,即从服务器中此用户session取出之前保存的state值,然后和带来的state值进行比较,如果相同继续向下进行。

2.通过Authorization Code获取Access Token

url = " https://graph.qq.com/oauth2.0/token? grant_type=authorization_code

&client_id=" + appid + "&client_secret="+ appkey + "&redirect_uri=" + redirectURI + "&code=" + code

请求参数请包含如下内容:

参数 是否必须 含义
grant_type 必须 授权类型,在本步骤中,此值为“authorization_code”。
client_id 必须 申请QQ登录成功后,分配给网站的appid。
client_secret 必须 申请QQ登录成功后,分配给网站的appkey。
code 必须 上一步返回的authorization code。
如果用户成功登录并授权,则会跳转到指定的回调地址,并在URL中带上Authorization Code。
例如,回调地址为www.qq.com/my.php,则跳转到:
http://www.qq.com/my.php?code=520DD95263C1CFEA087******
注意此code会在10分钟内过期。
redirect_uri 必须 与上面一步中传入的redirect_uri保持一致。

如果成功返回,即可在返回包中获取到Access Token。 如:

access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14。

参数说明 描述
access_token 授权令牌,Access_Token。
expires_in 该access token的有效期,单位为秒。
refresh_token 在授权自动续期步骤中,获取新的Access_Token时需要提供的参数。

然后通过正则表达式截取,正则表达式的截取见代码。

3.获取用户OpenID_OAuth2.0:

url = "=" + access_token

access_token 必须 在Step1中获取到的access token。

PC网站接入时,获取到用户OpenID,返回包如下:

callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );

openid是此网站上唯一对应用户身份的标识,网站可将此ID进行存储便于用户下次登录时辨识其身份,或将其与用户在网站上的原有账号进行绑定。

这一步我也是通过正则表达式截取的,因为他还有个callback字符串,没法直接转化为类。

4.OpenAPI调用说明_OAuth2.0

1. 以接口为例:

(请将access_token,appid等参数值替换为你自己的)

https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID

2. 成功返回后,即可获取到用户数据,获取你感兴趣的数据:

{"ret":0,"msg":"","nickname":"Peter","figureurl":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/30","figureurl_1":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/50","figureurl_2":"http://qzapp.qlogo.cn/qzapp/111111/942FEA70050EEAFBD4DCE2C1FC775E56/100","figureurl_qq_1":"http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/40","figureurl_qq_2":"http://q.qlogo.cn/qqapp/100312990/DE1931D5330620DBD07FB4A5422917B6/100","gender":"男","is_yellow_vip":"1","vip":"1","yellow_vip_level":"7","level":"7","is_yellow_year_vip":"1"}

这个返回值可以直接通过fastjson转化为json类,来获取值.

获取流程到这里结束了,但是还有一些细节问题。

1.数据库设计:show create table usr

CREATE TABLE `usr` (

  `openid` varchar(100) NOT NULL,
  `name` varchar(60) NOT NULL,
  `signature` varchar(100) DEFAULT NULL,
  `accessToken` varchar(100) DEFAULT NULL,
  `icon` varchar(100) NOT NULL,
  `gender` varchar(3) DEFAULT '男',
  `mytoken` char(33) DEFAULT NULL,
  PRIMARY KEY (`openid`),
  UNIQUE KEY `mytoken` (`mytoken`),
  KEY `tokenIndex` (`mytoken`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

这里面我mytoken是用来实现自动登录的。下面再说。

2.其中qq用户名可能有特殊符号,数据库可能保存乱码,需要处理一下通过base64编码一哈。

private String nameToDb(String str){        try {            return  Base64.encodeBase64String(str.getBytes("utf-8"));        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        return null;    }    private String nameToshow(String str){        try {            return   new String(Base64.decodeBase64(str.getBytes()),"utf-8");        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        return null;    }

3.我数据库中有mytoken是因为我有一个功能是通过cookie自动登录网站。

参考网址:

读的有点不明白,我就简单的操作一下:将openid通过md5加密一下,就是token,保存到数据库中,并发送到浏览器当做cookie。然后下次打开页面的时候通过cookie中的token来查询数据库,得到用户信息。

退出登录则只要删除session和浏览器保存的token:

4.其中登录的时候需要通过openid来判断数据库是否已经存在此用户,详细步骤见代码。

5.因为要实现登录完成后关闭小窗口,所以需要返回页面。因为jsp落伍了,我就使用的freemaker模版。feedback.html代码为:

这个页面主要是通过js来改变父模版的样式,这样做不是很合适,因为这样的操作在手机端会失效。而如果不通过这种方式还可以通过刷新的办法来实现登录,感觉这样更难受,我也不知道其他的办法。

    
回调页面

登录成功

因为本篇涉及到的session和freemaker模版知识很简单,自己了解一下吧。

最终的登录效果:(头像不一样,是因为我后来改的)

我将上述内容整合到一起,代码比较冗杂(下个版本会更改),而且更为致命的是没有注意到全部的错误处理,你们写的时候一定要加上,我等有机会再改吧。

@GetMapping("/recall")    public String getData(ModelMap model, HttpServletRequest request, HttpServletResponse response){        UsrShow usrShow = null;        //判段state值        HttpSession sessoin = request.getSession();        String mystate = (String) sessoin.getAttribute("state");        if (mystate == null) {            System.out.println("kong");        }        String state = request.getParameter("state");        if (!state.equals(mystate)) {            System.out.println("不相等");        }        //获取Access Token值:101533058  1f1aa99f87d83ecee80202354c0f31cb   http://www.qihea.xyz/recall        String url1 = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id="                + 你的id + "&client_secret=" + "写你的必看我的" + "&redirect_uri="                + "http://127.0.0.1:8080/recall" + "&code=" + request.getParameter("code");        //发送url请求获取数据        try {            URL url = new URL(url1);            HttpURLConnection conn = null;            conn = (HttpURLConnection) url.openConnection();            conn.setConnectTimeout(5 * 1000);            conn.setRequestMethod("GET");            InputStream inStream = conn.getInputStream();            byte[] data = toByteArray(inStream);            String result = new String(data, "UTF-8");            System.out.println(result);            //使用正则表达式解析网址            Pattern p = Pattern.compile("access_token=(\\w*)&");            Matcher m = p.matcher(result);            m.find();            //得到access_token            String access_token = m.group(1);            //System.out.println(access_token);            String url2 = "https://graph.qq.com/oauth2.0/me?access_token=" + access_token;            url = new URL(url2);            conn = (HttpURLConnection) url.openConnection();            conn.setConnectTimeout(5 * 1000);            conn.setRequestMethod("GET");            inStream = conn.getInputStream();            data = toByteArray(inStream);            String result2 = new String(data, "UTF-8");            //System.out.println(result2);            p = Pattern.compile("client_id\":\"(\\w*)\",");            m = p.matcher(result2);            m.find();            String appid = m.group(1);            p = Pattern.compile("openid\":\"(\\w*)\"");            m = p.matcher(result2);            m.find();            //得到openid            String openid = m.group(1);            System.out.println(openid);            //判断数据库中是否有此            usrShow = usrService.findUsrByid(openid);            //通过openid计算token            String token = getMD5(openid);            //如果数据库中没有            if (usrShow == null) {                String url3 = "https://graph.qq.com/user/get_user_info?access_token=" + access_token                        + "&oauth_consumer_key=" + appid + "&openid=" + openid;                url = new URL(url3);                conn = (HttpURLConnection) url.openConnection();                conn.setConnectTimeout(5 * 1000);                conn.setRequestMethod("GET");                inStream = conn.getInputStream();                data = toByteArray(inStream);                String result3 = new String(data, "UTF-8");                //System.out.println(result3);                //json字符串转化为json对象                JSONObject jsonObject = JSON.parseObject(result3);                String myname = jsonObject.getString("nickname");                myname = nameToDb(myname);                //生成usr对象,并插入数据库                Usr usr = new Usr("", myname                        , jsonObject.getString("figureurl_2"), jsonObject.getString("gender")                        , access_token, openid, token);                if (!usrService.insertUsr(usr))                    System.out.println("添加失败");                //生成usrshow对象                usrShow = new UsrShow(null, usr.getName()                        , usr.getIcon(), usr.getGender());            }            //改变名称格式            usrShow.setName(nameToshow(usrShow.getName()));            //加入session            sessoin.setAttribute("usrshow", usrShow);            //cookie中存入token            Cookie cookie = new Cookie("token", token);//创建新cookie            cookie.setMaxAge(2592000);// 设置存在时间为一个月            cookie.setPath("/");//设置作用域            response.addCookie(cookie);        } catch (IOException e) {            e.printStackTrace();        }        model.addAttribute("json",JSON.toJSONString(usrShow));        return "feedback";    }

 

转载地址:http://cfihn.baihongyu.com/

你可能感兴趣的文章
Ubuntu安装后的一些配置
查看>>
ubuntu9.10 tftp服务设置(这个绝对好使)
查看>>
关于UNIXDOMAIN协议的接收发送者验证
查看>>
I/O操作上设置超时之alarm闹钟法
查看>>
查看返回接收到UDP数据包的宿地址结构--(适用于LINUX和BSD系统)
查看>>
如何开启_GNU_SOURCE宏
查看>>
从网上搜索到的一些关于pcap源代码,入门级的
查看>>
Linux—— Posix IPC
查看>>
在ubuntu下安装ACE编译环境
查看>>
公司HR面试经常问的问题及回答思路
查看>>
ACE之反应堆学习(一)
查看>>
apache配置
查看>>
快速精通FRAME
查看>>
msf反弹木马之免杀
查看>>
写一个简单的python爬虫程序,爬取一下百度图片
查看>>
简单Dos命令以及批处理
查看>>
使用python执行cmd命令
查看>>
利用python脚本实现一招断网
查看>>
10行代码教你用python进行Dos攻击
查看>>
完善了一点的爬虫
查看>>