开放平台需要企业认证才能注册,正好这次公司提供了一个账号,调通以后,就顺便写一篇博客吧。
公众平台与开放平台的区别
微信开放平台
主要面对移动应用/网站应用开发者,为其提供微信登录、分享、支付等相关权限和服务。
微信公众平台
微信公众平台用于管理、开放微信公众号(包括订阅号、服务号、企业号),简单的说就是微信公众号的后台运营、管理系统。
这里想吐槽一下,微信基本注册全都要邮箱,公众号一个、小程序一个、开放平台一个、我哪他妈有这么多邮箱啊,又记不住密码,下面正题。
官方文档链接
准备工作
网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。
在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
image.png
点击创建网站应用
image.png
按照要求填写信息,等待审核通过就可以获取appid和appsecret
image.png
然后找到回调授权域名这一项 修改成要回调的域名,例如: 不要加http://或https://
image.png
好了需要提前准备的东西就完事了,下面就是代码部分了。
image.png
这是完整的调用图
我这里用的spring boot项目进行测试的,但是代码都大致相同
image.png
第一步:请求CODE
这是自己用到的HttpClientUtils工具类
package com.lj.demo.controller.util;import mons.lang3.StringUtils;import org.apache.http.conn.ConnectTimeoutException;import org.apache.http.impl.client.HttpClients;import .SocketTimeoutException;import java.security.GeneralSecurityException;import java.io.IOException;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;import .ssl.SSLContext;import .ssl.SSLException;import .ssl.SSLSession;import .ssl.SSLSocket;import mons.io.IOUtils;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.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.conn.PoolingHttpClientConnectionManager;import org.apache.http.message.BasicNameValuePair;/*** @author LvJun* @create -03-16 17:51**/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<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 = 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<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<org.apache.http.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 = 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=GBKif (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() {@Overridepublic boolean verify(String arg0, SSLSession arg1) {return true;}@Overridepublic void verify(String host, SSLSocket ssl)throws IOException {}@Overridepublic void verify(String host, X509Certificate cert)throws SSLException {}@Overridepublic void verify(String host, String[] cns,String[] subjectAlts) throws SSLException {}});return HttpClients.custom().setSSLSocketFactory(sslsf).build();} catch (GeneralSecurityException e) {throw e;}}}
微信有两种方式认证一种是调转到微信域名下,还一种是直接把二维码嵌套到自己网站中。
这里是跳转到微信的认证方式
首先在页面做一个简单的按钮 触发事件获取认证code
<button onclick="login()">微信登录</button>
js调用部分
<script>function login() {$.ajax({type: "POST",//这个路径根据自己的情况填写url: "/demo-wechat/wechat/getCode",success: function (data) {console.log(data);if (data.code == 200) {window.location.href = (data.result);} else {alert("认证失败");}},error: function (data) {alert("认证失败");}});}</script>
后台代码url参数都要根据自己的实际情况进行修改
@RequestMapping("/getCode")public void getCode() throws Exception {//拼接urlStringBuilder url = new StringBuilder();url.append("https://open./connect/qrconnect?");//appidurl.append("appid=" + "appid");//回调地址 ,回调地址要进行Encode转码 String redirect_uri = "回调地址"//转码url.append("&redirect_uri=" + URLEncodeUtil.getURLEncoderString(redirect_uri));url.append("&response_type=code");url.append("&scope=snsapi_login");HttpClientUtils.get(url.toString(), "GBK");}
参数说明
redirect_uri 这个参数是用户扫码确认以后微信调用自己的路径例如:/wechat/callback
拼完路径可以打印出来看一下 完整的路径应该是这样
https://open./connect/qrconnect?appid=wxbdc5610cc59c1631&redirect_uri=https%3A%2F%%2Fwechat%2Fcallback.do&response_type=code&scope=snsapi_login&state=3d6be0a4035d839573b04816624a415e#wechat_redirect
到此获取code就完成了,用户确认以后回调自己的时候会带两个参数,
1.code
2.state
如果调用没有传递state就不会返回state 用户拒绝了登录请求,返回的结果不会有code
第二步:通过code获取access_token
接下来完成回调的方法直接上代码
/*** 微信回调*/@RequestMapping("/callback")public ModelAndView callback(String code,String state) throws Exception {System.out.println("===="+code+"==="+state+"====");if(StringUtils.isNotEmpty(code)){StringBuilder url = new StringBuilder();url.append("https://api./sns/oauth2/access_token?");url.append("appid=" + "appid");url.append("&secret=" + "secret");//这是微信回调给你的codeurl.append("&code=" + code);url.append("&grant_type=authorization_code");String result = HttpClientUtils.get(url.toString(), "UTF-8");System.out.println("result:" + result.toString());}return new ModelAndView("login");}
返回说明
正确的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
错误返回样例:
{"errcode":40029,"errmsg":"invalid code"}
这个时候我们拿到用了用户的一些必要的信息,剩下的就根据自己具体的业务进行后面的操作吧。
这里是第二种 认证方式,把二维码嵌套到自己的网站
这里只需要改前台代码
创建一个放二维码的容器
<div id="login_container" style="width: 500px;height: 500px;"></div>
导入微信的JS
//我这种写法是spring boot对应的 根据自己前端框架导入<script th:src="@{http://res./connect/zh_CN/htmledition/js/wxLogin.js}"></script>
在JS里面实例这么一段代码
var obj = new WxLogin({id:"login_container",appid: "appid",scope: "snsapi_login",redirect_uri: "回调地址",state: "",style: "",//这个是二维码样式href: ""});
这样打开这个页面就会自动加载微信二维码。扫码认证后 后面流程不变,返回的也是code,根据code获取token。
这只是一个最简单入门的Demo,代码也有很多需要优化的地方,欢迎大家指正说明,最后感谢大家观看。
作者:路西法Lucifer丶
链接:/p/89c43290d7f6
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。