一、构建spring boot项目
1、新建项目
新建一个模块(module):enterprise-wechat
新建一个子模块(module):wechat
目录结构如下:
结构描述:
common
-> WeChatConstants:存放企业微信一些常量,公用参数
-> WeChatUtils:存放企业微信第三方应用api
controller
-> SystemController:控制层,接收请求
entity
-> aes:目录下文件企业微信加解密包
service
-> IConfigService:调用企业微信服务层
pom.xml
-> 导入所需要的jar包
pom.xml中需要导入commons.codec包
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
2、方法描述
1)doGetCallback:
① 接收验证请求,用于验证通用开发参数系统事件接收URL、数据回调URL、指令回调URL。
② 企业微信后台录入回调URL点击保存时,微信服务器会立即发送一条GET请求到对应URL,该函数就对URL的signature进行验证。
2)doPostCallback:
① 用于获取 suite_ticket,安装应用时企业微信传递过来的auth_code:指令回调URL。
② 当刷新ticket传递【SuitID】:指令回调URL。
③ 当打开应用时传递【CorpID】:数据回调URL。
3、代码编写
1)企业微信配置类:WeChatConstants
package com.wechat.common;
/**
* 企业微信
*/
public class WeChatConstants {
// 企业微信授权码获取时间
public static final Long EXPIRES_IN = 24 * 60 * 60 * 1000L;
//24 * 60 * 60 * 1000L 7200L * 1000
/**
* 服务商CorpID
*/
public static final String CORP_ID = \"ww14438c6c07a317f2\";
/**
* 服务商身份的调用凭证
*/
public static final String PROVIDER_SECRET = \"RH7PehRJX3LIcw4axad_H2T9HSUG1finOBEpnLTVIioBrP-zgZrGsqJ9pHVw5vVj\";
/**
* 应用的唯一身份标识
*/
public static final String SUITE_ID = \"ww4f66fa544a32f920\";
/**
* 应用的调用身份密钥
*/
public static final String SUITE_SECRET = \"vVv8JzaBlEVCTQkHKqmr57EAMs65AILWiI_4ANc25T4\";
// 回调相关
/**
* 回调/通用开发参数Token, 两者解密算法一样,所以为方便设为一样
*/
public static final String TOKENS = \"E0sOXx4LqeE5BmDvMTAz3x\";
/**
* 回调/通用开发参数EncodingAESKey, 两者解密算法一样,所以为方便设为一样
*/
public static final String ENCODING_AES_KEY = \"IESLPSyW4vyBB90jkzfwfYRtcMky6LIOevr4SVefz7I\";
}
2)企业微信api:WeChatUtils
package com.wechat.common;
/**
* 企业微信工具类
*/
public class WeChatUtils {
/**
* 第三方应用api start
*/
// 获取第三方应用凭证
public final static String THIRD_BUS_WECHAT_SUITE_TOKEN = \"https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token\";
// 获取企业永久授权码
public final static String THIRD_BUS_WECHAT_ACCESS_TOKEN = \"https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=SUITE_ACCESS_TOKEN\";
// 第三方 构造扫码登录链接
public final static String THIRD_BUS_WECHAT_LOGIN = \"https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?appid=CORPID&redirect_uri=REDIRECT_URI&state=web_login&usertype=member\";
// 第三方 获取登录用户信息 POST
public final static String THIRD_BUS_WECHAT_GET_LOGIN_INFO = \"https://qyapi.weixin.qq.com/cgi-bin/service/get_login_info?access_token=PROVIDER_ACCESS_TOKEN\";
// 第三方 构造网页授权链接
public final static String THIRD_BUS_WECHAT_AUTHORIZE_URL = \"https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_privateinfo&state=STATE#wechat_redirect\";
// 第三方 获取访问用户身份 GET
public final static String THIRD_BUS_WECHAT_GET_USER_INFO = \"https://qyapi.weixin.qq.com/cgi-bin/service/getuserinfo3rd?suite_access_token=SUITE_TOKEN&code=CODE\";
// 第三方 获取访问用户敏感信息 post
public final static String THIRD_BUS_WECHAT_GET_USER_DETAIL3RD = \"https://qyapi.weixin.qq.com/cgi-bin/service/getuserdetail3rd?suite_access_token=SUITE_ACCESS_TOKEN\";
// 第三方 获取部门列表
public final static String THIRD_BUS_WECHAT_DEPART_LIST = \"https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=ACCESS_TOKEN&id=ID\";
// 第三方 获取部门成员
public final static String THIRD_BUS_WECHAT_DEPART_USER = \"https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID&fetch_child=FETCH_CHILD\";
// 第三方 获取部门成员详情
public final static String THIRD_BUS_WECHAT_DEPART_USER_DETAIL = \"https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID&fetch_child=FETCH_CHILD\";
// 第三方 读取成员 GET
public final static String THIRD_BUS_WECHAT_GET_USER = \"https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID\";
// 服务商的token
public final static String THIRD_BUS_WECHAT_GET_PROVIDER_TOKEN = \"https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token\";
// 获取企业凭证
public final static String THIRD_BUS_WECHAT_GET_CORP_TOKEN = \"https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=SUITE_ACCESS_TOKEN\";
// 发送应用消息
public final static String THIRD_BUS_WECHAT_SEND = \"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN\";
// 获取应用的jsapi_ticket
public final static String THIRD_BUS_GET_JSAPI_TICKET = \"https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=agent_config\";
// 获取企业的jsapi_ticket
public final static String THIRD_BUS_GET_JSAPI_TICKET_BUS = \"https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN\";
/**
* 第三方应用api end
*/
}
3)controller层:SystemController
package com.wechat.controller;
import com.wechat.service.IConfigService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
/**
* 控制层
*/
@Slf4j
@RestController
@RequestMapping(value = \"system\")
public class SystemController {
@Autowired
private IConfigService configService;
/**
* 验证通用开发参数及应用回调
* @param: request
* @param: response
* @returns: void
*/
@ApiOperation(value = \"验证通用开发参数及应用回调\")
@GetMapping(value = \"getEchostr\")
public void doGetCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 微信加密签名
String msgSignature = request.getParameter(\"msg_signature\");
// 时间戳
String timestamp = request.getParameter(\"timestamp\");
// 随机数
String nonce = request.getParameter(\"nonce\");
// 随机字符串
// 如果是刷新,需返回原echostr
String echoStr = request.getParameter(\"echostr\");
String sEchoStr= \"\";
PrintWriter out;
log.debug(\"msgSignature: \" + msgSignature+\"timestamp=\"+timestamp+\"nonce=\"+nonce+\"echoStr=\"+echoStr);
try {
sEchoStr = configService.doGetCallback(msgSignature,timestamp,nonce,echoStr); //需要返回的明文;
log.debug(\"doGetCallback-> echostr: \" + sEchoStr);
// 验证URL成功,将sEchoStr返回
out = response.getWriter();
out.print(sEchoStr);
} catch (Exception e) {
//验证URL失败,错误原因请查看异常
e.printStackTrace();
}
}
/**
* 刷新ticket,AuthCode
*/
@ApiOperation(value = \"刷新ticket,AuthCode\")
@PostMapping(value = \"getEchostr\")
public String doPostCallback(HttpServletRequest request) throws Exception {
// 微信加密签名
String msgSignature = request.getParameter(\"msg_signature\");
// 时间戳
String timestamp = request.getParameter(\"timestamp\");
// 随机数
String nonce = request.getParameter(\"nonce\");
// 类型
String type = request.getParameter(\"type\");
// 企业id
String corpId = request.getParameter(\"corpid\");
ServletInputStream in = request.getInputStream();
// 刷新ticket,AuthCode
String success = configService.doPostCallback(msgSignature, timestamp, nonce, type, corpId, in);
return success;
}
}
4)Service层:IConfigService
package com.wechat.service;
import javax.servlet.ServletInputStream;
/**
* 企业微信第三方服务service
*/
public interface IConfigService {
/**
* 验证通用开发参数及应用回调
* @returns: java.lang.String
*/
String doGetCallback(String msgSignature, String timestamp, String nonce, String echoStr);
/**
* 获取SuiteTicket,AuthCode
*/
String doPostCallback(String msgSignature, String timestamp, String nonce, String type, String corpId, ServletInputStream in);
}
5)service实现类:ConfigServiceImpl
package com.wechat.service.impl;
import com.alibaba.druid.support.json.JSONUtils;
import com.wechat.common.StringUtils;
import com.wechat.common.WeChatConstants;
import com.wechat.common.WxUtil;
import com.wechat.entity.aes.AesException;
import com.wechat.entity.aes.WXBizMsgCrypt;
import com.wechat.service.IConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.servlet.ServletInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;
/**
* TODO 类描述
*/
@Slf4j
@Service
public class ConfigServiceImpl implements IConfigService {
/**
* 验证通用开发参数及应用回调
* @returns: java.lang.String
*/
@Override
public String doGetCallback(String msgSignature, String timestamp, String nonce, String echoStr) {
//需要返回的明文
String sEchoStr=\"\";
try {
log.debug(WeChatConstants.TOKENS, WeChatConstants.ENCODING_AES_KEY, WeChatConstants.CORP_ID);
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(WeChatConstants.TOKENS, WeChatConstants.ENCODING_AES_KEY, WeChatConstants.CORP_ID);
sEchoStr = wxcpt.VerifyURL(msgSignature, timestamp, nonce, echoStr);
} catch (AesException e) {
e.printStackTrace();
}
return sEchoStr;
}
/**
* 获取SuiteTicket,AuthCode
* @param: msgSignature 微信加密签名
* @param: timestamp 时间戳
* @param: nonce 随机数
* @param: type 类型
* @param: corpId 企业id
* @param: in
* @returns: java.lang.String
*/
@Override
public String doPostCallback(String msgSignature, String timestamp, String nonce, String type, String corpId, ServletInputStream in) {
String id = \"\";
// 访问应用和企业回调传不同的ID
if(!StringUtils.isNull(type) && type.equals(\"data\")){
id = corpId;
log.debug(\"======corpId===\"+id);
} else {
id = WeChatConstants.SUITE_ID;
log.debug(\"======SuiteId===\" + id);
}
try {
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(WeChatConstants.TOKENS, WeChatConstants.ENCODING_AES_KEY, id);
String postData=\"\"; // 密文,对应POST请求的数据
//1.获取加密的请求消息:使用输入流获得加密请求消息postData
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String tempStr = \"\"; //作为输出字符串的临时串,用于判断是否读取完毕
while(null != (tempStr=reader.readLine())){
postData+=tempStr;
}
log.debug(\"====msg_signature====\"+msgSignature+\"====timestamp===\"+timestamp+\"====nonce===\"+nonce+\"====postData===\"+postData);
String suiteXml = wxcpt.DecryptMsg(msgSignature, timestamp, nonce, postData);
log.debug(\"suiteXml: \" + suiteXml);
Map suiteMap = WxUtil.parseXml(suiteXml);
log.debug(\"==suiteMap==\"+ JSONUtils.toJSONString(suiteMap));
if(suiteMap.get(\"SuiteTicket\") != null) {
String suiteTicket = (String) suiteMap.get(\"SuiteTicket\");
log.debug(\"====SuiteTicket=====\" + suiteTicket);
} else if(suiteMap.get(\"AuthCode\") != null){
String authCode = (String) suiteMap.get(\"AuthCode\");
log.debug(\"doPostValid->AuthCode:\" + authCode);
}
} catch (Exception e) {
e.printStackTrace();
}
return \"success\";
}
}
4、验证
以上代码编写完成后,就可以打包到环境上面进行测试验证:
①:echostr验证
返回结果:返回 echostr,并显示已验证
16:11:46.940 [http-nio-9205-exec-7] INFO c.q.w.s.c.SystemController - [doGetValid,94] - doGetCallback->echostr: 577115934236344259
16:11:46.969 [http-nio-9205-exec-3] INFO c.q.w.s.c.SystemController - [doGetValid,94] - doGetCallback->echostr: 5267604771365158379
②:刷新Ticket:获取Ticket有两种方式,一是点击按钮获取,二是企业微信每15分钟会调用回调接口获取一次
点击“刷新Ticket” 会弹出如下图,然后点击确定
Ticket 有效期为30分钟;建议把Ticket放到数据库或者redis中
③:获取auth_code
安装第三方应用的时候,会获取auth_code
④:安装测试流程
通过企业微信扫码进行安装
上面就是验证通过,及获取Ticket和auth_code
5、总结
在第三方应用开发中,主要围绕三种类型的access_token(见企业微信地方应用(二)https://www.cnblogs.com/why0703/p/15983925.html)
provider_access_token:服务商的token
suite_access_token:获取第三方应用凭证
access_token:授权方(企业)access_token
通过上面的代码及配置,我们获取到了suiteTicket和auth_code。
接下来我们要通过这些值获取到上面token,通过springboot开发实现“企业微信第三方应用(二)api使用测试”
代码后面同步到github
来源:https://www.cnblogs.com/why0703/p/15983995.html
本站部分图文来源于网络,如有侵权请联系删除。