首先导入所需要的相关依赖:
org.apache.commons
commons-lang3
3.8.1
org.springframework.boot
spring-boot-starter-validation
commons-io
commons-io
2.5
org.apache.httpcomponents
httpcore
4.4.11
org.apache.httpcomponents
httpclient
4.5.8
在配置文件中加入信息:
### QQ
constants.qqAppId=101513767
constants.qqAppSecret=b1d978cefcf405388893d8e686d307b0
constants.qqRedirectUrl=http://127.0.0.1:8080/QQLogin
新建一个配置文件信息常量类Constants
package com.lz.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Constants {
@Value("${constants.qqAppId}")
private String qqAppId;
@Value("${constants.qqAppSecret}")
private String qqAppSecret;
@Value("${constants.qqRedirectUrl}")
private String qqRedirectUrl;
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;
}
}
在pojo层新建一个QQ的数据实体类QQUserInfo
package com.lz.entity;
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;
//下面的get,set方法省略了,可自行生成
http工具类 HttpClientUtils
package com.lz.utils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
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.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
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 javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Created by Administrator on 2018/10/30/030.
*/
@SuppressWarnings("all")
public class HttpClientUtils {
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 params)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}
public static String postParameters(String url, Map 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 = HttpClientUtils.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 params, Map headers,
Integer connTimeout, Integer readTimeout)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
HttpClient client = null;
HttpPost post = new HttpPost(url);
try {
if (params != null && !params.isEmpty()) {
List formParams = new ArrayList();
Set> entrySet = params.entrySet();
for (Entry 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 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 = HttpClientUtils.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 = HttpClientUtils.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 map = new HashMap(); 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();
}
}
}
url转码工具类 URLEncodeUtil.java
package com.lz.utils;
import java.io.UnsupportedEncodingException;
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;
}
}
QQController
package com.lz.controller;
import com.alibaba.fastjson.JSON;
import com.lz.entity.QQUserInfo;
import com.lz.utils.HttpClientUtils;
import com.lz.utils.URLEncodeUtil;
import com.lz.entity.Constants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
/**
* qq登录
*
* @author wangsong
* @date 2019年6月18日 下午8:04:15
*/
@Controller
public class QQController {
/**
* QQ :读取Appid相关配置信息静态类
*/
@Autowired
private Constants constants;
/**
* 登录页
*/
@GetMapping("/")
public String login() {
return "login";
}
/**
* 获得跳转到qq登录页的url,前台直接a连接访问
*
* @author wangsong
* @date 2019年6月18日 下午8:29:20
* @param session
* @return
* @throws Exception
*/
@GetMapping("/getQQCode")
public String getCode(HttpSession session, Model model) throws Exception {
// 拼接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");
System.out.println(url.toString());
model.addAttribute("url", url);
return "login";
}
/**
* 开始登录
*
* @param code
* @param
* @param 实际业务:token过期调用刷新 token重新获取token信息
* @param 数据库字段: accessToken,expiresIn,refreshToken,openId
* @return
* @throws Exception
*/
@GetMapping("/QQLogin")
@ResponseBody
public QQUserInfo QQLogin(String code, Model model) throws Exception {
if (code != null) {
System.out.println(code);
}
//获取tocket
Map qqProperties = getToken(code);
//获取openId(每个用户的openId都是唯一不变的)
String openId = getOpenId(qqProperties);
qqProperties.put("openId",openId);
//tocket过期刷新token
//Map refreshToken = refreshToken(qqProperties);
//获取数据
QQUserInfo userInfo = getUserInfo(qqProperties);
return userInfo;
}
/**
* 获得token信息(授权,每个用户的都不一致) --> 获得token信息该步骤返回的token期限为一个月
*
* @param (保存到Map qqProperties)
* @author wangsong
* @return
* @throws Exception
* @date 2019年6月18日 下午8:56:45
*/
public Map 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 = HttpClientUtils.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 qqProperties = new HashMap();
qqProperties.put("accessToken", accessToken);
qqProperties.put("expiresIn", String.valueOf(expiresIn));
qqProperties.put("refreshToken", refreshToken);
return qqProperties;
}
/**
* 刷新token 信息(token过期,重新授权)
*
* @return
* @throws Exception
*/
@GetMapping("/refreshToken")
public Map refreshToken(Map 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 = HttpClientUtils.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;
}
/**
* 获取用户openId(根据token)
*
* @param 把openId存到map中
* @return
* @throws Exception
*/
public String getOpenId(Map 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 = HttpClientUtils.get(url.toString(), "UTF-8");
String openId = StringUtils.substringBetween(result, "\"openid\":\"", "\"}");
return openId;
}
/**
* 根据token,openId获取用户信息
*/
public QQUserInfo getUserInfo(Map 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 = HttpClientUtils.get(url.toString(), "UTF-8");
Object json = JSON.parseObject(result, QQUserInfo.class);
QQUserInfo userInfo = (QQUserInfo) json;
return userInfo;
}
}
HTML页面
获取qq登录连接
开始登录
首先进入阿里云官网,搜索并开通短信服务,选择国内信息,添加签名模板和模板管理
之后在概览中获取一个自己的私钥,查看并保存下来
点击国内消息,新添加一个签名和模板
添加相关的依赖
com.aliyun
aliyun-java-sdk-core
4.4.0
com.aliyun
aliyun-java-sdk-dysmsapi
1.0.0
添加一个发送短信的工具类
@Component
public class Message {
private static final Logger logger= LoggerFactory.getLogger(Message.class);
public static void messagePost(String mobile,String password){
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "(自己的私钥)", "(自己的私钥密码)");
IAcsClient client = new DefaultAcsClient(profile);
logger.info(mobile+"\t"+password);
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", "cn-hangzhou");
request.putQueryParameter("PhoneNumbers", mobile);
request.putQueryParameter("SignName", "(签名名字)");
request.putQueryParameter("TemplateCode", "(自己的CODE)");
request.putQueryParameter("TemplateParam", "{code:" + password+ "}");
try {
CommonResponse response = client.getCommonResponse(request);
logger.info(response.getData());
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
}
}
}
在logincontroller中新添加方法
@RequestMapping(value = "/do_authcode")
@ResponseBody
public Result authcode_get(LoginVo loginVo) {
String mobile=loginVo.getMobile();
String password = "1" + RandomStringUtils.randomNumeric(5);//生成随机数,我发现生成5位随机数时,如果开头为0,发送的短信只有4位,这里开头加个1,保证短信的正确性
logger.info(mobile+"\t"+password);
redisService.set(UserKey.getById,mobile,password);//将验证码存入缓存
Message.messagePost(mobile,password);//发送短息
logger.info("----->"+mobile+"\t"+password);
return Result.success(true);
}
@RequestMapping("/authcode_login")
@ResponseBody
public Result authcode_login(Model model, LoginVo loginVo) {
MiaoshaUser user = new MiaoshaUser();
user.setNickname("pony");
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
String verCode = redisService.get(UserKey.getById,mobile, String.class);
logger.info(verCode);
if (password.equals(verCode)) {
model.addAttribute("user",user.getNickname());
return Result.success(true);
} else {
return Result.error(CodeMsg.PASSWORD_ERROR);
}
}