微信公众号开发简介
文末附源碼,或可先將依賴復制到自己的項目,內容參考鄭清的博客-CSDN博客
1、微信中的幾個概念
想要進行微信相關的開發,首先是要弄明白幾個概念,明確自己要干什么:
(1)微信公眾平臺和微信開放平臺
開放平臺:https://open.weixin.qq.com;公眾平臺:https://mp.weixin.qq.com
我們可以分別看一兩者的官網,首先看開放平臺
微信開放平臺就是微信把自家的服務給其他人使用。比如你的網站要做網頁授權登陸,就需要來到開放平臺,在開放平臺中將你的網站注冊到其中;再比如想要自己開發的手機app實現微信分享、收藏,使用微信支付,也是需要到開放平臺。這里的重點是你的應用要使用微信的服務。
其實在開放平臺中也包含公眾平臺的入口,那就是其中的公眾賬號開發,點進去就會進入公眾平臺,公眾平臺官網如下:
公眾平臺要做的事是讓所有人(普通人和開發者)更好的使用微信自己的產品,即服務號、訂閱號、小程序、企業微信。所謂更好的使用就是幫助我們使用使用它們,讓他們符合我們的需求,比如對于普通人,想使訂閱號發布內容、設置自動回復等。比如對于開發者想使用公眾號更有個性化的內容回復、進行網頁授權等,網頁授權也就是在公眾號內打開網頁,在網頁中可以獲取微信用戶的信息,這與上邊提到的第三方應用微信授權登錄是不一樣的,公眾號內的授權只能在微信客戶端中使用。但其實本質上差不多,都是去獲取微信用戶的信息。
這里可以知道,使用訂閱號、服務號、企業微信、小程序實現個性化業務就要到公眾平臺。這里的重點是你要直接使用微信的產品。
(2)公眾號
公眾號是一個統稱,通常我們說的就是訂閱號和服務號,訂閱月就是我們微信中放在一起的公眾號,服務號就是那些單獨的、在聊天頁面的公眾號。但是還包括微信小程序和企業微信,比如想使用企業微信管理員工、進行小程序開發。
(3)appID、appsecret、openID、unionID(來自微微信開放文檔 )
| AppID | - AppID是不同類型的產品的帳號ID,是帳號的唯一標識符。 - 例如公眾號的AppID、小程序的AppID、開放平臺的AppID、第三方平臺的AppID、移動應用的AppID、網站應用的AppID、小商店的AppID等等。 |
| openid | - openid是微信用戶在不同類型的產品的身份ID。 - 微信用戶訪問公眾號、小程序、移動應用、網站應用、小商店等都會有唯一的openid,但同一個微信用戶訪問不同的產品生成的openid也是不一樣的。 - 例如,對于不同公眾號,同一用戶的openid不同;同理,對于不同的小程序,同一用戶的openid也是不同的 |
| unionid | - unionid是微信用戶在同一個開放平臺下的產品的身份ID。 - 如果開發者擁有多個移動應用、網站應用、和公眾帳號(即公眾號和小程序),可通過 UnionID 來區分用戶的唯一性,因為只要是同一個微信開放平臺帳號下的移動應用、網站應用和公眾帳號,用戶的 UnionID 是唯一的。即,同一用戶,對同一個微信開放平臺下的不同應用,UnionID是相同的。 |
(4)code、access_token:
access_token是微信接口調用憑證,微信如何給我們提供功能和服務呢?自然是通過接口,微信的接口調用需要憑證,那就是access_token
code:在某些場景下,主要是用戶認證授權,如何確保用戶真的授權了,那就是code,用戶點了同意授權就會得到一個code,通過這個code就可以獲取access_token,從而使用微信接口。
2、微信公眾號模式
微信公眾號有兩種開發模式:(圖片來自微信公眾號開發 (1) 微信接入認證成為開發者)
這位老哥的圖里已經說的很清楚了。其實就是兩種公眾,普通人和開發者如何更好的使用公眾號的兩種模式。
既然是微信公眾號的開發,那么自然我們要討論的是第二種模式了。
3、開發前準備工作
(1)獲取一個公眾號
測試公眾號申請地址https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
既然要做公眾號的開發,自然首先需要有一個公眾號了,當然如果嫌注冊麻煩,或者已有公眾號但是不能使用,也可申請一個測試公眾號,隨便折騰,如下圖所示:
(2)將你自己電腦的80端口映射出去
也就說做內網穿透,讓微信服務可以訪問你電腦的服務。這里推薦使用ngrok (ngrok.cc),關于ngrok使用很簡單,注冊登錄之后選擇隧道管理–開通隧道,免費的就行,但是需要2元錢的實名認證費用。填寫配置,開通就行了,注意一定得是80端口。
然后下載客戶端,根據文檔使用命令啟動就行,啟動后我們接可以看到如下界面:
在外網訪問http://iwat.free.idcfengye.com就會請求到本機的80端口。
3、認證成為開發者
對微信公眾號開發又了一定的了解,做好了準備工作之后,就開始寫代碼了。這里我們使用java進行演示,創建spring-boot項目。
根據上邊的圖我們知道,我們需要搭建一個自己的服務器,然后我們在自己的服務中通過微信服務器給用戶發消息等,同時用戶也是通過微信服務器請求我們服務器的服務,所以首先就要讓微信認識我們的服務器,同時可以訪問到我們的服務(所以要把80映射出去)。認證成為開發者這一步要做的就是讓我們的服務和微信服務器相互認識。
(1)創建springboot項目,新建一個controller
添加如下代碼:
@RestController @RequestMapping("/api/weixin/index") @Api(tags = "微信 - 接口") public class IndexController {// TODO 這里的token是微信公眾平臺上自己所配的!private static final String token = "zhengqing";@Autowiredprivate MsgService msgService;/*** 處理微信認證:驗證服務器地址的有效性,get提交* signature: 微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。* timestamp 時間戳* nonce: 隨機數* echostr: 隨機字符串*/@GetMappingpublic void checkSignature(HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("============= 處理微信認證 ===============");// 拿到微信的請求參數String signature = request.getParameter("signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echostr = request.getParameter("echostr");// ① 將token、timestamp、nonce三個參數進行字典序排序 b a d c h ==>a b c d hString[] strArr = {token, timestamp, nonce};// 字典排序Arrays.sort(strArr);// ② 將三個參數字符串拼接成一個字符串進行sha1加密StringBuffer sb = new StringBuffer();// 字符串拼接for (String str : strArr) {sb.append(str);}// 加密String sha1Str = SecurityUtil.sha1(sb.toString());// ③ 開發者獲得加密后的字符串可與signature對比,標識該請求來源于微信if (sha1Str.equals(signature)) {// 如果相等,就是來自微信請求// 若確認此次GET請求來自微信服務器,原樣返回echostr參數內容,則接入生效response.getWriter().println(echostr);}} }SecurityUtil代碼
public class SecurityUtil {public static String sha1(String str) {try {StringBuilder sb = new StringBuilder();MessageDigest digest = MessageDigest.getInstance("sha1");// 放入加密字符串digest.update(str.getBytes());// 進行加密byte[] digestMsg = digest.digest();// byte轉換16進制for (byte b : digestMsg) {sb.append(String.format("%02x", b));}return sb.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return str;} }? (2)配置端口號
(3)啟動項目
(4)在測試號頁面填寫公眾號配置信息
提交,顯示配置成功就可以了。
4、通過接口自定義微信公眾號中的菜單
有了測試公眾號,點擊關注以后進入發現,里面沒有任何菜單。公眾號的菜單可以在公眾平臺直接設置,也可以通過在我們的服務中調用微信的接口去設置。
(1)第一步要看一下微信自定義菜單的文檔:微信開放文檔-自定義菜單 (qq.com)
(2)第二步我們可以先在微信公眾平臺接口調試工具 (qq.com) 中使用接口感受一下
首選獲取access_token
?
然后設置菜單:
菜單項(注意微信文檔中的測試菜單包含了小程序url,需要刪除)
{"button":[{ "type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC"},{"name":"菜單","sub_button":[{ "type":"view","name":"搜索","url":"http://www.soso.com/"},{"type":"miniprogram","name":"wxa","url":"http://mp.weixin.qq.com"},{"type":"click","name":"贊一下我們","key":"V1001_GOOD"}]}]}如此取關測試公眾號,然后重新關注就可以看到菜單的變化了。
(3)自己寫代碼
首先獲取access_token,創建WeixinService接口及其實現類、MenuService及其實現類,如圖
WerixinServiceImpl實現類代碼:
@Service public class WeixinServiceImpl implements WeixinService {@Autowiredprivate RestTemplate restTemplate;@Overridepublic AccessTokenVO getAccessToken(String appId, String appSecret) {AccessTokenVO accessTokenVO = restTemplate.getForObject(Constants.GET_ACCESS_TOKEN_URL.replace("APPID", appId).replace("APPSECRET", appSecret), AccessTokenVO.class);return accessTokenVO;} }AccessTokenVO代碼
@Data//lombok注解 public class AccessTokenVO {@ApiModelProperty(value = "獲取到的憑證")private String access_token;@ApiModelProperty(value = "憑證有效時間,單位:秒(微信目前暫7200秒,即2小時,過期后需再次獲取)")private int expires_in;}WeixinResponseResult代碼
@Data public class WeixinResponseResult {@ApiModelProperty(value = "響應碼")private int errcode;@ApiModelProperty(value = "響應消息")private String errmsg;}Constants代碼
public class Constants {/*** TODO 填寫自己的 `appID` 和 `appsecret`*/public static final String APP_ID = "xxxx";public static final String APP_SECRET = "xxxx";/*** 通過 `GET請求方式` 獲取 `access_token`*/public static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";/*** TODO 只做臨時方便測試使用*/public static final String ACCESS_TOKEN = "55_Nm9XUj_ZM-yHUt10mHEZ4BRMuXt-Dlm33pOWcycHy_EfAPlTB7Wd6-j01Hw0VwrqNARbm1jQzro4J1FGhpney0uu6H-7d3ajFM6Vd-85oJ7R6MgY29HlsRQ4cDB63yH1dTqbVrTBulzKnHhxGVAbAAANCH";/*** 查詢菜單接口 - GET請求*/public static final String GET_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";/*** 刪除菜單接口 - GET請求 (注意,在個性化菜單時,調用此接口會刪除默認菜單及全部個性化菜單)*/public static final String DELETE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";/*** 創建菜單接口 - POST請求*/public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";/*** 用戶認證*/public static final String AUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";/*** 用戶認證重定向url*/public static final String AUTH_REDIRECT_URI = "http://iwat.free.idcfengye.com/api/weixin/basic/getOpenId";/*** 獲取用戶openid和token*/public static final String AUTH_GET_ACCESS_TOKEN_AND_OPENID = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";/*** 根據token和openid獲取用戶信息*/public static final String AUTH_GET_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";/*** 刷新token*/public static final String AUTH_REFRESH_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";/*** 檢查token是否有效*/public static final String AUTH_CHECK_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID";/****/public static final String CONTENT_TYPE = "";}下面開始測試,代碼如下
@Slf4j @RunWith(SpringRunner.class) @SpringBootTest(classes = HelloWechatDepApplication.class) class HelloWechatDepApplicationTests {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate WeixinService weixinService;@Test // 獲取 `access_token`public void getAccessToken() throws Exception {//AccessTokenVO accessTokenVO = weixinService.getAccessToken(Constants.APP_ID, Constants.APP_SECRET);AccessTokenVO accessTokenVO = weixinService.getAccessToken(Constants.APP_ID, Constants.APP_SECRET);log.info("======================================== \n" + accessTokenVO.getAccess_token());}@Test // 獲取 創建菜單public void createMenu() throws Exception {String menu = "{\n" +" \"button\":[\n" +" {\t\n" +" \"type\":\"click\",\n" +" \"name\":\"今日歌曲\",\n" +" \"key\":\"V1001_TODAY_MUSIC\"\n" +" },\n" +" {\n" +" \"name\":\"菜單\",\n" +" \"sub_button\":[\n" +" {\t\n" +" \"type\":\"view\",\n" +" \"name\":\"搜索\",\n" +" \"url\":\"http://www.soso.com/\"\n" +" },\n" +" {\n" +" \"type\":\"miniprogram\",\n" +" \"name\":\"wxa\",\n" +" \"url\":\"http://mp.weixin.qq.com\"\n" +" },\n" +" {\n" +" \"type\":\"click\",\n" +" \"name\":\"贊一下我們\",\n" +" \"key\":\"V1001_GOOD\"\n" +" }]\n" +" }]\n" +" }";String jsonMenu = JSONUtil.toJsonStr(menu);WeixinResponseResult result = restTemplate.postForObject(Constants.CREATE_MENU_URL.replace("ACCESS_TOKEN", "上一步中獲取的token"), jsonMenu, WeixinResponseResult.class);System.out.println(result);} }5、微信模板消息
微信模板消息文檔:微信公眾平臺|模板消息 (qq.com)
所謂模版消息就是消息的一種,其中包含預留字段我們可以動態設置內容,主要用于業務通知,例如:我們微信中收到的這樣的消息
(1)在進行模板消息測試之前我們首先要定義一個模板,在測試號網頁中的消息模板中添加一個消息模板
模板說明:
{{first.DATA}} 申請人:{{keyword1.DATA}} 申請進度:{{keyword2.DATA}} 申請時間:{{keyword3.DATA}} 提交人:{{keyword4.DATA}}{{remark.DATA}}{{xxx.DATA}}中的內容就是預留字段,在接口中可以指定值,且必須以 {{first.DATA}} 開頭, {{remark.DATA}}結尾,中間也必須是xxx.DATA
(2)測試
首先例如創建菜單時的獲取token方法獲取access_token
然后在postman進行測試,如下圖
URL:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=xxx
請求體內容:
{"touser": "oC0So6FyAWhsDSzm80m2xrMzw98E", //用戶的openID"template_id": "sTLSPG7UgzYfhtWnGcFo5rE9QLZSuRU9FSN-xFYnKoM", //模板消息的模板id"url": "https://www.bilibili.com/", //點擊消息跳抓的url"data": {"first": {"value": "您有一條新消息", //渲染到{{first.DATA}} "color": "#173177"}, "keyword1": {"value": "林一", //{{keyword1.DATA}} "color": "#1731FF"}, "keyword2": {"value": "待審批", //{{keyword2.DATA}} "color": "#1731FF"}, "keyword3": {"value": "2038-13-01 12:12:12", //{{keyword3.DATA}} "color": "#1731FF"}, "keyword4": {"value": "秦安", //{{keyword4.DATA}} "color": "#1731FF"}, "remark": {"value": "希望您盡快審批", //{{remark.DATA}} "color": "#173177"}} }小結:看完創建菜單和消息模板,有的小伙伴可能有些困惑,創建菜單和消息模板和開始說的微信服務器認證沒有任何關系,是的僅就這兩點而言確實無關,但是如果你要實現用戶在公眾號發送一條消息,動態回復一條消息,這就需要了,所以忍不認證微信服服,主要是根據需求,這里只是演示使用。但是下面就會用到了。
6、公眾號內網頁授權
網頁授權文檔:微信開放文檔 |公眾號網頁授權,結合微信文檔看
公眾號網頁授權就是在公眾號內通過用戶點擊一個授權鏈接,用戶同意后從而可以獲取用戶的微信的信息,例如微信的信息就可以完成一些特定的業務。比如在微信公眾號菜單中進入某個網頁,在網頁里顯示微信用戶信息等等。
(1)首先我們需要一個填寫一個微信回調域名(在測試號可以填寫IP+端口),從而是的認證后微信可以重定向我們的填寫的回調地址
在測試號網頁中修改:
填寫:
用戶授權就是點擊如下的微信鏈接,鏈接含義說明如下
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect 解析: 1、scope 以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的用戶的openid的,并且是靜默授權并自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面) 以snsapi_userinfo為scope發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,并且由于用戶同意過,所以無須關注,就可在授權后獲取該用戶的基本信息。2、appid 公眾號的appid3、redirect_uri認證后微信回訪問的地址,這里我們可以寫接口地址,或者寫一個頁面地址都可以,因為用戶認證之后微信就會訪問它。例如填寫http://119.345.9.10:5500/sys/getopenid,認證成功后為微信就會調用http://119.345.9.10:5500/sys/getopenid?code=xxxxxx;填寫http://119.345.9.10:5500/show.html,認證成功后為微信就重定向到頁面http://119.345.9.10:5500/show.html?code=xxxxxx,攜帶者code參數注意:這里的回調地址必須是外網能夠訪問的,因為微信服務器要訪問這個地址,它必須能夠訪問到才行。所以這里就可以使用第一步微信授權時候做的內網穿透了,測試號頁面的回調地址就可以寫下面的域名。
(2)正式開始我們的認證過程
首先微信測試號頁面填寫回調地址域名
(3)在WeixinService接口新增方法
WeixinServiceImpl代碼
@Override public void getOpenId(HttpServletRequest request, HttpServletResponse response) {String code = request.getParameter("code");String state = request.getParameter("state");log.debug("======================================= \n code值:" + code);String responseContent = restTemplate.getForObject(Constants.AUTH_GET_ACCESS_TOKEN_AND_OPENID.replace("APPID", Constants.APP_ID).replace("SECRET", Constants.APP_SECRET).replace("CODE", code), String.class);JSONObject result = JSONUtil.parseObj(responseContent);String accessToken = result.getStr("access_token");String openid = result.getStr("openid");String refreshToken = result.getStr("refresh_token");log.debug("======================================= \n access_token值:" + accessToken + "\n openid值:" + openid);//這里要修改為你自己的域名String redirectUrl = "http://iwat.free.idcfengye.com/api/weixin/basic/getUserInfo?openid=" + openid + "&access_token=" + accessToken;try {// 授權之后重定向到指定URL(這里是跳轉到獲取用戶基本信息接口)response.sendRedirect(redirectUrl);} catch (IOException e) {e.printStackTrace();} }@Overridepublic WeixinUserInfoVO getUserInfo(String openId, String accessToken) {WeixinUserInfoVO weixinUserInfoVO = null;String responseContent = restTemplate.getForObject(Constants.AUTH_GET_USER_INFO.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId), String.class);weixinUserInfoVO = JSONUtil.toBean(responseContent, WeixinUserInfoVO.class);return weixinUserInfoVO;}新增WeixinController
WeixinController代碼如下
@Slf4j @RestController @RequestMapping("/api/weixin/basic") @Api(tags = "微信授權 - 接口") public class WeixinController extends BaseController {@Autowiredprivate WeixinService weixinService;@GetMapping(value = "/getOpenId"/*, produces = Constants.CONTENT_TYPE*/)@ApiOperation(value = "回調地址獲取code換取access_token和openid", httpMethod = "GET", response = ApiResult.class, notes = "回調地址獲取code換取access_token和openid")public ApiResult getOpenId(HttpServletRequest request, HttpServletResponse response) {weixinService.getOpenId(request, response);return ApiResult.ok("回調地址獲取code換取access_token和openid成功!");}@GetMapping(value = "/getUserInfo"/*, produces = Constants.CONTENT_TYPE*/)@ApiOperation(value = "獲取用戶基礎信息", httpMethod = "GET", response = ApiResult.class, notes = "獲取用戶基礎信息")public ApiResult getUserInfo(@RequestParam("openid") String openid, @RequestParam(value = "access_token", required = false) String accessToken) {WeixinUserInfoVO result = weixinService.getUserInfo(openid, accessToken);return ApiResult.ok("獲取用戶基礎信息成功!", result);} }(4)修改用戶菜單
在菜單測試那里,修改菜單內容如下。
{"button":[{ "type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC"},{"name":"菜單","sub_button":[{ "type":"view","name":"測試網頁授權","url":"https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的APPID&redirect_uri=iwat.free.idcfengye.com/api/weixin/basic/getOpenId&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect "},{"type":"miniprogram","name":"wxa","url":"http://mp.weixin.qq.com"},{"type":"click","name":"贊一下我們","key":"V1001_GOOD"}]}]}取關公眾號,重新關注,菜單就會刷新,測試即可。
源碼:hello-wechat-dev: 學習微信公眾號相關開發 (gitee.com)
如有不足歡迎指正!
總結
- 上一篇: 呼叫转移XCAP log的查看
- 下一篇: EF Code First学习笔记:数据