公司有個微信公眾的項目需要開發,在經過不停的查看文檔與嘗試后,終於能上手了,順便找到個相當不錯的微信公眾開發依賴包,借此機會分享下經驗。
微信公眾號開發文檔:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
概念
在此之前需要清楚幾個概念:
公眾號包括訂閱號和服務號,所謂訂閱號就是在微信聊天頁面被收在訂閱號消息里面的,服務號是和聊天消息同級的。
訂閱號與服務號開發統稱為公眾號開發,但是訂閱號和服務號的權限並不相同。
這是訂閱號:
這是服務號:
測試公眾號
在開發前需要先有一個公眾號,幸好微信官方提供了測試公眾號。
點擊鏈接登錄測試公眾號 https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
測試公眾號擁有的權限幾乎涵蓋了訂閱號和服務號的所有權限,仔細查看下自己的公眾號的權限,不要在上線的時候發現權限不夠了。
可以在這個頁面獲取到appId和appSecret。
項目添加依賴
添加pom依賴
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.1.0</version>
</dependency>
application.yml添加配置
appId和appSecret填入測試公眾號的
token是自己在測試公眾號頁面設置的
aesKey是加密的key,明文模式可以不使用
# 微信配置
wechat:
appId: ******
appSecret: *******
token: Sakura
aesKey:
然后添加對應的Config文件
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat")
public class WxConfig {
private String appId;
private String appSecret;
private String token;
private String aesKey;
}
使用配置
添加WxConfiguration類
/**
* wechat mp configuration
*
* @author Binary Wang(https://github.com/binarywang)
*/
@AllArgsConstructor
@Configuration
@EnableConfigurationProperties(WxConfig.class)
public class WxMpConfiguration {
private final WxConfig wxConfig;
private final StringRedisTemplate redisTemplate;
@Bean
public WxRedisOps wxRedisOps() {
return new RedisTemplateWxRedisOps(redisTemplate);
}
@Bean
public WxMpConfigStorage wxMpConfigStorage(WxRedisOps wxRedisOps) {
WxMpRedisConfigImpl mpRedisConfig = new WxMpRedisConfigImpl(wxRedisOps, "wechat");
mpRedisConfig.setAppId(wxConfig.getAppId());
mpRedisConfig.setSecret(wxConfig.getAppSecret());
mpRedisConfig.setToken(wxConfig.getToken());
mpRedisConfig.setAesKey(wxConfig.getAesKey());
return mpRedisConfig;
}
@Bean
public WxMpService wxMpService(WxMpConfigStorage configStorage) {
WxMpService service = new WxMpServiceImpl();
service.setWxMpConfigStorage(configStorage);
return service;
}
// 消息路由 通過設置規則可以將消息交給指定的MessageHandler去處理,再經過Controller返回給微信服務器
@Bean
public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
// newRouter.rule().async(false).handler(this.globalMessageHandler).end();
// newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
// newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.subscribeHandler).end();
// newRouter.rule().async(false).msgType(TEXT).handler(this.textMessageHandler).end();
// newRouter.rule().async(false).msgType(EVENT).event(CLICK).handler(this.menuClickHandler).end();
return newRouter;
}
}
編寫微信回調方法
如果是需要開發回復用戶消息之類的需要微信服務器與開發服務器交互的內容,則需要編寫微信回調接口並在公眾號進行相應配置。
回調接口需要根據微信的接口文檔實現相應的業務邏輯。
需要注意的是回調接口要能夠公網訪問,如果是在開發中可以使用內網穿透工具。
添加回調Controller類
/**
* Description: 微信回調控制器
*
* @author ZhangJiaYu
* @date 2021/10/21
*/
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/wx/callback")
public class WeChatCallbackController {
private final WxMpService wxService;
private final WxMpMessageRouter messageRouter;
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@RequestParam(name = "signature", required = false) String signature,
@RequestParam(name = "timestamp", required = false) String timestamp,
@RequestParam(name = "nonce", required = false) String nonce,
@RequestParam(name = "echostr", required = false) String echostr) {
log.info("\n接收到來自微信服務器的認證消息:[{}, {}, {}, {}]", signature,
timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("請求參數非法,請核實!");
}
if (wxService.checkSignature(timestamp, nonce, signature)) {
return echostr;
}
return "非法請求";
}
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@RequestBody String requestBody,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("openid") String openid,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature", required = false) String msgSignature) {
log.info("\n接收微信請求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!wxService.checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法請求,可能屬於偽造的請求!");
}
String out = null;
if (encType == null) {
// 明文傳輸的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
// 將路由的消息進行返回
WxMpXmlOutMessage outMessage = this.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toXml();
} else if ("aes".equalsIgnoreCase(encType)) {
// aes加密的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(),
timestamp, nonce, msgSignature);
log.debug("\n消息解密后內容為:\n{} ", inMessage.toString());
WxMpXmlOutMessage outMessage = this.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
}
log.debug("\n組裝回復信息:{}", out);
return out;
}
private WxMpXmlOutMessage route(WxMpXmlMessage message) {
try {
return this.messageRouter.route(message);
} catch (Exception e) {
log.error("路由消息時出現異常!", e);
}
return null;
}
}
編寫完成后在公眾號進行設置即可
微信網頁授權
文檔地址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
微信網頁授權是個難點,但是捋清楚了,就很簡單。
在微信中訪問targetUrl(目標頁面)的時候想要獲取用戶的信息,需要先通過redirect接口(重定向接口)去構建一個重定向地址到微信授權的地址,授權完成后,微信會再次重定向到targetUrl並攜帶code參數,通過code參數就可以換取用戶信息。
redirect(構建授權地址並重定向)->微信授權地址(攜帶redirect_url=targetUrl的參數)->targetUrl(攜帶code參數)->通過code參數換取用戶信息
編寫Controller類
@RestController
@RequestMapping("/wxlogin")
public class WeChatLoginController {
private final WxMpService wxMpService;
// 公網地址
@Value("${wechat.domain}")
private String domain;
// 當前controller的path
private String path = "/wxlogin";
public WeChatLoginController(WxMpService wxMpService) {
this.wxMpService = wxMpService;
}
// 通過此接口構建微信授權地址並重定向
@SneakyThrows
@GetMapping("/redirect")
public void login(HttpServletResponse response) {
/**
* 重定向地址 能夠獲取到code參數 {@link #callback}
*/
String redirectUrl = domain + path + "/callback";
WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service();
String authorizationUrl = oAuth2Service.buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, null);
response.sendRedirect(authorizationUrl);
}
// 微信授權通過后會寫攜帶code重定向到此地址
@SneakyThrows
@GetMapping("/callback")
public void callback(String code, HttpServletResponse response) {
// 通過code換取用戶信息
WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service();
WxOAuth2AccessToken accessToken = oAuth2Service.getAccessToken(code);
WxOAuth2UserInfo userInfo = oAuth2Service.getUserInfo(accessToken, null);
// 可以攜帶參數重定向到前端頁面或者執行其他操作
}
}
weixin-java-mp的Demo和文檔:https://github.com/binarywang/weixin-java-mp-demo