思路
主要就是引入的了qq互联的api
要想调用qq登录的api首先要2个条件
- 有阿里云服务器
- 要申请一个域名,并且备案
有了这两个就可以在qq互联申请一个网站,会得到一个回调方法,然后qqAppId和qqAppSecret相当于qq的认证账户和密码
前端获取到一个qq登录的地址,然后登录成功后调用前面申请的回调域
1.步骤一
引入首先qq登录需要用的的jar包
<!-- 以下是 qq wx 联合登陆需要的相关依赖工具 commons-io, commons-lang3,httpclient,还有以json返回给后端的fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</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.9</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
步骤二
把申请好的qqAppId,qqAppSecret,qqRedirectUrl放到yml文件中,方便以后的调用
# qq登录接口配置
constants:
qqAppId: 你自己的
qqAppSecret: 你自己的
qqRedirectUrl: http://你自己的
注册一个bean方便以后通过访问
@Component
@ConfigurationProperties(prefix = "constants")
public class Constants {
private String qqAppId;
private String qqAppSecret;
private String qqRedirectUrl;
private String weCatAppId;
private String weCatAppSecret;
private String weCatRedirectUrl;
public String getQqAppId() {
return qqAppId;
}
public void setQqAppId(String qqAppId) {
this.qqAppId = qqAppId;
}
public String getQqAppSecret() {
return qqAppSecret;
}
public void setQqAppSecret(String qqAppSecret) {
this.qqAppSecret = qqAppSecret;
}
public String getQqRedirectUrl() {
return qqRedirectUrl;
}
public void setQqRedirectUrl(String qqRedirectUrl) {
this.qqRedirectUrl = qqRedirectUrl;
}
public String getWeCatAppId() {
return weCatAppId;
}
public void setWeCatAppId(String weCatAppId) {
this.weCatAppId = weCatAppId;
}
public String getWeCatAppSecret() {
return weCatAppSecret;
}
public void setWeCatAppSecret(String weCatAppSecret) {
this.weCatAppSecret = weCatAppSecret;
}
public String getWeCatRedirectUrl() {
return weCatRedirectUrl;
}
public void setWeCatRedirectUrl(String weCatRedirectUrl) {
this.weCatRedirectUrl = weCatRedirectUrl;
}
@Override
public String toString() {
return "Constants{" +
"qqAppId='" + qqAppId + '\'' +
", qqAppSecret='" + qqAppSecret + '\'' +
", qqRedirectUrl='" + qqRedirectUrl + '\'' +
", weCatAppId='" + weCatAppId + '\'' +
", weCatAppSecret='" + weCatAppSecret + '\'' +
", weCatRedirectUrl='" + weCatRedirectUrl + '\'' +
'}';
}
}
步骤三
这里使用了两个写好的工具类
1.用于http请求的
2.用于url转码的
1.HttpClientConfigUtils
//清除所有类型的警告
@SuppressWarnings("all")
public class HttpClientConfigUtils {
public static final int connTimeout = 10000;
public static final int readTimeout = 10000;
public static final String charset = "UTF-8";
private static HttpClient client = null;
static {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(128);
cm.setDefaultMaxPerRoute(128);
client = HttpClients.custom().setConnectionManager(cm).build();
}
public static String postParameters(String url, String parameterStr)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
return post(url, parameterStr, "application/x-www-form-urlencoded", charset, connTimeout, readTimeout);
}
public static String postParameters(String url, String parameterStr, String charset, Integer connTimeout,
Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception {
return post(url, parameterStr, "application/x-www-form-urlencoded", charset, connTimeout, readTimeout);
}
public static String postParameters(String url, Map<String, String> params)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}
public static String postParameters(String url, Map<String, String> params, Integer connTimeout,
Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}
public static String get(String url) throws Exception {
return get(url, charset, null, null);
}
public static String get(String url, String charset) throws Exception {
return get(url, charset, connTimeout, readTimeout);
}
/**
* 发送一个 Post 请求, 使用指定的字符集编码.
*
* @param url
* @param body RequestBody
* @param mimeType 例如 application/xml "application/x-www-form-urlencoded"
* a=1&b=2&c=3
* @param charset 编码
* @param connTimeout 建立链接超时时间,毫秒.
* @param readTimeout 响应超时时间,毫秒.
* @return ResponseBody, 使用指定的字符集编码.
* @throws ConnectTimeoutException 建立链接超时异常
* @throws SocketTimeoutException 响应超时
* @throws Exception
*/
public static String post(String url, String body, String mimeType, String charset, Integer connTimeout,
Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception {
HttpClient client = null;
HttpPost post = new HttpPost(url);
String result = "";
try {
if (StringUtils.isNotBlank(body)) {
HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
post.setEntity(entity);
}
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());
HttpResponse res;
if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(post);
} else {
// 执行 Http 请求.
client = HttpClientConfigUtils.client;
res = client.execute(post);
}
result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}
/**
* 提交form表单
*
* @param url
* @param params
* @param connTimeout
* @param readTimeout
* @return
* @throws ConnectTimeoutException
* @throws SocketTimeoutException
* @throws Exception
*/
public static String postForm(String url, Map<String, String> params, Map<String, String> headers,
Integer connTimeout, Integer readTimeout)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
HttpClient client = null;
HttpPost post = new HttpPost(url);
try {
if (params != null && !params.isEmpty()) {
List<NameValuePair> formParams = new ArrayList<NameValuePair>();
Set<Entry<String, String>> entrySet = params.entrySet();
for (Entry<String, String> entry : entrySet) {
formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
post.setEntity(entity);
}
if (headers != null && !headers.isEmpty()) {
for (Entry<String, String> entry : headers.entrySet()) {
post.addHeader(entry.getKey(), entry.getValue());
}
}
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());
HttpResponse res = null;
if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(post);
} else {
// 执行 Http 请求.
client = HttpClientConfigUtils.client;
res = client.execute(post);
}
return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
}
/**
* 发送一个 GET 请求
*
* @param url
* @param charset
* @param connTimeout 建立链接超时时间,毫秒.
* @param readTimeout 响应超时时间,毫秒.
* @return
* @throws ConnectTimeoutException 建立链接超时
* @throws SocketTimeoutException 响应超时
* @throws Exception
*/
public static String get(String url, String charset, Integer connTimeout, Integer readTimeout)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
HttpClient client = null;
HttpGet get = new HttpGet(url);
String result = "";
try {
// 设置参数
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
get.setConfig(customReqConf.build());
HttpResponse res = null;
if (url.startsWith("https")) {
// 执行 Https 请求.
client = createSSLInsecureClient();
res = client.execute(get);
} else {
// 执行 Http 请求.
client = HttpClientConfigUtils.client;
res = client.execute(get);
}
result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
get.releaseConnection();
if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}
/**
* 从 response 里获取 charset
*
* @param ressponse
* @return
*/
@SuppressWarnings("unused")
private static String getCharsetFromResponse(HttpResponse ressponse) {
// Content-Type:text/html; charset=GBK
if (ressponse.getEntity() != null && ressponse.getEntity().getContentType() != null
&& ressponse.getEntity().getContentType().getValue() != null) {
String contentType = ressponse.getEntity().getContentType().getValue();
if (contentType.contains("charset=")) {
return contentType.substring(contentType.indexOf("charset=") + 8);
}
}
return null;
}
/**
* 创建 SSL连接
*
* @return
* @throws GeneralSecurityException
*/
private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
@Override
public void verify(String host, SSLSocket ssl) throws IOException {
}
@Override
public void verify(String host, X509Certificate cert) throws SSLException {
}
@Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
}
});
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
} catch (GeneralSecurityException e) {
throw e;
}
}
public static void main(String[] args) {
try {
String str = post("https://localhost:443/ssl/test.shtml", "name=12&page=34",
"application/x-www-form-urlencoded", "UTF-8", 10000, 10000);
// String str=
// get("https://localhost:443/ssl/test.shtml?name=12&page=34","GBK");
/*
* Map<String,String> map = new HashMap<String,String>(); map.put("name",
* "111"); map.put("page", "222"); String str=
* postForm("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);
*/
System.out.println(str);
} catch (ConnectTimeoutException e) {
e.printStackTrace();
} catch (SocketTimeoutException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.URLEncodeUtil
public class URLEncodeUtil {
private final static String ENCODE = "UTF-8";
/**
* URL 解码
*/
public static String getURLDecoderString(String str) {
String result = "";
if (null == str) {
return "";
}
try {
result = java.net.URLDecoder.decode(str, ENCODE);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
/**
* URL 转码
*/
public static String getURLEncoderString(String str) {
String result = "";
if (null == str) {
return "";
}
try {
result = java.net.URLEncoder.encode(str, ENCODE);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
}
步骤四
获取到的qq信息实体类QQUserInfo
public class QQUserInfo {
private Integer ret;
private String msg;
private Integer is_lost;
private String nickname;
private String gender;
private String province;
private String city;
private String year;
private String constellation;
private String figureurl;
private String figureurl_1;
private String figureurl_2;
private String figureurl_qq;
private String figureurl_qq_1;
private String figureurl_qq_2;
private String is_yellow_vip;
private String vip;
private String yellow_vip_level;
private String level;
private String is_yellow_year_vip;
public Integer getRet() {
return ret;
}
public void setRet(Integer ret) {
this.ret = ret;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Integer getIs_lost() {
return is_lost;
}
public void setIs_lost(Integer is_lost) {
this.is_lost = is_lost;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public String getConstellation() {
return constellation;
}
public void setConstellation(String constellation) {
this.constellation = constellation;
}
public String getFigureurl() {
return figureurl;
}
public void setFigureurl(String figureurl) {
this.figureurl = figureurl;
}
public String getFigureurl_1() {
return figureurl_1;
}
public void setFigureurl_1(String figureurl_1) {
this.figureurl_1 = figureurl_1;
}
public String getFigureurl_2() {
return figureurl_2;
}
public void setFigureurl_2(String figureurl_2) {
this.figureurl_2 = figureurl_2;
}
public String getFigureurl_qq() {
return figureurl_qq;
}
public void setFigureurl_qq(String figureurl_qq) {
this.figureurl_qq = figureurl_qq;
}
public String getFigureurl_qq_1() {
return figureurl_qq_1;
}
public void setFigureurl_qq_1(String figureurl_qq_1) {
this.figureurl_qq_1 = figureurl_qq_1;
}
public String getFigureurl_qq_2() {
return figureurl_qq_2;
}
public void setFigureurl_qq_2(String figureurl_qq_2) {
this.figureurl_qq_2 = figureurl_qq_2;
}
public String getIs_yellow_vip() {
return is_yellow_vip;
}
public void setIs_yellow_vip(String is_yellow_vip) {
this.is_yellow_vip = is_yellow_vip;
}
public String getVip() {
return vip;
}
public void setVip(String vip) {
this.vip = vip;
}
public String getYellow_vip_level() {
return yellow_vip_level;
}
public void setYellow_vip_level(String yellow_vip_level) {
this.yellow_vip_level = yellow_vip_level;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getIs_yellow_year_vip() {
return is_yellow_year_vip;
}
public void setIs_yellow_year_vip(String is_yellow_year_vip) {
this.is_yellow_year_vip = is_yellow_year_vip;
}
}
返回url的类
public class QQUrl implements Serializable {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "QQUrl{" +
"url='" + url + '\'' +
'}';
}
}
步骤五
最后就是controller的编写了
得到qq登录的地址
/**
* 得到一个地址
*/
@GetMapping("/getQQUrl")
public QQUrl getQQUrl() {
// 拼接url
StringBuilder url = new StringBuilder();
url.append("https://graph.qq.com/oauth2.0/authorize?");
url.append("response_type=code");
url.append("&client_id=" + constants.getQqAppId());
// 回调地址 ,回调地址要进行Encode转码
String redirect_uri = constants.getQqRedirectUrl();
// 转码
url.append("&redirect_uri=" + URLEncodeUtil.getURLEncoderString(redirect_uri));
url.append("&state=ok");
// HttpClientUtils.get(url.toString(), "UTF-8");
QQUrl qqUrl = new QQUrl();
qqUrl.setUrl(url.toString());
return qqUrl;
}
2.回调函数的工具方法
/**
* 获得token信息(授权,每个用户的都不一致) --> 获得token信息该步骤返回的token期限为一个月
* <p>
* // * @param (保存到Map<String,String> qqProperties)
*
* @return
* @throws Exception
* @author wangsong
* @date 2019年6月18日 下午8:56:45
*/
public Map<String, Object> getToken(String code) throws Exception {
StringBuilder url = new StringBuilder();
url.append("https://graph.qq.com/oauth2.0/token?");
url.append("grant_type=authorization_code");
url.append("&client_id=" + constants.getQqAppId());
url.append("&client_secret=" + constants.getQqAppSecret());
url.append("&code=" + code);
// 回调地址
String redirect_uri = constants.getQqRedirectUrl();
// 转码
url.append("&redirect_uri=" + URLEncodeUtil.getURLEncoderString(redirect_uri));
// 获得token
String result = HttpClientConfigUtils.get(url.toString(), "UTF-8");
System.out.println("url:" + url.toString());
// 把token保存
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(result, "&");
String accessToken = StringUtils.substringAfterLast(items[0], "=");
Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
String refreshToken = StringUtils.substringAfterLast(items[2], "=");
//token信息
Map<String, Object> qqProperties = new HashMap<String, Object>();
qqProperties.put("accessToken", accessToken);
qqProperties.put("expiresIn", String.valueOf(expiresIn));
qqProperties.put("refreshToken", refreshToken);
return qqProperties;
}
/**
* 获取用户openId(根据token)
* <p>
* // * @param 把openId存到map中
*
* @return
* @throws Exception
*/
public String getOpenId(Map<String, Object> qqProperties) throws Exception {
// 获取保存的用户的token
String accessToken = (String) qqProperties.get("accessToken");
if (!StringUtils.isNotEmpty(accessToken)) {
// return "未授权";
}
StringBuilder url = new StringBuilder("https://graph.qq.com/oauth2.0/me?");
url.append("access_token=" + accessToken);
String result = HttpClientConfigUtils.get(url.toString(), "UTF-8");
String openId = StringUtils.substringBetween(result, "\"openid\":\"", "\"}");
return openId;
}
/**
* 根据token,openId获取用户信息
*/
public QQUserInfo getUserInfo(Map<String, Object> qqProperties) throws Exception {
// 取token
String accessToken = (String) qqProperties.get("accessToken");
String openId = (String) qqProperties.get("openId");
if (!StringUtils.isNotEmpty(accessToken) || !StringUtils.isNotEmpty(openId)) {
return null;
}
//拼接url
StringBuilder url = new StringBuilder("https://graph.qq.com/user/get_user_info?");
url.append("access_token=" + accessToken);
url.append("&oauth_consumer_key=" + constants.getQqAppId());
url.append("&openid=" + openId);
// 获取qq相关数据
String result = HttpClientConfigUtils.get(url.toString(), "UTF-8");
Object json = JSON.parseObject(result, QQUserInfo.class);
QQUserInfo userInfo = (QQUserInfo) json;
return userInfo;
}
/**
* 刷新token 信息(token过期,重新授权)
*
* @return
* @throws Exception
*/
@GetMapping("/refreshToken")
public Map<String, Object> refreshToken(Map<String, Object> qqProperties) throws Exception {
// 获取refreshToken
String refreshToken = (String) qqProperties.get("refreshToken");
StringBuilder url = new StringBuilder("https://graph.qq.com/oauth2.0/token?");
url.append("grant_type=refresh_token");
url.append("&client_id=" + constants.getQqAppId());
url.append("&client_secret=" + constants.getQqAppSecret());
url.append("&refresh_token=" + refreshToken);
System.out.println("url:" + url.toString());
String result = HttpClientConfigUtils.get(url.toString(), "UTF-8");
// 把新获取的token存到map中
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(result, "&");
String accessToken = StringUtils.substringAfterLast(items[0], "=");
Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
String newRefreshToken = StringUtils.substringAfterLast(items[2], "=");
//重置信息
qqProperties.put("accessToken", accessToken);
qqProperties.put("expiresIn", String.valueOf(expiresIn));
qqProperties.put("refreshToken", newRefreshToken);
return qqProperties;
}
3.回调方法
@GetMapping("/qqLogin")
public void qqLogin(String code,HttpServletRequest request) throws Exception {
//获取tocket
Map<String, Object> qqProperties = getToken(code);
//获取openId(每个用户的openId都是唯一不变的)
String openId = getOpenId(qqProperties);
/tocket过期刷新token
Map<String, Object> refreshToken = refreshToken(qqProperties);
//获取数据
QQUserInfo userInfo = getUserInfo(qqProperties);
}
qqLogin回调域可以很灵活的使用,用户主要就是用户登录后可以获取到唯一的id和qq用户的昵称头像等方法,进行操作
前后端分离的回调,可以通过redis和session进行监听用户是否登录成功,从而跳转到登录成功的页面
希望对于不会qq接口调用的同学有帮助
完结撒花~