【微信开放平台】微信第三方扫码登录(亲测可用)

开放平台需要企业认证才能注册,正好这次公司提供了一个账号,调通以后,就顺便写一篇博客吧。
公众平台与开放平台的区别 微信开放平台 主要面对移动应用/网站应用开发者,为其提供微信登录、分享、支付等相关权限和服务。
微信公众平台 微信公众平台用于管理、开放微信公众号(包括订阅号、服务号、企业号),简单的说就是微信公众号的后台运营、管理系统。
这里想吐槽一下,微信基本注册全都要邮箱,公众号一个、小程序一个、开放平台一个、我哪他妈有这么多邮箱啊,又记不住密码,下面正题。
官方文档链接
准备工作 网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。
在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。

【微信开放平台】微信第三方扫码登录(亲测可用)
文章图片
image.png
点击创建网站应用
【微信开放平台】微信第三方扫码登录(亲测可用)
文章图片
image.png
按照要求填写信息,等待审核通过就可以获取appid和appsecret
【微信开放平台】微信第三方扫码登录(亲测可用)
文章图片
image.png
然后找到回调授权域名这一项 修改成要回调的域名,例如:www.baidu.com 不要加http://或https://
【微信开放平台】微信第三方扫码登录(亲测可用)
文章图片
image.png
好了需要提前准备的东西就完事了,下面就是代码部分了。

【微信开放平台】微信第三方扫码登录(亲测可用)
文章图片
image.png
这是完整的调用图
我这里用的spring boot项目进行测试的,但是代码都大致相同

【微信开放平台】微信第三方扫码登录(亲测可用)
文章图片
image.png 第一步:请求CODE 这是自己用到的HttpClientUtils工具类

package com.lj.demo.controller.util; import org.apache.commons.lang3.StringUtils; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.impl.client.HttpClients; import java.net.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 javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.apache.commons.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 2018-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 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; } }}

微信有两种方式认证一种是调转到微信域名下,还一种是直接把二维码嵌套到自己网站中。
这里是跳转到微信的认证方式 首先在页面做一个简单的按钮 触发事件获取认证code

js调用部分

后台代码url参数都要根据自己的实际情况进行修改
@RequestMapping("/getCode") public void getCode() throws Exception { //拼接url StringBuilder url = new StringBuilder(); url.append("https://open.weixin.qq.com/connect/qrconnect?"); //appid url.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"); }

参数 是否必填 说明
appid 应用唯一标识
redirect_uri 请使用urlEncode对链接进行处理
response_type 填code
scope 网页应用目前仅填写snsapi_login即可
state 安全性相关具体可以参考官方文档这里可以不填
参数说明
redirect_uri 这个参数是用户扫码确认以后微信调用自己的路径例如:https://www.baidu.com/wechat/callback
拼完路径可以打印出来看一下 完整的路径应该是这样
https://open.weixin.qq.com/connect/qrconnect?appid=wxbdc5610cc59c1631&redirect_uri=https%3A%2F%2Fpassport.yhd.com%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.weixin.qq.com/sns/oauth2/access_token?"); url.append("appid=" + "appid"); url.append("&secret=" + "secret"); //这是微信回调给你的code url.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"}
这个时候我们拿到用了用户的一些必要的信息,剩下的就根据自己具体的业务进行后面的操作吧。
这里是第二种 认证方式,把二维码嵌套到自己的网站 这里只需要改前台代码
创建一个放二维码的容器

导入微信的JS
//我这种写法是spring boot对应的 根据自己前端框架导入

在JS里面实例这么一段代码
var obj = new WxLogin({ id:"login_container", appid: "appid", scope: "snsapi_login", redirect_uri: "回调地址", state: "", style: "",//这个是二维码样式 href: "" });

【【微信开放平台】微信第三方扫码登录(亲测可用)】这样打开这个页面就会自动加载微信二维码。扫码认证后 后面流程不变,返回的也是code,根据code获取token。
这只是一个最简单入门的Demo,代码也有很多需要优化的地方,欢迎大家指正说明,最后感谢大家观看。

    推荐阅读