第三方登陆(一)QQ登陆

我们在讲qq登陆的集成。

首先qq登陆的注册问题,我们需要去qq互联申请账号,

地址:https://connect.qq.com/index.html

注册后在应用管理创建网站应用,设置回调地址:

获取app ID,app Key

注意就算审核不通过,你也可以登陆你自己的账号,测试是不会收到影响的

前期的东西都准备齐了现在我们就开始集成。

这是资料文档的地址一定要看完:

http://wiki.connect.qq.com/%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C_oauth2-0

下面是文档中的基本操作顺序:

看完文档后就是代码集成部分:

maven 依赖:

<dependency>
      <groupId>commons-httpclient</groupId>
      <artifactId>commons-httpclient</artifactId>
      <version>3.0.1</version>
   </dependency>

   <dependency>
   	<groupId>org.springframework.boot</groupId>
   	<artifactId>spring-boot-devtools</artifactId>
   </dependency>
   <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.3.2</version>
</dependency>

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.4</version>
  </dependency>

 <dependency>
      <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.3.2</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.38</version>
    </dependency>

前端放置QQ登陆的按钮,login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<a href="/login/qq">QQ登陆</a>
</body>
</html>

这个是登陆成功以后跳转的页面,index.html:

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>测试页面</title>
</head>
<body>
<span>欢迎:</span>
<p th:text="${nickName}"></p>
</body>
</html>

下面是application.properties,配置文件:


########qq的登陆密钥#####
oauth.qq.client_id=
oauth.qq.client_secret=
oauth.qq.redirect_uri=http://zhy.nat100.top/authorize/qq
oauth.qq.code_callback_uri=https://graph.qq.com/oauth2.0/authorize
oauth.qq.access_token_callback_uri=https://graph.qq.com/oauth2.0/token
oauth.qq.openid_callback_uri=https://graph.qq.com/oauth2.0/me
oauth.qq.user_info_callback_uri=https://graph.qq.com/user/get_user_info


下面是util类,HttpsUtils :

package com.haihua.haihua.Utils;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by xslde on 2018/7/21
 */
public class HttpsUtils {
    private static PoolingHttpClientConnectionManager connMgr;
    private static RequestConfig requestConfig;
    private static final int MAX_TIMEOUT = 7000;

    private static final Logger logger = LoggerFactory.getLogger(HttpsUtils.class);

    static {
        // 设置连接池
        connMgr = new PoolingHttpClientConnectionManager();
        // 设置连接池大小
        connMgr.setMaxTotal(100);
        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
        // Validate connections after 1 sec of inactivity
        connMgr.setValidateAfterInactivity(1000);
        RequestConfig.Builder configBuilder = RequestConfig.custom();
        // 设置连接超时
        configBuilder.setConnectTimeout(MAX_TIMEOUT);
        // 设置读取超时
        configBuilder.setSocketTimeout(MAX_TIMEOUT);
        // 设置从连接池获取连接实例的超时
        configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);

        requestConfig = configBuilder.build();
    }

    /**
     * 发送 GET 请求(HTTP),不带输入数据
     *
     * @param url
     * @return
     */
    public static String doGet(String url) {
        return doGet(url, new HashMap<String, Object>());
    }

    /**
     * 发送 GET 请求(HTTP),K-V形式
     *
     * @param url
     * @param params
     * @return
     */
    public static String doGet(String url, Map<String, Object> params) {
        String apiUrl = url;
        StringBuffer param = new StringBuffer();
        int i = 0;
        for (String key : params.keySet()) {
            if (i == 0)
                param.append("?");
            else
                param.append("&");
            param.append(key).append("=").append(params.get(key));
            i++;
        }
        apiUrl += param;
        String result = null;
        HttpClient httpClient = null;
        if (apiUrl.startsWith("https")) {
            httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                    .setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
        } else {
            httpClient = HttpClients.createDefault();
        }
        try {
            HttpGet httpGet = new HttpGet(apiUrl);
            HttpResponse response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream instream = entity.getContent();
                result = new BufferedReader(new InputStreamReader(instream)).lines().collect(Collectors.joining(System.lineSeparator()));
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
        return result;
    }

    /**
     * 发送 POST 请求(HTTP),不带输入数据
     *
     * @param apiUrl
     * @return
     */
    public static String doPost(String apiUrl) {
        return doPost(apiUrl, new HashMap<String, Object>());
    }

    /**
     * 发送 POST 请求,K-V形式
     *
     * @param apiUrl API接口URL
     * @param params 参数map
     * @return
     */
    public static String doPost(String apiUrl, Map<String, Object> params) {
        CloseableHttpClient httpClient = null;
        if (apiUrl.startsWith("https")) {
            httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                    .setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
        } else {
            httpClient = HttpClients.createDefault();
        }
        String httpStr = null;
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;

        try {
            httpPost.setConfig(requestConfig);
            List<NameValuePair> pairList = new ArrayList<>(params.size());
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            httpStr = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    logger.error(e.getMessage());
                }
            }
        }
        return httpStr;
    }

    /**
     * 发送 POST 请求,JSON形式
     *
     * @param apiUrl
     * @param json   json对象
     * @return
     */
    public static String doPost(String apiUrl, Object json) {
        CloseableHttpClient httpClient = null;
        if (apiUrl.startsWith("https")) {
            httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                    .setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
        } else {
            httpClient = HttpClients.createDefault();
        }
        String httpStr = null;
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;

        try {
            httpPost.setConfig(requestConfig);
            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// 解决中文乱码问题
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            httpStr = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    logger.error(e.getMessage());
                }
            }
        }
        return httpStr;
    }

    /**
     * 创建SSL安全连接
     *
     * @return
     */
    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {

                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {

                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
            });
        } catch (GeneralSecurityException e) {
            logger.error(e.getMessage());
        }
        return sslsf;
    }
}

下面是接受properties值的实体类和config类:(注意实体我是用了Lombok的Data属性,你可以自己手动getter,setter)

package com.haihua.haihua.properties;

import lombok.Data;

@Data
public class QQProperties {
    private String client_id;
    private String client_secret;
    private String redirect_uri;
    private String code_callback_uri;
    private String access_token_callback_uri;
    private String openid_callback_uri;
    private String user_info_callback_uri;

}
package com.haihua.haihua.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component//注入到spring容器,方便后面使用
@ConfigurationProperties(prefix = "oauth")//对应application.yml中,oauth下参数
public class OAuthProperties {

    //获取applicaiton.yml下qq下所有的参数
    private QQProperties qq = new QQProperties();

    public QQProperties getQQ() {
        return qq;
    }

    public void setQQ(QQProperties qq) {
        this.qq = qq;
    }
}

下面是接受qq信息的实体类 ,QQDTO.java:

package com.haihua.haihua.entity;

import lombok.Data;

@Data
public class QQDTO {
    private String ret;        //返回码
    private String msg;        //如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
    private String nickname;             //用户在QQ空间的昵称。
    private String figureurl;              //大小为30×30像素的QQ空间头像URL。
    private String figureurl_1;                //大小为50×50像素的QQ空间头像URL。
    private String figureurl_2;                //大小为100×100像素的QQ空间头像URL。
    private String figureurl_qq_1;                   //大小为40×40像素的QQ头像URL。
    private String figureurl_qq_2;                   //大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。
    private String gender;           //性别。 如果获取不到则默认返回"男"
    private String is_yellow_vip;                  //标识用户是否为黄钻用户(0:不是;1:是)。
    private String vip;        //标识用户是否为黄钻用户(0:不是;1:是)
    private String yellow_vip_level;                     //黄钻等级
    private String level;          //黄钻等级
    private String is_yellow_year_vip;                       //标识是否为年费黄钻用户(0:不是; 1:是)
}

下面是接受qq应用的实体类 ,QQDTO.java:

package com.haihua.haihua.entity;

import lombok.Data;

@Data
public class QQOpenidDTO {

    private String openid;

    private String client_id;
}

下面则是控制类,LoginAction.java:

package com.haihua.haihua.controller;

import com.google.gson.Gson;
import com.haihua.haihua.Utils.HttpsUtils;
import com.haihua.haihua.entity.QQDTO;
import com.haihua.haihua.entity.QQOpenidDTO;
import com.haihua.haihua.properties.OAuthProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * Created by xslde on 2018/7/21
 */
@Controller
public class LoginAction {


    private Logger logger = LoggerFactory.getLogger(getClass());


    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @Autowired
    private OAuthProperties oauth;


    //QQ登陆对外接口,只需将该接口放置html的a标签href中即可
    @GetMapping("/login/qq")
    public void loginQQ(HttpServletResponse response) {
        try {
            response.sendRedirect(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");//授权模式,授权码模式
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //接收回调地址带过来的code码
    @GetMapping("/authorize/qq")
    public String authorizeQQ(Map<String, String> msg, String code,Model model) {
        HashMap<String, Object> 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<String, String> 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 {
            logger.info("nickName:"+qqDTO.getNickname()+";opendId:"+qqOpenidDTO.getOpenid());
             model.addAttribute("nickName",qqDTO.getNickname());
        } catch (Exception e) {
            msg.put("msg", "第三方登陆失败,请联系管理!");
            logger.error(e.getMessage());
            return "login";
        }
        return "index";
    }


}

实现后请求域名加 qq/login

点击QQ登录以后跳转:

点击即可登录:

这就完成了qq登录的基本功能。

猜你喜欢

转载自blog.csdn.net/weixin_40927959/article/details/89741737