在配置以下的各URL,其中 通用开发参数设置 、 数据回调URL 、 指令回调URL 微信是要做URL校验的,所以配置的URL必须在公网可以访问到,微信以GET请求到该URL上,并携带了四个参数: msg_signature 、 timestamp 、 nonce 、 echostr 通过微信提供的加解密工具 JAVA库 来校验是否来自微信的合法请求。
| 参数 | 必须 | 说明 |
|---|---|---|
| msg_signature | 是 | 企业微信加密签名,msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体 |
| timestamp | 是 | 时间戳 |
| nonce | 是 | 随机数 |
| echostr | 是 | 加密的字符串。需要 解密得到消息内容明文 ,解密后有random、msg_len、msg、CorpID四个字段,其中msg即为消息内容明文 |
在第三方应用授权流程中,授权成功后会302跳转到该域名下的url(详见 从服务商网站发起授权应用 ),返回临时授权码。企业微信跳转URL时,会检查该URL是否在此处填下的域名之下,以杜绝伪造攻击。
设置可信域名后支持应用的OAuth2授权、JSSDK调用等,这个可信域名是网页应用中是可以设置的,如果第三方应用是关联的小程序,则不用可信域名设置。
该URL为服务商侧的管理后台链接,授权企业的管理员可从企业微信后台的应用详情页免登录直接跳转该链接(暂时不涉及到该URL,下文会讲到)
用于接收跟应用无关的系统消息(如注册完成)。(填写URL时需要正确响应企业微信验证URL的请求。请参考 接收消息 )
在创建好的 spring-boot 项目中创建一个 WxCpPortalController 作为通用的开发参数接收类:
WxCpPortalController.java import com.alibaba.fastjson.JSON;
import com.asiainfo.smart.weixin.cp.config.WxCpConfiguration;
import com.asiainfo.smart.weixin.cp.properties.WxCpProperties;
import com.asiainfo.smart.weixin.cp.util.WXBizMsgCrypt;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 企业微信-用于接收跟应用无关的系统消息 corpId-是服务商的corpid
*/
@Slf4j
@RestController
@RequestMapping("/cp/portal/{corpId}")
public class WxCpPortalController {
@Autowired
private WxCpProperties properties;
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@RequestParam(name = "msg_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 = [{}]",
signature, timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
try {
WXBizMsgCrypt crypt = new WXBizMsgCrypt(properties.getToken(), properties.getAesKey(), properties.getCorpId());
String echo = crypt.VerifyURL(signature, timestamp, nonce, echostr);
log.info("明文信息:{}", echo);
return echo;
} catch (Exception e) {
e.printStackTrace();
}
return "非法请求";
}
/**
* 此处根据自己的相关业务作相应的处理,以下是我做的业务处理,经供参考。
*/
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@PathVariable("corpId") String corpId,
@RequestBody String requestBody,
@RequestParam("msg_signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce) {
log.info("/n接收微信请求:[signature=[{}], timestamp=[{}], nonce=[{}], requestBody=[/n{}/n] ",
signature, timestamp, nonce, requestBody);
final WxCpService wxCpService = WxCpConfiguration.getCpServices(corpId);
WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(requestBody, wxCpService.getWxCpConfigStorage(),
timestamp, nonce, signature);
log.debug("/n消息解密后内容为:/n{} ", JSON.toJSONString(inMessage));
WxCpXmlOutMessage outMessage = this.route(corpId, inMessage);
if (outMessage == null) {
return "";
}
String out = outMessage.toEncryptedXml(wxCpService.getWxCpConfigStorage());
log.debug("/n组装回复信息:{}", out);
return out;
}
private WxCpXmlOutMessage route(String suiteId, WxCpXmlMessage message) {
try {
return WxCpConfiguration.getRouters(suiteId).route(message);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
}
token 、 aseKey 和下面URL用到的最好统一用一个,方便管理。
用于接收托管企业微信应用的 用户消息 和 用户事件 。URL支持使用 $CORPID$ 模板参数表示corpid,推送事件时企业微信会自动将其替换为授权企业的corpid。(关于如何回调,请参考 接收消息 。注意验证时 $CORPID$ 模板参数会替换为当前服务商的corpid,校验时也应该使用corpid初始化解密库)
再创建一个 WxCpDataController 用来接收数据回调URL。
WxCpDataController.java import com.asiainfo.smart.weixin.cp.config.WxCpConfiguration;
import com.asiainfo.smart.weixin.cp.properties.WxCpProperties;
import com.asiainfo.smart.weixin.cp.util.WXBizMsgCrypt;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 用于接收托管企业微信应用的用户消息和用户事件 corpId-此处的是企业的corpid,要和上面服务商的区分开
*
* @author dgb
* @create 2019-03-19 14:53
**/
@Slf4j
@RestController
@RequestMapping("/cp/data/service/{corpId}")
public class WxCpDataController {
@Autowired
private WxCpProperties properties;
@Autowired
private WxCpConfiguration configuration;
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@RequestParam(name = "msg_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 = [{}]",
signature, timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
try {
WXBizMsgCrypt crypt = new WXBizMsgCrypt(properties.getToken(), properties.getAesKey(), properties.getCorpId());
String echo = crypt.VerifyURL(signature, timestamp, nonce, echostr);
log.info("明文信息:{}", echo);
return echo;
} catch (Exception e) {
e.printStackTrace();
}
return "非法请求";
}
/**
* 此处根据自己的相关业务作相应的处理,以下是我做的业务处理,经供参考。
*/
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@PathVariable("corpId") String corpId,
@RequestBody String requestBody,
@RequestParam("msg_signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce) {
log.info("/n数据回调:[signature=[{}], timestamp=[{}], nonce=[{}], requestBody=[/n{}/n] ",
signature, timestamp, nonce, requestBody);
try {
final WxCpService wxCpService = WxCpConfiguration.getCpServices(corpId);
WxCpXmlMessage inMessage = null;
if (wxCpService == null) {
final WxCpService cpService = configuration.setCpServices(corpId);
inMessage = WxCpXmlMessage.fromEncryptedXml(requestBody, cpService.getWxCpConfigStorage(),
timestamp, nonce, signature);
}else {
inMessage = WxCpXmlMessage.fromEncryptedXml(requestBody, wxCpService.getWxCpConfigStorage(),
timestamp, nonce, signature);
}
this.route(corpId, inMessage);
return "success";
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private WxCpXmlOutMessage route(String corpId, WxCpXmlMessage message) {
return WxCpConfiguration.getRouters(corpId).route(message);
}
}
系统将会把此应用的 授权变更事件以及ticket参数等 推送给此URL,ticket说明详见API接口说明。
(填写URL时需要正确响应企业微信验证URL的请求。请参考 接收消息 )
再创建一个 WxCpDirectiveController 用来接收指令回调URL。
WxCpDirectiveController.java import com.asiainfo.smart.weixin.cp.config.WxCpConfiguration;
import com.asiainfo.smart.weixin.cp.properties.WxCpProperties;
import com.asiainfo.smart.weixin.cp.util.WXBizMsgCrypt;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 系统将会把此应用的授权变更事件以及ticket参数等推送给此URL,
* ticket说明详见API接口说明 suiteId-是第三方应用的id
*
* @author dgb
* @create 2019-03-19 15:00
**/
@Slf4j
@RestController
@RequestMapping("/cp/directive/service/{suiteId}")
public class WxCpDirectiveController {
@Autowired
private WxCpProperties properties;
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@RequestParam(name = "msg_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 = [{}]",
signature, timestamp, nonce, echostr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("请求参数非法,请核实!");
}
try {
WXBizMsgCrypt crypt = new WXBizMsgCrypt(properties.getToken(), properties.getAesKey(), properties.getCorpId());
String echo = crypt.VerifyURL(signature, timestamp, nonce, echostr);
log.info("明文信息:{}", echo);
return echo;
} catch (Exception e) {
e.printStackTrace();
}
return "非法请求";
}
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@PathVariable("suiteId") String suiteId,
@RequestBody String requestBody,
@RequestParam("msg_signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce) {
log.info("/n指令回调:[signature=[{}], timestamp=[{}], nonce=[{}], requestBody=[/n{}/n] ",
signature, timestamp, nonce, requestBody);
try {
final WxCpService wxCpService = WxCpConfiguration.getCpServices(suiteId);
WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(requestBody, wxCpService.getWxCpConfigStorage(),
timestamp, nonce, signature);
this.route(suiteId, inMessage);
return "success";
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private WxCpXmlOutMessage route(String suiteId, WxCpXmlMessage message) {
return WxCpConfiguration.getRouters(suiteId).route(message);
}
}
各URL配置到这里,顺利的话就配置完成了,接下来企业安装授权。