前言
现在越来越多的互联网应用需要对接第三方用户登录(比如:微信、QQ、新浪微博等),今天我就记录一下在springboot+vue前后端分离项目中对接微信扫描二维码登录的步骤及相关代码。
项目中使用的架构:后端spingboot + mybatis + shrio、前端vue + element-ui。
参考文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
第一步 新建网站应用
微信开放平台认证的过程相对慢些,提交完公司相关资料审核通过需要2~3天左右。开放平台账号准备好后我们开始新建网站应用(个人是没有资格认证开放平台的,请注意!!! )。
提交审核后就等着吧,网站应用的审核时间是1~2天,审核通过后我们就可以拿到AppID和AppSecret了。
第二步 编码及测试
前端引用了vue-wxlogin插件,需要在前端项目中执行以下命令:
npm install vue-wxlogin --save-dev
在login.vue中导入改插件:
import wxlogin from 'vue-wxlogin'
在需要显示二维码的地方贴入以下代码:
<wxlogin :theme="'black'"
:appid="appid"
:scope="'snsapi_login'"
:redirect_uri="redirect_uri"></wxlogin>
在login.vue的creator钩子函数中贴入下面代码:
getwxcode () {
if (this.$route.query.code) {
this.code = this.$route.query.code
let params = {}
params.appid = this.appid
params.secret = this.secret
params.code = this.code
params.grant_type = 'authorization_code'
getAccessToken(params).then((res) => {
if (res.code === 0) {
this.$cookie.set('token', res.token)
this.$router.replace({ name: 'home' })
} else if (res.code === 500) {
this.dialogVisible4 = true
this.openId = res.openId
} else {
this.$message({
message: res.msg,
type: 'error'
})
}
})
}
},
用户扫描二维码确认登录之后还回调到登录页面,执行获取getAccessToken请求,后端根据获取到的用户openid到用户中心查询用户,能查询用户的话登录成功,查不到用户会提示绑定账号。
后端代码:
@RequestMapping("/getAccessToken")
public void getAccessToken(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
JSONObject tempJson = new JSONObject();
String code = request.getParameter("code");
String grantType = "authorization_code";
JSONObject result = JSONObject.parseObject(HttpsUtils.sendGetRequest(accessTokenUrl+"?&appid=" + appId + "&secret=" + secret + "&code=" + code + "&grant_type=" + grantType, null));
openid = result.get("openid").toString();
try {
out = response.getWriter();
//查询用户信息
UserEntity userEntity = userService.queryByPcOpenId(openid);
if(org.springframework.util.StringUtils.isEmpty(userEntity)){
tempJson.put("msg", "根据OPENID查询不到用户!");
tempJson.put("openId", openid);
tempJson.put("code", 500);
out.append(tempJson.toJSONString());
} else {
R r = sysTokenService.createToken(userEntity.getId(),"sys");
tempJson.put("openId", openid);
tempJson.put("token", r.get("token"));
tempJson.put("code", 0);
out.append(tempJson.toJSONString());
}
} catch (Exception e) {
userService.updateOpenIdIsNull(openid);
e.printStackTrace();
}
}
HttpsUtils代码:
package cn.demo.common.utils;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
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 javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
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.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
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;
public class HttpsUtils {
private static Logger logger = LoggerFactory.getLogger(HttpsUtils.class);
static CloseableHttpClient httpClient;
static CloseableHttpResponse httpResponse;
public static CloseableHttpClient createSSLClientDefault() {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
return HttpClients.createDefault();
}
/**
* 发送HTTP_GET请求
* @see 该方法会自动关闭连接,释放资源
* @param requestURL 请求地址(含参数)
* @param decodeCharset 解码字符集,解析响应数据时用之,其为null时默认采用UTF-8解码
* @return 远程主机响应正文
*/
public static String sendGetRequest(String reqURL, String decodeCharset){
long responseLength = 0; //响应长度
String responseContent = null; //响应内容
HttpClient httpClient = new DefaultHttpClient(); //创建默认的httpClient实例
HttpGet httpGet = new HttpGet(reqURL); //创建org.apache.http.client.methods.HttpGet
try{
HttpResponse response = httpClient.execute(httpGet); //执行GET请求
HttpEntity entity = response.getEntity(); //获取响应实体
if(null != entity){
responseLength = entity.getContentLength();
responseContent = EntityUtils.toString(entity, decodeCharset==null ? "UTF-8" : decodeCharset);
EntityUtils.consume(entity); //Consume response content
}
System.out.println("请求地址: " + httpGet.getURI());
System.out.println("响应状态: " + response.getStatusLine());
System.out.println("响应长度: " + responseLength);
System.out.println("响应内容: " + responseContent);
}catch(ClientProtocolException e){
logger.debug("该异常通常是协议错误导致,比如构造HttpGet对象时传入的协议不对(将'http'写成'htp')或者服务器端返回的内容不符合HTTP协议要求等,堆栈信息如下", e);
}catch(ParseException e){
logger.debug(e.getMessage(), e);
}catch(IOException e){
logger.debug("该异常通常是网络原因引起的,如HTTP服务器未启动等,堆栈信息如下", e);
}finally{
httpClient.getConnectionManager().shutdown(); //关闭连接,释放资源
}
return responseContent;
}
/**
* 发送https请求
*
* @param
* @throws Exception
*/
public static String sendByHttp(Map<String, Object> params, String url) {
try {
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> listNVP = new ArrayList<NameValuePair>();
if (params != null) {
for (String key : params.keySet()) {
listNVP.add(new BasicNameValuePair(key, params.get(key).toString()));
}
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(listNVP, "UTF-8");
logger.info("创建请求httpPost-URL={},params={}", url, listNVP);
httpPost.setEntity(entity);
httpClient = HttpsUtils.createSSLClientDefault();
httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
if (httpEntity != null) {
String jsObject = EntityUtils.toString(httpEntity, "UTF-8");
return jsObject;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
httpResponse.close();
httpClient.close();
logger.info("请求流关闭完成");
} catch (IOException e) {
logger.info("请求流关闭出错");
e.printStackTrace();
}
}
}
}