zoukankan      html  css  js  c++  java
  • 第三方登录:QQ登录实现(OAuth2.0)

    一、创建应用

    1、在 QQ互联 创建应用

    地址:https://connect.qq.com/manage.html#/

    然后进行实名认证,创建应用,审核通过

    SpringBoot网站添加第三方登录之QQ登录

    然后点击查看,可以获得 APP ID 和 APP Key

    SpringBoot网站添加第三方登录之QQ登录

    回调地址如下

    SpringBoot网站添加第三方登录之QQ登录

    2、授权的基本原理

    可以参考官方文档

    1)根据QQ登录链接可以回调获得 code

    2)根据APP ID 、APP Key 和 code 可获得 token

    3)根据 token 获得 OpenId

    4)  根据 OpenId 可以获得用户的基本信息

    SpringBoot网站添加第三方登录之QQ登录

    其中 OpenId 是一个唯一的值,比如 8A674574E1B12345D790A1B3EFE81234

    3、几个必要的URL

    1)登录页面授权 URL:

    https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=%s

    2)获得 Token 的 URL:

    https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s

    3)获得用户OpenId 的 URL:

    https://graph.qq.com/oauth2.0/me?access_token=%s

    4)获得用户信息的 URL:

    https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s

    二、基本准备

    1、数据库设计

    主要看 bind 和 bind_type 表的数据

    SpringBoot网站添加第三方登录之QQ登录

    identifier 表示识别码,保证唯一性

    credential 表示凭证,通常用不上

    bind_type_id 和 user_id 是外键,很好理解

    SpringBoot网站添加第三方登录之QQ登录

    name 唯一性

    style 样式

    2、放置 QQ 登录按钮

    这里有两种常用的方式

    ① 弹小窗

     
    1. <script>
    2.     function openWin(url,name,iWidth,iHeight) {
    3.         //获得窗口的垂直位置
    4.         var iTop = (window.screen.availHeight - 30 - iHeight) / 2;
    5.         //获得窗口的水平位置
    6.         var iLeft = (window.screen.availWidth - 10 - iWidth) / 2;
    7.         window.open(url, name, 'height=' + iHeight + ',innerHeight=' + iHeight + ',width=' + iWidth + ',innerWidth=' + iWidth + ',top=' + iTop + ',left=' + iLeft + ',status=no,toolbar=no,menubar=no,location=no,resizable=no,scrollbars=0,titlebar=no');
    8.     }
    9.     function qqLogin() {
    10.         var url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101472393&redirect_uri=http://codergroup.cn/oauth/qq/callback&scope=get_user_info";
    11.         openWin(url,"qqLogin",650,500);
    12.     }
    13. </script>
    14. <a  href="javascript:void(0);" onclick="qqLogin()"></a>

    SpringBoot网站添加第三方登录之QQ登录

    ② 在新窗口打开

     
    1. <href="https://graph.qq.com/oauth2.0/authorizeresponse_type=code&client_id=101472393&redirect_uri=http://codergroup.cn/oauth/qq/callback&scope=get_user_info" target="_blank"></a>

    三、具体代码

    参考这里

    1、由于做了多个登录,所以代码做了一定程度的封装,大致如下:

     
    1. //多个登录差不多都要共用这些方法,所以统一放到这个接口中
    2. public interface AuthService {
    3.     public abstract String getAccessToken(String code);
    4.     public abstract String getOpenId(String accessToken);
    5.     public abstract String refreshToken(String code);
    6.     public abstract String getAuthorizationUrl() throws UnsupportedEncodingException;
    7.     public abstract JSONObject getUserInfo(String accessToken,String openId);
    8. }

    2、由于全部是自己封装的,所以http请求的代码也是所有的登录共用的,这里统一放放到了类DefaultAuthServiceImpl中,代码如下:

     
    1. public abstract class DefaultAuthServiceImpl implements AuthService{
    2.     public static RestTemplate getRestTemplate() {// 手动添加
    3.         SimpleClientHttpRequestFactory requestFactory=new SimpleClientHttpRequestFactory();
    4.         requestFactory.setReadTimeout(120000);
    5.         List<HttpMessageConverter<?>> messageConverters = new LinkedList<>();
    6.         messageConverters.add(new ByteArrayHttpMessageConverter());
    7.         messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
    8.         messageConverters.add(new ResourceHttpMessageConverter());
    9.         messageConverters.add(new SourceHttpMessageConverter<Source>());
    10.         messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    11.         messageConverters.add(new MappingJackson2HttpMessageConverter());
    12.         RestTemplate restTemplate=new RestTemplate(messageConverters);
    13.         restTemplate.setRequestFactory(requestFactory);
    14.         return restTemplate;
    15.     }
    16. }

    由此,所有的登录Service只需要继承AuthService即可。

    3、QQ登录 Service 接口

    QQAuthService.java

     
    1. public interface QQAuthService extends AuthService {
    2. }

    4、QQ登录 Service 实现

    QQAuthServiceImpl.java

     
    1. @Service
    2. public class QQAuthServiceImpl extends DefaultAuthServiceImpl implements QQAuthService {
    3.     private Logger logger = LoggerFactory.getLogger(QQAuthServiceImpl.class);
    4.     //QQ 登陆页面的URL
    5.     private final static String AUTHORIZATION_URL =
    6.             "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=%s";
    7.     //获取token的URL
    8.     private final static String ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s";
    9.     // 获取用户 openid 的 URL
    10.     private static final String OPEN_ID_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";
    11.     // 获取用户信息的 URL,oauth_consumer_key 为 apiKey
    12.     private static final String USER_INFO_URL = "https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s";
    13.     // 下面的属性可以通过配置读取
    14.     private  static final String CALLBACK_URL = "http://XXX/XX/XX"; // QQ 在登陆成功后回调的 URL,这个 URL 必须在 QQ 互联里填写过
    15.     private  static final String API_KEY  = "xxxxxx";                                      // QQ 互联应用管理中心的 APP ID
    16.     private  static final String API_SECRET = "xxxxxx";               // QQ 互联应用管理中心的 APP Key
    17.     private  static final String SCOPE       = "get_user_info";                                  // QQ 互联的 API 接口,访问用户资料
    18.     /**
    19.      * @return QQ 登陆页面的 URL
    20.      */
    21.     @Override
    22.     public String getAuthorizationUrl() {
    23.         String url = String.format(AUTHORIZATION_URL,API_KEY,CALLBACK_URL,SCOPE);
    24.         return url;
    25.     }
    26.     @Override
    27.     public String getAccessToken(String code) {
    28.         String url = String.format(ACCESS_TOKEN_URL,API_KEY,API_SECRET,code, CALLBACK_URL);
    29.         UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
    30.         URI uri = builder.build().encode().toUri();
    31.         String resp = getRestTemplate().getForObject(uri, String.class);
    32.         logger.error("getAccessToken resp = "+resp);
    33.         if(resp.contains("access_token")){
    34.             Map<String,String> map = getParam(resp);
    35.             String access_token = map.get("access_token");
    36.             return access_token;
    37.         }else{
    38.             throw new ServiceException(resp);
    39.         }
    40.     }
    41.     //由于QQ的几个接口返回类型不一样,此处是获取key-value类型的参数
    42.     private Map<String,String> getParam(String string){
    43.         Map<String,String> map = new HashMap();
    44.         String[] kvArray = string.split("&");
    45.         for(int i = 0;i<kvArray.length;i++){
    46.             String[] kv = kvArray[i].split("=");
    47.             map.put(kv[0],kv[1]);
    48.         }
    49.         return map;
    50.     }
    51.     //QQ接口返回类型是text/plain,此处将其转为json
    52.     public JSONObject ConvertToJson(String string){
    53.         string = string.substring(string.indexOf("(")+1,string.length());
    54.         string = string.substring(0,string.indexOf(")"));
    55.         logger.error("ConvertToJson s = "+string);
    56.         JSONObject jsonObject = JSONObject.parseObject(string);
    57.         return jsonObject;
    58.     }
    59.     @Override
    60.     public String getOpenId(String accessToken) {
    61.         String url = String.format(OPEN_ID_URL,accessToken);
    62.         UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
    63.         URI uri = builder.build().encode().toUri();
    64.         String resp = getRestTemplate().getForObject(uri, String.class);
    65.         logger.error("getAccessToken resp = "+resp);
    66.         if(resp.contains("openid")){
    67.             JSONObject jsonObject = ConvertToJson(resp);
    68.             String openid = jsonObject.getString("openid");
    69.             return openid;
    70.         }else{
    71.             throw new ServiceException(resp);
    72.         }
    73.     }
    74.     /**
    75.      * https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID
    76.      * @param accessToken
    77.      * @param openId
    78.      * @return
    79.      */
    80.     @Override
    81.     public JSONObject getUserInfo(String accessToken, String openId){
    82.         openId = getOpenId(accessToken);
    83.         String url = String.format(USER_INFO_URL,accessToken, API_KEY, openId);
    84.         UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
    85.         URI uri = builder.build().encode().toUri();
    86.         String resp = getRestTemplate().getForObject(uri,String.class);
    87.         JSONObject data = JSONObject.parseObject(resp);
    88.         logger.error("resp = "+data);
    89.         JSONObject result = new JSONObject();
    90.         result.put("id",openId);
    91.         result.put("nickName",data.getString("nickname"));
    92.         result.put("avatar",data.getString("figureurl_qq_1"));
    93.         return result;
    94.     }
    95.     @Override
    96.     public String refreshToken(String code) {
    97.         return null;
    98.     }
    99. }

    5、在Controller中调用,代码如下:

     
    1. @RestController
    2. @RequestMapping(value = "/oauth")
    3. public class AuthController {
    4.      private Logger logger = LoggerFactory.getLogger(AuthController.class);
    5.       @Autowired
    6.     private QQAuthService qqAuthService;
    7.     @Autowired
    8.     private UserService userService;
    9.  //访问登陆页面,然后会跳转到 QQ 的登陆页面
    10.     @RequestMapping(value = "/qqLoginPage",method = RequestMethod.GET)
    11.     public JSONObject qqLogin() throws Exception {
    12.         String uri = qqAuthService.getAuthorizationUrl();
    13.         return loginPage(uri);
    14.     }
    15. }
    16. //qq授权后会回调此方法,并将code传过来
    17. @RequestMapping("/qq/callback")
    18.     public void getQQCode(String code, HttpServletRequest request,HttpServletResponse response) throws Exception {
    19.         //根据code获取token
    20.         String accessToken = qqAuthService.getAccessToken(code);
    21.         // 保存 accessToken 到 cookie,过期时间为 30 天,便于以后使用
    22.         Cookie cookie = new Cookie("accessToken", accessToken);
    23.         cookie.setMaxAge(60 * 24 * 30);
    24.         response.addCookie(cookie);
    25.         //本网站是将用户的唯一标识存在用户表中,大家也可以加一张表,存储用户和QQ的对应信息。
    26.         //根据openId判断用户是否已经绑定过
    27.         String openId = qqAuthService.getOpenId(accessToken);
    28.         KmsUser user = userService.getUserByCondition(openId);
    29.         if (user == null) {
    30.         //如果用户不存在,则跳转到绑定页面
    31.             response.sendRedirect(request.getContextPath() + "/bind?type="+Constants.LOGIN_TYPE_QQ);
    32.         } else {
    33.             //如果用户已存在,则直接登录
    34.             response.sendRedirect(request.getContextPath());
    35.         }
    36.     }

    查看更多

    SpringBoot网站添加第三方登录之GitHub登录

    本文转载自

    原文作者:言曌博客

    原文链接:https://liuyanzhao.com/8210.html

  • 相关阅读:
    算法的时间复杂度
    二叉树递归建立和遍历
    数据挖掘之分类算法---knn算法(有matlab例子)
    C链表之创建简单静态链表
    ID3决策树算法原理及C++实现(其中代码转自别人的博客)
    adobe reader安装完成之前被中断,错误代码150210解决方法
    Oracle性能诊断艺术-读书笔记
    先对结果集排序然后做update、delete操作
    索引聚簇因子相关
    直方图及low_value、high_value
  • 原文地址:https://www.cnblogs.com/xifengxiaoma/p/9508333.html
Copyright © 2011-2022 走看看