1.概述
企业微信集成方案,通过请求微信API的方式,获得当前微信访问用户的ID,然后通过用户ID实现对微信报表的权限管控。
2. 前期准备
a) 准备企业微信,需要给开发人员管理员权限,官网:https://work.weixin.qq.com/
b) 创建企业应用小程序,如下图:
c) 准备好企业微信的企业ID(Corpid),点击我的企业即可查看,如下图:
d) 准备好应用的AgentId和Secret (CorpSecret),如下图:
e) 企业微信API,官网:http://work.weixin.qq.com/api/doc#10012
3.开始开发
a) 发送请求到微信API,获得微信验证 用户授权CODE
https://open.weixin.qq.com/connect/oauth2/authorize?appid={CORP_ID}&redirect_uri= {REDIRECT_URI}&response_type=code&scope=SCOPE&agentid={AGENT_ID}&state=STATE#wechat_redirect
{CORP_ID} = 企业ID
{REDIRECT_URI} = 请求成功之后,回调的URL。
b) 发送请求到微信API,获得ACCESS_TOKEN(权限验证TOKEN)
https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={CORP_ID}&corpsecret={CORPSECRET}
{CORP_ID} = 企业ID
{CORPSECRET} = 应用的Secret
c) 发送请求到微信API,获得微信当前访问的USERID(用户名)
https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token={ACCESS_TOKEN}&code={CODE}&agentid={AGENTID}
{ACCESS_TOKEN} = 之前获得的ACCESS_TOKEN
{CODE} = 之前获得的 用户授权的CODE
{AGENTID} = 应用的AgentId
4. 代码剖析
微信API调用类
package action;
import com.opensymphony.xwork2.ActionSupport;
import dto.WeChatUrlConstant;
import net.sf.json.JSONObject;
import org.apache.struts2.ServletActionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import servlet.WeiXinTools;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
/**
* 微信API调用类
*/
public class WeChatReportAction extends ActionSupport{
// 输出日志打印
public static Logger log = LoggerFactory.getLogger(WeChatReportAction.class);
/**
* 获得微信授权Code
*/
public void getWeChatCode(){
//获得Servlet 请求响应对象
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
String getCodeUrl="";
try {
//拼接请求code链接的url
getCodeUrl = WeChatUrlConstant.GET_CODE.replace("{CORP_ID}",WeChatUrlConstant.CORP_ID ).replace("{REDIRECT_URI}",URLEncoder.encode("https://wechat.aimatech.com:7777/aima/queryUserInfo.action","UTF-8"));
//跳转 重定向到获取ceode请求
response.sendRedirect(getCodeUrl);
} catch (Exception e) {
log.error("Default boot user authorization failed!---{}", e);
}
}
/**
* 获得微信当前访问的用户信息
*/
public void queryUserInfo(){
//获得Servlet 请求响应对象
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
try {
//通过刚刚重定向的URL 在请求对象中 拿到 用户授权code
String code = request.getParameter("code");
//获取ACCESS_TOKEN
JSONObject accessToken_json = WeiXinTools.sendHttps(WeChatUrlConstant.GET_ACCESS_TOKEN.replace("{CORP_ID}",WeChatUrlConstant.CORP_ID).replace("{CORPSECRET}", WeChatUrlConstant.CORPSECRET), "GET", null);
String accesstoken = accessToken_json.getString("access_token");
//获取用户user_id接口
String url = WeChatUrlConstant.GET_USER_ID.replace("{ACCESS_TOKEN}",accesstoken).replace("{CODE}", code).replace("{AGENTID}", WeChatUrlConstant.AGENT_ID);
JSONObject josnObj = WeiXinTools.sendHttps(url, "GET", null);
if(josnObj.containsKey("errmsg")){ //请求调用后,调用失败返回的编码
log.error("code:==>"+code+"url==>"+url+"==== Failed to get UserId!");
}
String userId = (String)josnObj.get("UserId"); //获取用户id
//日志打印 用户ID
log.info("UserId:==>"+userId);
//跳转的URL
String redirectUrl ="https://wechat.aimatech.com:7777/aima/page/index.html?userId="+userId;
//跳转 重定向URL
response.sendRedirect(redirectUrl);
} catch (Exception e) {
log.error("Failed to get user information, specific error :"+e.getMessage());
}
}
}
工具类
package servlet;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.*;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* 工具类
* 主要功能:
* 1、recv():将微信公众平台发来的reuquest请求转换成一个具体的接收消息类
* 2、send():消息的发送
* 3、builderSendByRecv():根据接收消息类构建一个发送消息类
* @author 魏友元
*
*/
public final class WeiXinTools {
private static Logger log = LoggerFactory.getLogger(WeiXinTools.class);
private static double x_pi = 3.14499265358979324 * 3000.0 / 180.0;
/**
* 校验类
* @param token
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean access(String token,String signature,String timestamp,String nonce) {
List ss = new ArrayList();
ss.add(timestamp);
ss.add(nonce);
ss.add(token);
Collections.sort(ss);
StringBuilder builder = new StringBuilder();
for(String s : ss) {
builder.append(s);
}
return signature.equalsIgnoreCase(HashKit.sha1(builder.toString()));
}
/**
* 调用接口,在这里做数据处理
* @param url 请求的接口路径
* @param obj 需要发送的消息数据封装类
* @param methodType 请求方式(POST、GET)
* @return
*/
public static String sendTemp(String url,Object obj,String methodType) {
String result = "";
// 将消息对象转换成json字符串
String dataJson = JSONObject.fromObject(obj).toString();
// 调用对应的接口
JSONObject jsonObject = sendHttps(url, methodType, dataJson);
if (null != jsonObject) {
if (null != jsonObject.getString("errcode")&&!jsonObject.getString("errcode").equals("")) {
result = jsonObject.getString("errcode");
}
}
return result;
}
/**
* 发起https请求并获取结果
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static JSONObject sendHttps(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[0], tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String s, SSLSession sslsession) {
return true;//全部通过
}
});
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
httpUrlConn.setRequestProperty("Accept", "text/xml,text/javascript,text/html");
httpUrlConn.setRequestProperty("User-Agent", "top-sdk-java");
if ("GET".equalsIgnoreCase(requestMethod)){
httpUrlConn.connect();
}
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
if(buffer!=null&&!buffer.equals("")&&buffer.toString().indexOf("{")==0){
jsonObject = JSONObject.fromObject(buffer.toString());
}
} catch (ConnectException ce) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return jsonObject;
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url
* 发送请求的 URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
BufferedReader in = null;
OutputStreamWriter out =null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
conn.setRequestProperty("user-agent",
"Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Mobile/11D257 MicroMessenger/6.0.2 NetType/WIFI");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
// 发送请求参数
out.write(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
/**
* 向指定URL发送GET方法的请求
*
* @param url
* 发送请求的URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url;
if(param!=null && !param.equals("")){
if(url.indexOf("?")!=-1){
urlNameString = urlNameString + "&" + param;
}else{
urlNameString = urlNameString + "?" + param;
}
}
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
// connection.setRequestProperty("accept", "*/*");
// connection.setRequestProperty("connection", "Keep-Alive");
// connection.setRequestProperty("user-agent",
// "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Mobile/11D257 MicroMessenger/6.0.2 NetType/WIFI");
// 建立实际的连接
connection.connect();
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
log.error("发送GET请求出现异常!" + e);
}
//log.info("get请求获取的数据1:---->"+result);
return result;
}
/**
* 向指定URL发送GET方法的请求
*
* @param url
* 发送请求的URL
* @param param
* 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static InputStream getImg(String url, String param) {
InputStream in = null;
try {
String urlNameString = url;
if(param!=null && !param.equals("")){
if(url.indexOf("?")!=-1){
urlNameString = urlNameString + "&" + param;
}else{
urlNameString = urlNameString + "?" + param;
}
}
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
// 建立实际的连接
connection.connect();
in = connection.getInputStream();
} catch (Exception e) {
System.out.println("下载图片请求出现异常!" + e);
e.printStackTrace();
}
return in;
}
/**
* 文件存储
* @param fileName 文件名
* @param inputStream 文件输入流
* @return
*/
public static String uploadFile(String fileName,InputStream inputStream){
byte[] data = new byte[1024];
int len = 0;
FileOutputStream fileOutputStream = null;
String saveU ="";
try {
String tempPath="c:/wxScanUpload/"+new SimpleDateFormat("yyyyMMdd").format(new Date());
File file=new File(tempPath);
if(!file.exists() && !file.isDirectory()){
file.mkdirs();
}
String name=fileName+".jpg";
saveU=tempPath+"/"+name;
fileOutputStream = new FileOutputStream(saveU);
while ((len = inputStream.read(data)) != -1) {
fileOutputStream.write(data, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return saveU;
}
/**
* 表情转换
* @param emoji
* @return
*/
public static String getEmoji(int emoji){
return String.valueOf(Character.toChars(emoji));
}
/**
*中国正常坐标系GCJ02协议的坐标,转到 百度地图对应的 BD09 协议坐标
* @param lat 维度
* @param lng 经度
* 返回经度+纬度的字符串 例如 119.2321,31.12321
*/
public static String Convert_GCJ02_To_BD09(double lat,double lng){
double x = lng, y = lat;
double z =Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x)+ 0.000003 * Math.cos(x * x_pi);
lng = z * Math.cos(theta)+ 0.0065;
lat = z * Math.sin(theta) + 0.006;
return lng+","+lat;
}
/**
*中国正常坐标系GCJ02协议的坐标,转到 百度地图对应的 BD09 协议坐标
* @param lat 维度
* @param lng 经度
* 返回纬度+经度的字符串 例如 31.12321,119.2321
*/
public static String Convert_GCJ02_To_BD09_1(double lat,double lng){
double x = lng, y = lat;
double z =Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x)+ 0.000003 * Math.cos(x * x_pi);
lng = z * Math.cos(theta)+ 0.0065;
lat = z * Math.sin(theta) + 0.006;
return lat+","+lng;
}
/**
*百度地图对应的 BD09 协议坐标,转到 中国正常坐标系GCJ02协议的坐标
* @param lat 维度
* @param lng 经度
*/
public static String Convert_BD09_To_GCJ02(double lat, double lng){
double x = lng - 0.0065, y = lat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
lng = z * Math.cos(theta);
lat = z * Math.sin(theta);
return lat+","+lng;
}
}