OAuth 2.0简介
为了简化网站和应用的注册登录开发,第三方登录橫空出世,比如QQ登录,微信登录,微博登录等等;
第三方登录也叫做第三方授权,相应的规范是OAuth 2.0;每家提供第三方登录的大型互联网公司都有具体实现,原理一致,大体过
一致,细节有差别;
流程
QQ登录OAuth2.0总体处理流程如下:
Step1:申请接入,获取appid和apikey;
Step2:开发应用,并设置协作者帐号进行测试联调;
Step3:放置QQ登录按钮;
Step4:通过用户登录验证和授权,获取Access Token;
前期准备
1.(新网)申请域名:https://login.xinnet.com/?service=http://www.xinnet.com/views/login/login.jsp
并将域名进行解析到步骤3中的虚拟主机
2.准备内网穿透工具(这里推荐小米球)https://manager.xiaomiqiu.com/index
3.注册NB互联账号购买0元虚拟主机https://host.40nb.cn/login.html
登录虚拟主机点击域名跳转,将自己从新网购买的域名跳转到内网穿透获得的域名
qq互联申请
1.注册成为开发者,并审核成功
2.创建应用
网站地址写自己的域名
主办单位名称写自己名字
3.发送邮件给connect@qq.com
邮件标题:域名证明材料
邮件内容:
Appid: ******
域名证书:图片.jpg
本人身份证图片:身份证图片.jpg
4.编写页面
4.1放置qq登录按钮
建议从官网下载按钮图片
html代码
<a href="/cd/login/qq_login"><img src="static/image/Connect_logo_3.png"></a></img>
如果用户成功登录并授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code和原始的state值。
5.通过Access Token获取用户的OpenID
http://lmj.ngrok2.xiaomiqiu.cn/cd/login/return?code=3EB9C511B4F25E81B999776A9C39CBA6&state=ok
5.1建立配置文件qqconnentconfig.properties
app_ID = ****
app_KEY = ****
redirect_URI = ******scope=get_user_info,add_topic,add_one_blog,add_album,upload_pic,list_album,add_share,check_page_fans,add_t,add_pic_t,del_t,get_repost_list,get_info,get_other_info,get_fanslist,get_idollist,add_idol,del_ido,get_tenpay_addr(请修改此处)
baseURL = https://graph.qq.com/
getUserInfoURL = https://graph.qq.com/user/get_user_info
accessTokenURL = https://graph.qq.com/oauth2.0/token
authorizeURL = https://graph.qq.com/oauth2.0/authorize
getOpenIDURL = https://graph.qq.com/oauth2.0/me
addTopicURL = https://graph.qq.com/shuoshuo/add_topic
addBlogURL = https://graph.qq.com/blog/add_one_blog
addAlbumURL = https://graph.qq.com/photo/add_album
uploadPicURL = https://graph.qq.com/photo/upload_pic
listAlbumURL = https://graph.qq.com/photo/list_album
addShareURL = https://graph.qq.com/share/add_share
checkPageFansURL = https://graph.qq.com/user/check_page_fans
addTURL = https://graph.qq.com/t/add_t
addPicTURL = https://graph.qq.com/t/add_pic_t
delTURL = https://graph.qq.com/t/del_t
getWeiboUserInfoURL = https://graph.qq.com/user/get_info
getWeiboOtherUserInfoURL = https://graph.qq.com/user/get_other_info
getFansListURL = https://graph.qq.com/relation/get_fanslist
getIdolsListURL = https://graph.qq.com/relation/get_idollist
addIdolURL = https://graph.qq.com/relation/add_idol
delIdolURL = https://graph.qq.com/relation/del_idol
getTenpayAddrURL = https://graph.qq.com/cft_info/get_tenpay_addr
getRepostListURL = https://graph.qq.com/t/get_repost_list
version = 2.0.0.0
5.2配置QQ_loginConfig.java
package com.test.config;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;@Component //添加 @Component 注解让 Component Scan 扫描到,
@PropertySource("classpath:/qqconnectconfig.properties")
@Data
public class QQ_loginConfig {
@Value("${app_ID}")private String app_ID;@Value("${app_KEY}")private String app_KEY;@Value("${redirect_URI}")private String redirect_URI;@Value("${authorizeURL}")private String authorizeURL;@Value("${baseURL}")private String baseURL;@Value("${getUserInfoURL}")private String getUserInfoURL;@Value("${getOpenIDURL}")private String getOpenIDURL;@Value("${accessTokenURL}")private String accessTokenURL;private String authorization_code;//第二次请求返回的授权令牌private String access_token;public String baseUrl(){
System.out.println("登录中:"+authorizeURL);return authorizeURL+"?response_type=code&client_id="+app_ID+"&redirect_uri="+redirect_URI;}
}
5.3控制器QQ_loginController.java
package com.test.contoller;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.test.config.QQ_loginConfig;
import com.test.entity.QQUserInfo;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
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 org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import static com.google.common.io.ByteStreams.toByteArray;@Controller
@RequestMapping("/cd/login")
public class QQ_loginController {
@AutowiredQQ_loginConfig qq_loginConfig;public QQUserInfo qqUserInfo=new QQUserInfo();public String states;@GetMapping("/qq_login")public void qqlogin(HttpServletRequest request, HttpServletResponse response)throws IOException, ServletException {
response.setContentType("text/html;charset=utf-8");response.addHeader("Access-Control-Allow-Origin","www.lmjtest.xyz");HttpSession sessoin = request.getSession();String r_num=getRandomString(6);states=r_num;String str_url=qq_loginConfig.baseUrl()+"&state="+r_num;response.sendRedirect(str_url);}/*** 登录回调接口** @param request* @param response* @param code* @return 用户信息*/@RequestMapping("/return")public String qqreturn(HttpServletRequest request, HttpServletResponse response, String code, String state) throws IOException, ServletException {
response.addHeader("Access-Control-Allow-Origin","*");response.setContentType("text/html;charset=utf-8");System.out.println("qq登录返回");HttpSession sessoin = request.getSession();System.out.println(sessoin.getAttribute("state"));String res_state=states;JSONObject jsondate=new JSONObject();//先进行state值的验证if (state.equals(res_state)) {
System.out.println("state1:"+res_state);System.out.println("state2:" + state);//通过Authorization Code获取Access Tokenqq_loginConfig.setAuthorization_code(code);//进行第二次请求获取access_tokenString access_token = getUrlDate_access_token();qq_loginConfig.setAccess_token(access_token);//进行第三次请求获取用户OpenIDjsondate=getUrlDate_OpenID();String openid=jsondate.get("openid").toString();System.out.println("openid:"+openid);qqUserInfo.setOpenid(openid);} else {
System.out.println("不是原服务器发出的请求");}return "/login_return.html";}/*** 第二次请求获取access_token** @return* @throws IOException*/public String getUrlDate_access_token() throws IOException {
System.out.println("获取access_token");HttpURLConnection conn = null;String url1 = qq_loginConfig.getAccessTokenURL() + "?grant_type=authorization_code&client_id=" + qq_loginConfig.getApp_ID() + "&client_secret=" + qq_loginConfig.getApp_KEY() + "&redirect_uri=" + qq_loginConfig.getRedirect_URI() + "&code=" + qq_loginConfig.getAuthorization_code();URL url = new URL(url1);conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");//设置请求头,否则ajax不允许跨源请求conn.setRequestProperty("Access-Control-Allow-Origin","*");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_tokenString access_token = m.group(1);return access_token;}/***第三次请求获取用户OpenID*/public JSONObject getUrlDate_OpenID() throws IOException {
System.out.println("获取用户OpenID");HttpURLConnection conn = null;String url1=qq_loginConfig.getGetOpenIDURL()+"?access_token="+qq_loginConfig.getAccess_token()+"&fmt=json";URL url = new URL(url1);conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setRequestProperty("Access-Control-Allow-Origin","*");InputStream inStream = conn.getInputStream();byte[] data = toByteArray(inStream);String result = new String(data, "UTF-8");//json字符串转化为json对象JSONObject jsonObject = JSON.parseObject(result);//得到openidreturn jsonObject;}/*** 返回用户信息* @return*/@RequestMapping("/getUser")public @ResponseBody Map<String,Object> getDate_User() throws IOException {
HttpURLConnection conn = null;Map<String,Object> map=new HashMap<>();if(qq_loginConfig.getAccess_token()==null||qqUserInfo.getOpenid()==null){
System.out.println("未登录");map.put("code",1);return map;}else{
String url1=qq_loginConfig.getGetUserInfoURL()+"?access_token="+qq_loginConfig.getAccess_token()+"&oauth_consumer_key="+qq_loginConfig.getApp_ID()+"&openid="+qqUserInfo.getOpenid();URL url = new URL(url1);conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");InputStream inStream = conn.getInputStream();byte[] data = toByteArray(inStream);String result = new String(data, "UTF-8");//json字符串转化为json对象JSONObject jsonObject = JSON.parseObject(result);map.put("code",0);map.put("userdata",jsonObject);// System.out.println(jsonObject);if(jsonObject.get("ret").toString().equals("-1")){
map.put("code",1);return map;}System.out.println("获得用户信息:"+jsonObject.getString("nickname")+jsonObject.getString("figureurl_1"));return map;}}@RequestMapping("/login_out")public @ResponseBody String login_out(HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpSession sessoin = request.getSession();sessoin.invalidate();System.out.println("退出");qq_loginConfig.setAccess_token(null);qqUserInfo.setOpenid(null);return "ok";}//length用户要求产生字符串的长度public String getRandomString(int length){
String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";Random random=new Random();StringBuffer sb=new StringBuffer();for(int i=0;i<length;i++){
int number=random.nextInt(62);sb.append(str.charAt(number));}return sb.toString();}}
返回login_returun.html
5.4前端页面
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"><head><meta charset="UTF-8"><title>Title</title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css"><script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script><script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<style>/*v-cloak解决闪烁问题*/[v-cloak]{
display:none;}</style>
</head><body><div id="app" style="font-size: 28px;text-align: center" v-cloak>欢迎<p id="name">{
{
userdata.nickname}}</p><a href="http://localhost:8081" id="go_login">去登陆</a><img v-bind:src="userdata.figureurl_1" id="img_src"><br><br><a v-on:click="login_out">退出</a></div></body>
<script>new Vue({
el: '#app',data: {
userdata:{
nickname:''},display:''},mounted: function () {
const that = this$.ajax({
url: '/cd/login/getUser',type: 'get',async:true,success: function (data) {
console.log("ok112");if (data.code == 0) {
console.log("已登录");var res = data.userdata;console.log(res);that.userdata = res;$("#go_login").attr("style","display:none");} else {
console.log("未登录");// $("#go_login").attr("style","display:block");that.userdata.nickname="未登录";}}});},methods:{
login_out:function(){
$.ajax({
url:'/cd/login/login_out',type:'get',success:function(data){
if(data=="ok"){
window.location.reload();}}});}}});function test() {
alert("ok");$.ajax({
url: '/cd/login/getUser',type: 'get',success: function (data) {
console.log("ok112");console.log(data);var res = data.userdata;$('#name').text(res.nickname);$('#img_src').attr("src", res.figureurl_2);}});}
</script></html>
5.5成功后效果