企业微信服务商开发(三)

企业微信服务商开发(二)中我们已经将开发的环境搭建起来了,接下来,

配置各URL

在配置以下的各URL,其中通用开发参数设置数据回调URL指令回调URL微信是要做URL校验的,所以配置的URL必须在公网可以访问到,微信以GET请求到该URL上,并携带了四个参数:msg_signaturetimestampnonceechostr通过微信提供的加解密工具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时需要正确响应企业微信验证URL的请求。请参考接收消息

在创建好的spring-boot项目中创建一个WxCpPortalController作为通用的开发参数接收类:

  • WxCpPortalController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import com.alibaba.fastjson.JSON;
import com.itwork.weixin.cp.config.WxCpConfiguration;
import com.itwork.weixin.cp.properties.WxCpProperties;
import com.itwork.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;
}
}
注:此处用到的tokenaseKey和下面URL用到的最好统一用一个,方便管理。

数据回调URL

用于接收托管企业微信应用的用户消息用户事件。URL支持使用$CORPID$模板参数表示corpid,推送事件时企业微信会自动将其替换为授权企业的corpid。(关于如何回调,请参考接收消息。注意验证时$CORPID$模板参数会替换为当前服务商的corpid,校验时也应该使用corpid初始化解密库)

再创建一个WxCpDataController用来接收数据回调URL。

  • WxCpDataController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import com.itwork.weixin.cp.config.WxCpConfiguration;
import com.itwork.weixin.cp.properties.WxCpProperties;
import com.itwork.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);
}
}

指令回调URL

系统将会把此应用的授权变更事件以及ticket参数等推送给此URL,ticket说明详见API接口说明。
(填写URL时需要正确响应企业微信验证URL的请求。请参考接收消息

再创建一个WxCpDirectiveController用来接收指令回调URL。

  • WxCpDirectiveController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import com.itwork.weixin.cp.config.WxCpConfiguration;
import com.itwork.weixin.cp.properties.WxCpProperties;
import com.itwork.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配置到这里,顺利的话就配置完成了,接下来企业安装授权

获取示例源码

请关注微信公众号:「特想学英语」并回复:企业微信

原文作者: dgb8901,yinxing

原文链接: https://www.itwork.club/2019/03/27/corp-develop-3/

版权声明: 转载请注明出处

为您推荐

体验小程序「简易记账」

关注公众号「特想学英语」

企业微信服务商开发(四)