公司要将移动端审批流程接入企业微信,员工经过企业微信的自建审批应用就能够在微信端审批单据,要审批单据就先得让企业微信跟用户绑定起来,实现无感自动登陆系统内。因为企业的员工已经被手动的从微信拉入企业微信内,这样就不能够经过企业微信的建立人员接口将人员推到微信内。只能在用户第一次登陆审批系统的时候去微信获取用户ID(userId)并与审批系统的人员关联起来。绑定好后就能够实现自动登陆。html
企业微信API:https://work.weixin.qq.com/api/doc#90001/90143/91201java
员工点击应用后自动登陆步骤以下:sql
1.拦截用户是否登陆数据库
在过滤器或者拦截器内检查用户是否登陆,若是没有登陆跳转到微信获取用户的身份信息apache
企业微信API地址:https://work.weixin.qq.com/api/doc#90001/90143/91120json
获取企业微信受权信息URL:c#
https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&agentid=AGENTID&state=STATE#wechat_redirec
页面跳转到企业微信后企业微信处理完成后,页面将从新跳转至 redirect_uri参数指定的回调地址并带上参数code=CODE&state=STATE,企业可根据code参数得到员工的userid。code长度最大为512字节。api
public static final String OAUTH_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=%s#wechat_redirect"; Integer serverPort = request.getServerPort(); String port = (null != serverPort && serverPort == 80) ? "" : ":" + serverPort.toString(); String url = "http://" + request.getServerName() // 服务器地址 + port // 端口号 + request.getContextPath(); // 项目名称 url = url + "/weixin/auth/weixinLogin/oauthLogin.html"; try{ url = URLEncoder.encode(url, "utf-8"); }catch (UnsupportedEncodingException e){ e.printStackTrace(); } String weixinUrl = String.format(OAUTH_CODE_URL, "企业ID", url, "state""); response.sendRedirect(weixinUrl);
2.获取用户信息并登陆缓存
微信回调接收到CODE值并用CODE去交换真实的用户信息,若是用户与系统绑定就表示登陆成功,登陆成功后将用户信息放入SESSION中。这样就能够实现自动登陆审批系统了。服务器
/** * 微信回调接口 * @return */ public String oauthLogin(){ try{ Map<String, Object> returnMap = WeixinUtil.getUserInfo(code, Constants.APPROVAL); if (null != returnMap){ //获得微信用户ID String userid = MapUtils.getString(returnMap, "UserId"); if (!StringUtil.isRealEmpty(userid)){ //用户与微信ID绑定实体 UserWx userWx = userWxManager.findByWXID(userid); if (userWx != null){ if (StringUtil.isRealEmpty(userWx.getAvatar())){ WxUser wxUser = WeixinUtil.getWxUser(userid, Constants.APPROVAL); if (null != wxUser && !StringUtil.isRealEmpty(wxUser.getAvatar())){ // +64取小头像 ;+0取大头像 userWx.setAvatar(wxUser.getAvatar()); userWxManager.save(userWx); } } //登陆成功,将用户信息放入SESSION等后续操做 ....... return null; }else{ //显示未绑定的逻辑 return customMethod("unbind"); } }else{ return customMethod("error"); } }else{ return customMethod("error"); } }catch (Exception e){ return customMethod("error"); } }
WeixinUtil的代码以下:
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.URL; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import org.apache.commons.collections.MapUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.alibaba.fastjson.JSON; import cn.boxbank.WxUser; public class WeixinUtil { private static Log log = LogFactory.getLog(WeixinUtil.class); /** * 发起https请求并获取结果 * * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(经过JSONObject.get(key)的方式获取json对象的属性值) */ public static Map<String, Object> HttpRequest(String requestUrl, String requestMethod, String outputStr) { // log.error("发起https请求并获取结果 :"+requestUrl+","+requestMethod+","+outputStr); Map<String, Object> map = new HashMap<String, Object>(); StringBuffer buffer = new StringBuffer(); try { // 建立SSLContext对象,并使用咱们指定的信任管理器初始化 TrustManager[] tm = { new EwX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, 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.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); httpUrlConn.setConnectTimeout(60 * 1000); httpUrlConn.setReadTimeout(60 * 1000); // 设置请求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); httpUrlConn.connect(); // if ("GET".equalsIgnoreCase(requestMethod)) // 当有数据须要提交时 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(); // 返回map // map = JsonUtil.json2Map(buffer.toString(), Map.class, String.class, Object.class); map = (Map<String, Object>) JSON.parseObject(buffer.toString(), Map.class); } catch (ConnectException ce) { System.out.println("Connection Timed Out......"); } catch (Exception e) { String result = String.format("Https Request Error:%s", e); System.out.println(result); } return map; } /** * 根据code调微信接口获取USERID * * @param code * @return */ public static Map<String, Object> getUserInfo(String code, String agentFlag) { String OAUTH_GETUSERINFO_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=%s&code=%s"; String url = String.format(OAUTH_GETUSERINFO_URL, getAccessToken(agentFlag), code); Map<String, Object> result = HttpRequest(url, "GET", null); return result; } /** * 从配置参数中获取 * @param agentFlag * @return */ public static String getAccessToken(String agentFlag) { //这里是获取应用的ACCESS_TOKEN,能够将获取ACCESS_TOKEN放到定时线程内,每2个小时去从新获取一次。 //从数据库或者缓存中拿出应用的ACCESS_TOKEN ...... } /** * 经过userid 从微信获取用户 * * @param userid * @return */ public static WxUser getWxUser(String userid, String agentFlag) { String GET_USER_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s"; WxUser user = null; String url = String.format(Constants.GET_USER_URL, getAccessToken(agentFlag), userid); Map<String, Object> returnMap = HttpRequest(url, "GET", null); int errcode = MapUtils.getIntValue(returnMap, "errcode"); if (errcode == 0) { user = JSON.toJavaObject((JSON) JSON.toJSON(returnMap), WxUser.class); } return user; } /** * 调用微信接口获取accessToken * * @param cropid 企业号ID * @param cropsecret 企业号管理组对应的secret * @return AccessToken */ public static void getAccessToken(String cropid, String cropsecret) { String ACCESS_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s"; // 添加URL中的cropid和cropsecret的值 String url = String.format(ACCESS_TOKEN_URL, cropid, cropsecret); // 根据url经过https请求获取accesstoken Map<String, Object> returnMap = WeixinUtil.HttpRequest(url, "GET", null); if (null != returnMap && returnMap.size() > 0) { // access_token String access_token = MapUtils.getString(returnMap, "access_token"); // accessToken有效时间expires_in:7200秒 Integer expires_in = MapUtils.getInteger(returnMap, "expires_in"); //可将取到的ACCESS_TOKEN存入数据库或者缓存中。每2个小时更新一次 ........ } else { log.info("accesstoken获取失败"); } } /** * 微信接口返回代码转换信息 * * @param errorcode * @return */ public static String convertErrorCode2Msg(int errorcode) { String errmsg = ""; switch (errorcode) { case -1: errmsg = "系统繁忙 "; break; case 0: errmsg = "请求成功 "; break; case 40001: errmsg = "获取access_token时Secret错误,或者access_token无效 "; break; case 40002: errmsg = "不合法的凭证类型"; break; case 40003: errmsg = "不合法的UserID"; break; case 40004: errmsg = "不合法的媒体文件类型 "; break; case 40005: errmsg = "不合法的文件类型 "; break; case 40006: errmsg = "不合法的文件大小"; break; case 40007: errmsg = "不合法的媒体文件id"; break; case 40008: errmsg = "不合法的消息类型 "; break; case 40013: errmsg = "不合法的corpid "; break; case 40014: errmsg = "不合法的access_token"; break; case 40015: errmsg = "不合法的菜单类型"; break; case 40016: errmsg = "不合法的按钮个数"; break; case 40017: errmsg = "不合法的按钮类型"; break; case 40018: errmsg = "不合法的按钮名字长度"; break; case 40019: errmsg = "不合法的按钮KEY长度"; break; case 40020: errmsg = "不合法的按钮URL长度 "; break; case 40021: errmsg = "不合法的菜单版本号 "; break; case 40022: errmsg = "不合法的子菜单级数"; break; case 40023: errmsg = "不合法的子菜单按钮个数"; break; case 40024: errmsg = "不合法的子菜单按钮类型"; break; case 40025: errmsg = "不合法的子菜单按钮名字长度"; break; case 40026: errmsg = "不合法的子菜单按钮KEY长度"; break; case 40027: errmsg = "不合法的子菜单按钮URL长度"; break; case 40028: errmsg = "不合法的自定义菜单使用员工"; break; case 40029: errmsg = "不合法的oauth_code"; break; case 40031: errmsg = "不合法的UserID列表"; break; case 40032: errmsg = "不合法的UserID列表长度"; break; case 40033: errmsg = "不合法的请求字符,不能包含\\uxxxx格式的字符 "; break; case 40035: errmsg = "不合法的参数 "; break; case 40038: errmsg = "不合法的请求格式 "; break; case 40039: errmsg = "不合法的URL长度"; break; case 40040: errmsg = "不合法的插件token"; break; case 40041: errmsg = "不合法的插件id"; break; case 40042: errmsg = "不合法的插件会话"; break; case 40048: errmsg = "url中包含不合法domain"; break; case 40054: errmsg = "不合法的子菜单url域名"; break; case 40055: errmsg = "不合法的按钮url域名 "; break; case 40056: errmsg = "不合法的agentid"; break; case 40057: errmsg = "不合法的callbackurl"; break; case 40058: errmsg = "不合法的红包参数 "; break; case 40059: errmsg = "不合法的上报地理位置标志位 "; break; case 40060: errmsg = "设置上报地理位置标志位时没有设置callbackurl"; break; case 40061: errmsg = "设置应用头像失败"; break; case 40062: errmsg = "不合法的应用模式"; break; case 40063: errmsg = "红包参数为空"; break; case 40064: errmsg = "管理组名字已存在"; break; case 40065: errmsg = "不合法的管理组名字长度"; break; case 40066: errmsg = "不合法的部门列表"; break; case 40067: errmsg = "标题长度不合法 "; break; case 40068: errmsg = "不合法的标签ID"; break; case 40069: errmsg = "不合法的标签ID列表"; break; case 40070: errmsg = "列表中全部标签(用户)ID都不合法 "; break; case 40071: errmsg = "不合法的标签名字,标签名字已经存在 "; break; case 40072: errmsg = "不合法的标签名字长度"; break; case 40073: errmsg = "不合法的openid"; break; case 40074: errmsg = "news消息不支持指定为高保密消息"; break; case 41001: errmsg = "缺乏access_token参数 "; break; case 41002: errmsg = "缺乏corpid参数"; break; case 41003: errmsg = "缺乏refresh_token参数"; break; case 41004: errmsg = "缺乏secret参数"; break; case 41005: errmsg = "缺乏多媒体文件数据"; break; case 41006: errmsg = "缺乏media_id参数"; break; case 41007: errmsg = "缺乏子菜单数据"; break; case 41008: errmsg = "缺乏oauth code"; break; case 41009: errmsg = "缺乏UserID"; break; case 41010: errmsg = "缺乏url"; break; case 41011: errmsg = "缺乏agentid"; break; case 41012: errmsg = "缺乏应用头像mediaid"; break; case 41013: errmsg = "缺乏应用名字"; break; case 41014: errmsg = "缺乏应用描述"; break; case 41015: errmsg = "缺乏Content"; break; case 41016: errmsg = "缺乏标题"; break; case 41017: errmsg = "缺乏标签ID"; break; case 41018: errmsg = "缺乏标签名字 "; break; case 42001: errmsg = "access_token超时 "; break; case 42002: errmsg = "refresh_token超时"; break; case 42003: errmsg = "oauth_code超时 "; break; case 42004: errmsg = "插件token超时"; break; case 43001: errmsg = "须要GET请求"; break; case 43002: errmsg = "须要POST请求"; break; case 43003: errmsg = "须要HTTPS"; break; case 43004: errmsg = "须要接收者关注"; break; case 43005: errmsg = "须要好友关系"; break; case 43006: errmsg = "须要订阅"; break; case 43007: errmsg = "须要受权"; break; case 43008: errmsg = "须要支付受权"; break; case 43009: errmsg = "须要员工已关注"; break; case 43010: errmsg = "须要处于回调模式"; break; case 43011: errmsg = "须要企业受权"; break; case 44001: errmsg = "多媒体文件为空"; break; case 44002: errmsg = "POST的数据包为空"; break; case 44003: errmsg = "图文消息内容为空"; break; case 44004: errmsg = "文本消息内容为空"; break; case 45001: errmsg = "多媒体文件大小超过限制"; break; case 45002: errmsg = "消息内容超过限制"; break; case 45003: errmsg = "标题字段超过限制"; break; case 45004: errmsg = "描述字段超过限制"; break; case 45005: errmsg = "连接字段超过限制"; break; case 45006: errmsg = "图片连接字段超过限制"; break; case 45007: errmsg = "语音播放时间超过限制"; break; case 45008: errmsg = "图文消息超过限制"; break; case 45009: errmsg = "接口调用超过限制"; break; case 45010: errmsg = "建立菜单个数超过限制"; break; case 45015: errmsg = "回复时间超过限制"; break; case 45016: errmsg = "系统分组,不容许修改"; break; case 45017: errmsg = "分组名字过长"; break; case 45018: errmsg = "分组数量超过上限"; break; case 45024: errmsg = "帐号数量超过上限"; break; case 46001: errmsg = "不存在媒体数据"; break; case 46002: errmsg = "不存在的菜单版本"; break; case 46003: errmsg = "不存在的菜单数据"; break; case 46004: errmsg = "不存在的员工"; break; case 47001: errmsg = "解析JSON/XML内容错误"; break; case 48002: errmsg = "Api禁用"; break; case 50001: errmsg = "redirect_uri未受权"; break; case 50002: errmsg = "员工不在权限范围"; break; case 50003: errmsg = "应用已停用"; break; case 50004: errmsg = "员工状态不正确(未关注状态) "; break; case 50005: errmsg = "企业已禁用"; break; case 60001: errmsg = "部门长度不符合限制"; break; case 60002: errmsg = "部门层级深度超过限制"; break; case 60003: errmsg = "部门不存在"; break; case 60004: errmsg = "父亲部门不存在"; break; case 60005: errmsg = "不容许删除有成员的部门"; break; case 60006: errmsg = "不容许删除有子部门的部门"; break; case 60007: errmsg = "不容许删除根部门"; break; case 60008: errmsg = "部门名称已存在"; break; case 60009: errmsg = "部门名称含有非法字符"; break; case 60010: errmsg = "部门存在循环关系"; break; case 60011: errmsg = "管理员权限不足,(user/department/agent)无权限"; break; case 60012: errmsg = "不容许删除默认应用"; break; case 60013: errmsg = "不容许关闭应用"; break; case 60014: errmsg = "不容许开启应用"; break; case 60015: errmsg = "不容许修改默认应用可见范围"; break; case 60016: errmsg = "不容许删除存在成员的标签"; break; case 60017: errmsg = "不容许设置企业"; break; case 60102: errmsg = "UserID已存在"; break; case 60103: errmsg = "手机号码不合法"; break; case 60104: errmsg = "手机号码已存在"; break; case 60105: errmsg = "邮箱不合法"; break; case 60106: errmsg = "邮箱已存在"; break; case 60107: errmsg = "微信号不合法"; break; case 60108: errmsg = "微信号已存在"; break; case 60109: errmsg = "QQ号已存在"; break; case 60110: errmsg = "部门个数超出限制"; break; case 60111: errmsg = "UserID不存在"; break; case 60112: errmsg = "成员姓名不合法"; break; case 60113: errmsg = "身份认证信息(微信号/手机/邮箱)不能同时为空 "; break; case 60114: errmsg = "性别不合法"; break; default: errmsg = "没有此错误码! "; break; } return errmsg; } }
WxUser类的结构以下,省get\set
public class WxUser { // 必填, 成员UserID。对应管理端的账号,企业内必须惟一。不区分大小写,长度为1~64个字节 private String userid; // 必填,成员名称。长度为1~64个字节 private String name; // 必填,成员所属部门id列表,不超过20个 private String[] department; private String deptId; // 职位信息。长度为0~64个字节 private String position; // 手机号码。企业内必须惟一,mobile/weixinid/email三者不能同时为空 private String mobile; // 性别。1表示男性,2表示女性 private String gender; // 邮箱。长度为0~64个字节。企业内必须惟一 private String email; // 微信号。企业内必须惟一。(注意:是微信号,不是微信的名字) private String weixinid; // 成员头像的mediaid,经过多媒体接口上传图片得到的mediaid private String avatar_mediaid; private String avatar; private String status; }