微信公共号开发教程java版——微信网页授权(八)
一:微信网页授权介绍
官网详细介绍:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
关于网页授权回调域名的说明
1、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;
2、授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com无法进行OAuth2.0鉴权
3、如果公众号登录授权给了第三方开发者来进行管理,则不必做任何设置,由第三方代替公众号实现网页授权即可
关于网页授权的两种scope的区别说明
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
关于网页授权access_token和普通access_token的区别
1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
2、其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。
关于UnionID机制
1、请注意,网页授权获取用户基本信息也遵循UnionID机制。即如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。
2、UnionID机制的作用说明:如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为同一用户,对同一个微信开放平台下的不同应用(移动应用、网站应用和公众帐号),unionid是相同的。
关于特殊场景下的静默授权
1、上面已经提到,对于以snsapi_base为scope的网页授权,就静默授权的,用户无感知;
2、对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。
具体而言,网页授权流程分为四步:
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
二:网页授权的实现
1.用户同意授权,获取code
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。
尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问
参考链接(请在微信客户端中打开此链接体验):
scope为snsapi_base
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
scope为snsapi_userinfo
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
尤其注意:跳转回调redirect_uri,应当使用https链接来确保授权code的安全性。
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 公众号的唯一标识 |
redirect_uri | 是 | 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理 |
response_type | 是 | 返回类型,请填写code |
scope | 是 | 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 ) |
state | 否 | 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
wechat_redirect | 是 | 无论直接打开还是做页面302重定向时候,必须带此参数 |
下图为scope等于snsapi_userinfo时的授权页面:
用户同意授权后
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
错误返回码说明如下:
返回码 | 说明 |
---|---|
10003 | redirect_uri域名与后台配置不一致 |
10004 | 此公众号被封禁 |
10005 | 此公众号并没有这些scope的权限 |
10006 | 必须关注此测试号 |
10009 | 操作太频繁了,请稍后重试 |
10010 | scope不能为空 |
10011 | redirect_uri不能为空 |
10012 | appid不能为空 |
10013 | state不能为空 |
10015 | 公众号未授权第三方平台,请检查授权状态 |
10016 | 不支持微信开放平台的Appid,请使用公众号Appid |
2. 通过网页授权获取的用户信息
用户信息类:SNSUserInfo类
package com.wyj.wechart.pojo;import java.util.List;/*** 通过网页授权获取的用户信息* * * @author:WangYuanJun* @date:2018年1月24日 下午3:09:02*/
public class SNSUserInfo {// 用户标识private String openId;// 用户昵称private String nickname;// 性别(1是男性,2是女性,0是未知)private int sex;// 国家private String country;// 省份private String province;// 城市private String city;// 用户头像链接private String headImgUrl;// 用户特权信息private List<String> privilegeList;public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}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 getHeadImgUrl() {return headImgUrl;}public void setHeadImgUrl(String headImgUrl) {this.headImgUrl = headImgUrl;}public List<String> getPrivilegeList() {return privilegeList;}public void setPrivilegeList(List<String> privilegeList) {this.privilegeList = privilegeList;}}
3.凭证实体类
package com.wyj.wechart.pojo;/*** * 凭证* * @author:WangYuanJun* @date:2018年1月23日 下午3:19:14*/
public class Token {// 接口访问凭证private String accessToken;// 凭证有效期,单位:秒private int expiresIn;public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public int getExpiresIn() {return expiresIn;}public void setExpiresIn(int expiresIn) {this.expiresIn = expiresIn;}
}
4.网页授权信息 WeixinOauth2Token类
package com.wyj.wechart.pojo;/*** 网页授权信息* * * @author:WangYuanJun* @date:2018年1月24日 下午3:10:03*/
public class WeixinOauth2Token {// 网页授权接口调用凭证private String accessToken;// 凭证有效时长private int expiresIn;// 用于刷新凭证private String refreshToken;// 用户标识private String openId;// 用户授权作用域private String scope;public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public int getExpiresIn() {return expiresIn;}public void setExpiresIn(int expiresIn) {this.expiresIn = expiresIn;}public String getRefreshToken() {return refreshToken;}public void setRefreshToken(String refreshToken) {this.refreshToken = refreshToken;}public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}}
5.微信用户的基本信息WeixinUserInfo类
package com.wyj.wechart.pojo;/*** 微信用户的基本信息* * * @author:WangYuanJun* @date:2018年1月24日 上午10:05:42*/
public class WeixinUserInfo {// 用户的标识private String openId;// 关注状态(1是关注,0是未关注),未关注时获取不到其余信息private int subscribe;// 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间private String subscribeTime;// 昵称private String nickname;// 用户的性别(1是男性,2是女性,0是未知)private int sex;// 用户所在国家private String country;// 用户所在省份private String province;// 用户所在城市private String city;// 用户的语言,简体中文为zh_CNprivate String language;// 用户头像private String headImgUrl;public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}public int getSubscribe() {return subscribe;}public void setSubscribe(int subscribe) {this.subscribe = subscribe;}public String getSubscribeTime() {return subscribeTime;}public void setSubscribeTime(String subscribeTime) {this.subscribeTime = subscribeTime;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public int getSex() {return sex;}public void setSex(int sex) {this.sex = sex;}public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}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 getLanguage() {return language;}public void setLanguage(String language) {this.language = language;}public String getHeadImgUrl() {return headImgUrl;}public void setHeadImgUrl(String headImgUrl) {this.headImgUrl = headImgUrl;}
}
6.获取网页授权凭证及获取用户信息
package com.wyj.wechart.utils;import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.wyj.wechart.pojo.SNSUserInfo;
import com.wyj.wechart.pojo.WeixinOauth2Token;import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
/*** * * * @author:WangYuanJun* @date:2018年1月24日 下午3:19:44*/
public class AdvancedUtil {private static Logger log = LoggerFactory.getLogger(AdvancedUtil.class);/*** 获取网页授权凭证* * @param appId* 公众账号的唯一标识* @param appSecret* 公众账号的密钥* @param code* @return WeixinAouth2Token*/public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {WeixinOauth2Token wat = null;// 拼接请求地址String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";requestUrl = requestUrl.replace("APPID", appId);requestUrl = requestUrl.replace("SECRET", appSecret);requestUrl = requestUrl.replace("CODE", code);// 获取网页授权凭证JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);if (null != jsonObject) {try {wat = new WeixinOauth2Token();wat.setAccessToken(jsonObject.getString("access_token"));wat.setExpiresIn(jsonObject.getInt("expires_in"));wat.setRefreshToken(jsonObject.getString("refresh_token"));wat.setOpenId(jsonObject.getString("openid"));wat.setScope(jsonObject.getString("scope"));} catch (Exception e) {wat = null;int errorCode = jsonObject.getInt("errcode");String errorMsg = jsonObject.getString("errmsg");log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);}}return wat;}/*** 通过网页授权获取用户信息* * @param accessToken* 网页授权接口调用凭证* @param openId* 用户标识* @return SNSUserInfo*/@SuppressWarnings({ "deprecation", "unchecked" })public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {SNSUserInfo snsUserInfo = null;// 拼接请求地址String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);// 通过网页授权获取用户信息JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, "GET", null);if (null != jsonObject) {try {snsUserInfo = new SNSUserInfo();// 用户的标识snsUserInfo.setOpenId(jsonObject.getString("openid"));// 昵称snsUserInfo.setNickname(jsonObject.getString("nickname"));// 性别(1是男性,2是女性,0是未知)snsUserInfo.setSex(jsonObject.getInt("sex"));// 用户所在国家snsUserInfo.setCountry(jsonObject.getString("country"));// 用户所在省份snsUserInfo.setProvince(jsonObject.getString("province"));// 用户所在城市snsUserInfo.setCity(jsonObject.getString("city"));// 用户头像snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));// 用户特权信息snsUserInfo.setPrivilegeList(JSONArray.toList(jsonObject.getJSONArray("privilege"), List.class));} catch (Exception e) {snsUserInfo = null;int errorCode = jsonObject.getInt("errcode");String errorMsg = jsonObject.getString("errmsg");log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode, errorMsg);}}return snsUserInfo;}}
7.封装https请求类 CommonUtil 类
https请求的工具
package com.wyj.wechart.utils;import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;import javax.net.ssl.X509TrustManager;/*** 证书信任管理器(用于https请求)* 这个证书管理器的作用就是让它信任我们指定的证书,下面的代码意味着信任所有证书,不管是否权威机构颁发。* * @author:WangYuanJun* @date:2018年1月23日 下午3:22:19*/
public class MyX509TrustManager implements X509TrustManager {// 检查客户端证书public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 检查服务器端证书public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 返回受信任的X509证书数组public X509Certificate[] getAcceptedIssuers() {return null;}
}
/*** 发送https请求* * @param requestUrl* 请求地址* @param requestMethod* 请求方式(GET、POST)* @param outputStr* 提交的数据* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)*/public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {JSONObject jsonObject = null;try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = { new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();conn.setSSLSocketFactory(ssf);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);// 设置请求方式(GET/POST)conn.setRequestMethod(requestMethod);// 当outputStr不为null时向输出流写数据if (null != outputStr) {OutputStream outputStream = conn.getOutputStream();// 注意编码格式outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 从输入流读取返回内容InputStream inputStream = conn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;StringBuffer buffer = new StringBuffer();while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}// 释放资源bufferedReader.close();inputStreamReader.close();inputStream.close();inputStream = null;conn.disconnect();jsonObject = JSONObject.fromObject(buffer.toString());} catch (ConnectException ce) {log.error("连接超时:{}", ce);} catch (Exception e) {log.error("https请求异常:{}", e);}return jsonObject;}
8.写授权类:
替换成自己的appid 和 密钥
package com.wyj.wechart.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;import com.wyj.wechart.pojo.SNSUserInfo;
import com.wyj.wechart.pojo.WeixinOauth2Token;
import com.wyj.wechart.utils.AdvancedUtil;
/*** 授权后的回调请求处理* * * @author:WangYuanJun* @date:2018年1月27日 下午5:31:09*/
@Controller
@RequestMapping("/oauth")
public class OAuthController {@RequestMappingpublic ModelAndView index(String code,String state){ModelAndView mv = new ModelAndView("/index");// 用户同意授权if (!"authdeny".equals(code)) {// 获取网页授权access_tokenWeixinOauth2Token weixinOauth2Token = AdvancedUtil.getOauth2AccessToken("wx17fdedc3d6d0b68e", "c3b3d919d65a781ba7db58d9d8dfb515", code);// 网页授权接口访问凭证String accessToken = weixinOauth2Token.getAccessToken();// 用户标识String openId = weixinOauth2Token.getOpenId();// 获取用户信息SNSUserInfo snsUserInfo = AdvancedUtil.getSNSUserInfo(accessToken, openId);// 设置要传递的参数mv.addObject("snsUserInfo", snsUserInfo);mv.addObject("state", state);}return mv;}}
9.授权后,显示信息的页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>OAuth2.0网页授权</title>
</head>
<body><#if snsUserInfo??><table width="100%" cellspacing="0" cellpadding="0"><tr><td width="20%">属性</td><td width="80%">值</td></tr><tr><td>OpenID</td><td>${snsUserInfo.openId}</td></tr><tr><td>昵称</td><td>${snsUserInfo.nickname}</td></tr><tr><td>性别</td><td>${snsUserInfo.sex}</td></tr><tr><td>国家</td><td>${snsUserInfo.country}</td></tr><tr><td>省份</td><td>${snsUserInfo.province}</td></tr><tr><td>城市</td><td>${snsUserInfo.city}</td></tr><tr><td>头像</td><td>${snsUserInfo.headImgUrl}</td></tr>
<!-- <tr><td>特权</td><td>${snsUserInfo.privilegeList}</td></tr> --><tr><td>state:</td><td>${state}</td></tr></table><#else><p>用户不同意授权,未获取到用户信息!</p>
</#if></body>
</html>
10.application.properties配置
server.port=80spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
spring.freemarker.request-context-attribute=request
spring.freemarker.template-loader-path=classpath:/templates
spring.freemarker.suffix=.html
spring.mvc.static-path-pattern=/static/**
11.替换官方的链接成我们的方法路径:
官方的请求链接:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
需要修改的地方:
(1)替换自己的AppID
(2)将redirect_url换成自己的授权请求链接URL。注意这个连接需要经过UTF-8编码。
(3)需要修改scope。需要弹出页面则要修改为snsapi_userinfo 。
scope参数的解释:
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
URL转码
/*** URL编码(utf-8)* * @param source* @return*/public static String urlEncodeUTF8(String source) {String result = source;try {result = java.net.URLEncoder.encode(source, "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result;}
package com.wyj.wechart.test;import com.wyj.wechart.utils.CommonUtil;
/*** URL转码* * * @author:WangYuanJun* @date:2018年1月27日 下午5:35:02*/
public class TransCodeUrlTest {/*** 生成URL编码* * @param args*/public static void main(String[] args) {String source = "http://6400cc45.ngrok.io/oauth";System.out.println(CommonUtil.urlEncodeUTF8(source));}
}
也可以直接在线url编码: http://tool.chinaz.com/Tools/URLEncode.aspx
12.修改网页授权获取用户基本信息
微信公共平台->测试号管理->体验接口权限表->网页服务->网页帐号->修改
修改完成后需要重新关注
13.测试效果:
复制上面替换好的链接,然后丢进浏览器,然后用微信来扫一扫。
注:github项目地址:微信公共号开发用例
微信公共号开发教程java版——微信网页授权(八)相关推荐
- 微信公共号开发教程java版——获取用户基本信息(UnionID机制)(七)
一:UnionID机制说明 官网详细介绍:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839 获取用户基本信息( ...
- 微信公共号开发教程java版——请求消息,响应消息及事件消息类的封装(三)
一:封装请求信息 当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上. 各消息类型的推送XML数据包结构如下: 查看官网详细介绍 文本消息<xml&g ...
- 微信公众平台开发教程Java版(六) 事件处理(菜单点击/关注/取消关注)
前言: 事件处理是非常重要的,这一章讲讲常见的事件处理 1.关注/取消关注 2.菜单点击 事件类型介绍: 在微信中有事件请求是消息请求中的一种.请求类型为:event 而event事件类型又分多种事件 ...
- 微信公众号开发java流程_微信公众号开发教程java 编程语言的特点及选择
微信公众号开发教程java 编程语言的特点及选择 微信公众号为用户提供了相关的工具,来对微信公众号进行一个简单的开发.但是如果想实现一些复杂的功能,其实还是要借助于一些编程语言的使用.所以要了解,在微 ...
- 微信公众平台开发教程Java版(三) 消息接收和发送
前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送 可以先了解公众平台的消息api接口(接收消息,发送消息) http://mp.weixin.qq.com/wiki/index.ph ...
- java 微信 接收消息_微信公众平台开发教程Java版(三) 消息接收和发送
https://www.iteye.com/blog/tuposky-2017429 前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送 可以先了解公众平台的消息api接口(接收消息, ...
- 微信公众号开发教程(六)获取微信用户信息-网页授权
作者:陈惠,叩丁狼教育高级讲师.原创文章,转载请注明出处. 在学习网页授权之前,我们先来了解下这次的需求: 我们的应用中有一个用来显示个人信息的页面,当微信用户在微信app中打开这个页面,希望可以获取 ...
- 微信公众平台开发教程Java版(一)环境准备篇
准备写系列博客,记录下我的微信公众平台学习记录,也为那些摸索中的开发者提供点参考. 希望与大家共同进步. 微信3.0的时候我开始做微信公众账号,那时候没时间研究开发,先用的是编辑者模式,后用开发者模式 ...
- 【微信公众号开发(Java版)笔记整理二】消息处理
版权声明:本文为 小异常 原创文章,非商用自由转载-保持署名-注明出处,谢谢! 本文网址:https://sunkuan.blog.csdn.net/article/details/117574310 ...
- 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单
我们来了解一下 自定义菜单创建接口: http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_to ...
最新文章
- 新手入门深度学习 | 卷积神经网络是什么?
- 怎么使用继承的一个实例
- ubuntu16.04搭建ftp服务器
- IE 10的新HTML​解析规则​
- Cookie和Session的作用和工作原理
- leetcode(283)移动零
- 前端学习(1810):前端调试之css装饰cursor
- 蓝桥杯第五届省赛JAVA真题----七对数字
- java数组 0912
- ibatis resultclass java.util.list_mybatis 动态sql返回一个List封装类报错求解决方法
- rhel-server-7.2-x86_64无法联网(VMware环境)
- Java基础——枚举类的使用教程
- 微软发布 .NET Core 卸载工具,适用于 Windows 和 Mac
- 简述mysql事件作用_MYSQL使用简述
- python模块基础之OS模块
- 牛腩新闻发布--.NET控件FreeTextBox使用方法
- nes 红白机模拟器 第4篇 linux 手柄驱动支持
- 前后端分离之Web前端架构设计
- ECS云服务器搭建自己的博客网站worldpress
- python读取grib2文件