1. 程式人生 > 實用技巧 >golang macaron各種形式入參封裝

golang macaron各種形式入參封裝

從零玩轉第三方QQ登陸

專案原始碼--- 請移步GITee

https://gitee.com/yangbuyi/yby_qq

在真正開始對接之前,我們先來聊一聊後臺的方案設計。既然是對接第三方登入,那就免不了如何將使用者資訊儲存。首先需要明確一點的是,使用者在第三方登入成功之後,
我們能拿到的僅僅是一個代表使用者唯一身份的ID(微博是真實uid,QQ是加密的openID)以及用來識別身份的accessToken,當然還有暱稱、頭像、性別等有限資料,
對接第三方登入的關鍵就是如何確定使用者是合法登入,如果確定這次登入的和上次登入的是同一個人並且不是假冒的。其實這個並不用我們特別操心,就以微博登入為例,
使用者登入成功之後會回撥一個code給我們,然後我們再拿code去微博那換取 accessToken

,如果這個code是使用者亂填的,那這一關肯定過不了,所以,前面的擔心有點多餘,哈哈。

1. 認識Oauth2.0

現在很多網站都要不管是為了引流也好,為了使用者方便也好一般都有第三方賬號登陸的需求,今天以QQ登陸為例,來實現一個最簡單的第三方登陸。
目前主流的第三方登入都是依賴的Oauth2.0實現的,最常見的就是在各種中小型網站或者App中的QQ登入,微信登入等等。所以我建議想要學習和實現第三方登入同學去了解下這個協議。

1.2 必須要域名並且進行備案

比如我的域名: https://yangbuyi.top/
因為騰訊有一個域名認證機制啥的。。。。。。

2.實名認證

QQ登入我們對接的是QQ互聯,地址:

https://connect.qq.com ,首先需要註冊成為開發者並實名認證,需要手持身份證照片,具體就不講了。

2.1、進行申請開發者身份

2.2 建立應用

進入應用管理頁面建立應用,根據實際需要是建立網站應用還是移動應用,我這裡是網站應用:

提交成功完步後等待客服稽核即可

2.3. QQ登陸流程

2.4. 請求引數

3.前臺準備

/** * 封裝一個居中開啟新視窗的方法 */function openWindow(url, width, height) {width = width || 600;height = height || 400;var left = (window.screen.width - width) / 2;var top = (window.screen.height - height) / 2;var win =window.open(url, '_blank','toolbar=yes, location=yes, directories=no, status=no, menubar=yes, scrollbars=yes, resizable=no, copyhistory=yes, left=' +left + ', top=' + top + ', width=' + width + ', height=' + height);console.log(win)}

3.1 使用 隨便點選按鈕進行呼叫這個 qq函式

3.2 點選訪問後端 登陸方法

4. 後端實現

package top.yangbuyi.system.controller;import com.google.gson.Gson;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.subject.Subject;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import top.yangbuyi.system.common.ActiveUser;import top.yangbuyi.system.common.Constant;import top.yangbuyi.system.common.HttpsUtils;import top.yangbuyi.system.common.WebUtils;import top.yangbuyi.system.config.QQ.OAuthProperties;import top.yangbuyi.system.config.QQ.vo.QQDTO;import top.yangbuyi.system.config.QQ.vo.QQOpenidDTO;import javax.servlet.http.HttpServletResponse;import java.util.HashMap;import java.util.Map;import java.util.UUID;/** * description:  楊不易網站 :www.yangbuyi.top * program:  yangbuyi-erp-2020 * ClassName:  QQController * create:  2020-06-24 17:17 * * @author: yangbuyi * @since: JDK1.8 **/@RestController@RequestMapping('api')public class QQController {            @Autowired      private OAuthProperties oauth;      private static final Logger logger = LoggerFactory.getLogger(QQController.class);            /**       * 呼叫QQ登陸介面       *       * @param response       */      @GetMapping('/login/oauth')      public String loginQQ(HttpServletResponse response) {            /**             * 重定向             *///                  response.sendRedirect();//授權模式,授權碼模式                        System.out.println(                  oauth.getQQ().getCode_callback_uri() + //獲取code碼地址                        '?client_id=' + oauth.getQQ().getClient_id()//appid                        + '&state=' + UUID.randomUUID() + //這個說是防攻擊的,就給個隨機uuid吧                        '&redirect_uri=' + oauth.getQQ().getRedirect_uri() +//這個很重要,這個是回撥地址,即就收騰訊返回的code碼                        '&response_type=code'            );            return oauth.getQQ().getCode_callback_uri() + //獲取code碼地址                  '?client_id=' + oauth.getQQ().getClient_id()//appid                  + '&state=' + UUID.randomUUID() + //這個說是防攻擊的,就給個隨機uuid吧                  '&redirect_uri=' + oauth.getQQ().getRedirect_uri() +//這個很重要,這個是回撥地址,即就收騰訊返回的code碼                  '&response_type=code';                  }                  //接收回調地址帶過來的code碼      @GetMapping('/oauth2')      public String authorizeQQ(Map    msg, String code, HttpServletResponse response) {            HashMap      params = new HashMap<>();            params.put('code', code);            params.put('grant_type', 'authorization_code');            params.put('redirect_uri', oauth.getQQ().getRedirect_uri());            params.put('client_id', oauth.getQQ().getClient_id());            params.put('client_secret', oauth.getQQ().getClient_secret());            //獲取access_token如:access_token=9724892714FDF1E3ED5A4C6D074AF9CB&expires_in=7776000&refresh_token=9E0DE422742ACCAB629A54B3BFEC61FF            String result = HttpsUtils.doGet(oauth.getQQ().getAccess_token_callback_uri(), params);            //對拿到的資料進行切割字串            String[] strings = result.split('&');            //切割好後放進map            Map        reulsts = new HashMap<>();            for (String str : strings) {                  String[] split = str.split('=');                  if (split.length > 1) {                        reulsts.put(split[0], split[1]);                  }            }            //到這裡access_token已經處理好了            //下一步獲取openid,只有拿到openid才能拿到使用者資訊            String openidContent = HttpsUtils.doGet(oauth.getQQ().getOpenid_callback_uri() + '?access_token=' + reulsts.get('access_token'));            //接下來對openid進行處理            //擷取需要的那部分json字串            String openid = openidContent.substring(openidContent.indexOf('{'), openidContent.indexOf('}') + 1);            Gson gson = new Gson();            //將返回的openid轉換成DTO            QQOpenidDTO qqOpenidDTO = gson.fromJson(openid, QQOpenidDTO.class);                        //接下來說說獲取使用者資訊部分            //登陸的時候去資料庫查詢使用者資料對於openid是存在,如果存在的話,就不用拿openid獲取使用者資訊了,而是直接從資料庫拿使用者資料直接認證使用者,            // 否則就拿openid去騰訊伺服器獲取使用者資訊,並存入資料庫,再去認證使用者            //下面關於怎麼獲取使用者資訊,並登陸            params.clear();            params.put('access_token', reulsts.get('access_token'));//設定access_token            params.put('openid', qqOpenidDTO.getOpenid());//設定openid            params.put('oauth_consumer_key', qqOpenidDTO.getClient_id());//設定appid            //獲取使用者資訊            String userInfo = HttpsUtils.doGet(oauth.getQQ().getUser_info_callback_uri(), params);            QQDTO qqDTO = gson.fromJson(userInfo, QQDTO.class);            //這裡拿使用者暱稱,作為使用者名稱,openid作為密碼(正常情況下,在開發時候用openid作為使用者名稱,再自己定義個密碼就可以了)            try {                  System.out.println('使用者資訊:' + userInfo);                  System.out.println(qqDTO);                  // 獲取主體                  Subject subject = SecurityUtils.getSubject();//                  SecurityUtils.getSubject().login(new UsernamePasswordToken(qqOpenidDTO.getOpenid(), Constant.DEFAULT_PWD));                  System.out.println(qqOpenidDTO.getOpenid());                  subject.login(new UsernamePasswordToken(qqDTO.getNickname(), Constant.DEFAULT_PWD));                                    String token = subject.getSession().getId().toString();                  ActiveUser active = (ActiveUser) subject.getPrincipal();                  params.put('token', token);                  params.put('code', 200);                  params.put('permissions', active.getPermissions());                  params.put('username', active.getUser().getName());                  params.put('usertype', active.getUser().getType());                  System.out.println('Shiro認證成功');            } catch (Exception e) {                  msg.put('msg', '第三方登陸失敗,請聯絡管理!');                  logger.error(e.getMessage());                  System.out.println('Shiro認證失敗');//                  return new ResultObj(-1, 'login.html');//                  return 'redirect:https://www.yangbuyi.top/login.html';                  WebUtils.getHttpSession().setAttribute('params', params);                  return '/login.html';            }//            return 'redirect:' + 'https://www.yangbuyi.top/'//            params                                    WebUtils.getHttpSession().setAttribute('params', params);//            return 'redirect:https://www.yangbuyi.top/index.html';           // return 'getParams';            return '/index.html';      }            /**       * 獲取引數       *       * @return       */      @RequestMapping('getParams')      public Object getParams() {            System.out.println(WebUtils.getHttpSession().getAttribute('params'));            return WebUtils.getHttpSession().getAttribute('params');      }            }