前提
- 拥有一个企业微信
-
按照上两篇文章,设定了可信域名
自建应用可信域名
阿里云完成域名解析
- 使用的是springMVC架构
注意事项;
- 其他架构可能需要根据架构自身,对本实现环节进行调整
- 本篇文章,意在指导用户建立完整的企业微信自建应用的调用流程
- 所有的细节部分,如果有报错,可能是由于大家对于springMVC的配置不同,单个细节部分完全可以百度实现。
实现步骤
微信资源请求流转
设定拦截器
在配置文件中,设定拦截器拦截内容,需要区分微信和平台端,配置方法可以参考下面的图片
配置的代码段:
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/www/**" />
<mvc:exclude-mapping path="/viewer/**" />
<mvc:exclude-mapping path="/css/**" />
<mvc:exclude-mapping path="/error/**" />
<mvc:exclude-mapping path="/index/**" />
<mvc:exclude-mapping path="/image/**" />
<mvc:exclude-mapping path="/initlib/**" />
<mvc:exclude-mapping path="/js/**" />
<mvc:exclude-mapping path="/weChat/**" />
<!-- 配置权限拦截器 如果部定义mappingUrl,则默认拦截所有对controller的请求 可以使用正则表达式对url进行匹配,从而更细粒度的进行拦截(.*/entryOrJsonController\.do\?action=reg.*) -->
<bean class="com.pms.system.interceptor.SecurityAndLogInterceptor">
<property name="mappingURL" value="edit|del|add|save|lock|assign|batchAssign"/>
</bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- OAuth2拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 对所有的请求拦截使用/** ,对某个模块下的请求拦截使用:/myPath/*-->
<mvc:mapping path="/weChat/**" />
<ref bean="oauth2Interceptor" />
</mvc:interceptor>
</mvc:interceptors>
<bean id="oauth2Interceptor" class="com.hhisp.wechat.interceptor.OAuth2Interceptor">
</bean>
编写拦截器代码
拦截器主要是用于在企业应用内部中,对资源请求进行拦截,防止未登录直接跳转到某一资源下。
在本示例中,微信的拦截器oauth2Interceptor主要完成微信的token获取,code获取等功能,并且实现页面的跳转。此处相当于一个归口作用,将所有关于微信的请求,都汇总到一个方法中处理登录认证等通用流程,完成认证流程后,再跳转到具体的业务页面进行业务办理。
package com.hhisp.wechat.interceptor;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
public class OAuth2Interceptor implements HandlerInterceptor {
/**
* 在DispatcherServlet完全处理完请求后被调用
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
/**
* 在业务处理器处理请求执行完成后,生成视图之前执行的动作
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView modelAndView) throws Exception {
}
/**
* 在业务处理器处理请求之前被调用 如果返回false 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
* 如果返回true 执行下一个拦截器,直到所有的拦截器都执行完毕 再执行被拦截的Controller 然后进入拦截器链,
* 从最后一个拦截器往回执行所有的postHandle() 接着再从最后一个拦截器往回执行所有的afterCompletion()
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//String url = request.getRequestURL().toString();
HttpSession session = request.getSession();
// 先判断是否有注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
OAuthRequired annotation = method.getAnnotation(OAuthRequired.class);
if (annotation != null) {
System.out.println("session:"+session.toString());
Object objUid = session.getAttribute("weChatId");
if (objUid == null) {
String resultUrl = request.getRequestURL().toString();
String param=request.getQueryString();
if(param!=null){
resultUrl+= "?" + param;
}
try {
resultUrl = java.net.URLEncoder.encode(resultUrl, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//请求的路径
String contextPath=request.getContextPath();
System.out.println("contextPath !"+contextPath);
System.out.println("resultUrl !"+resultUrl);
System.out.println("all!"+contextPath + "/weChat/oauth2/oauth2.do?resultUrl=" + resultUrl);
response.sendRedirect(contextPath + "/weChat/oauth2/oauth2.do?resultUrl=" + resultUrl);
return false;
}
}
return true;
}
}
代码详解:
整篇代码中,要实现的功能就是最后一句话,相当于将所有wechat请求的信息,都重定向到微信认证接口,进行微信认证。
添加微信认证的相关类和函数
- 刚才我们在拦截器中,重定向了一个url指向后台的oauth2.do方法
-
在该方法中,我们需要向微信官方的链接,发送获取用户微信Code的请求,并且将获取code后重定向的url提交给微信官方链接。
具体代码如下:
import com.hhisp.wechat.pojo.AccessToken;
import com.hhisp.wechat.utils.Constants;
import com.hhisp.wechat.utils.QiYeUtil;
import com.hhisp.wechat.utils.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
@Controller
@RequestMapping("/weChat/oauth2")
public class OAuth2Controller {
/**
* 构造参数并将请求重定向到微信API获取登录信息
*
* @param resultUrl
* @return
*/
@RequestMapping(value = { "/oauth2.do", "/oauth2" })
public String Oauth2API(HttpServletRequest request, @RequestParam String resultUrl) {
// 此处可以添加获取持久化的数据,如企业号id等相关信息
String CropId = 自有企业微信ID;
String redirectUrl = "";
if (resultUrl != null) {
String backUrl = Constants.BEFORE_URL + "/weChat/oauth2/oauth2url.do?oauth2url=" + resultUrl;
redirectUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + corpid + "&redirect_uri=" + redirect_uri
+ "&response_type=code&scope=snsapi_base&state=sunlight#wechat_redirect";;
}
return "redirect:" + redirectUrl;
}
}
获取微信账号
上面,我们向微信官方提交了一个请求code的链接,并且告诉微信官方,我们获取了code后,需要定向的目的url是什么
接下来,在这个url的处理函数中,需要获取微信官方返回的code,并且根据code,处理相关的业务逻辑,从而跳转到具体的业务页面
/**
* 根据code获取Userid后跳转到需要带用户信息的最终页面
*
* @param request
* @param code
* 获取微信重定向到自己设置的URL中code参数
* @param oauth2url
* 跳转到最终页面的地址
* @return
*/
@RequestMapping(value = { "/oauth2url.do" })
public String Oauth2MeUrl(HttpServletRequest request, @RequestParam String code, @RequestParam String oauth2url,String activityId) {
AccessToken accessToken = QiYeUtil.getAccessToken(Constants.CORPID,Constants.SECRET);
HttpSession session = request.getSession();
if (accessToken != null && accessToken.getToken() != null) {
String weChatId = getMemberGuidByCode(accessToken.getToken(), code, Constants.AGENTID);
if (weChatId != null) {
session.setAttribute("weChatId", weChatId);
System.out.println("weChatId"+weChatId);
System.out.println("session after set wid"+session.toString());
}
}
if(StringUtils.isNotEmpty(activityId)){
return "redirect:" + oauth2url+ "&activityId=" + activityId;
}
// 这里简单处理,存储到session中
return "redirect:" + oauth2url;
}
/**
* 调用接口获取用户信息
*
* @param token
* @param code
* @param agentId
* @return
*/
public String getMemberGuidByCode(String token, String code, int agentId) {
Result<String> result = QiYeUtil.oAuth2GetUserByCode(token, code, agentId);
if (result.getErrcode() == "0") {
if (result.getObj() != null) {
// 此处可以通过微信授权用code获取的Userid查询自己本地服务器中的数据
return result.getObj();
}
}
return "";
}
获取code后的步骤解析:
- 带code跳转中,code在request参数中可以获取,但是这个code不是用户编号,是分配的一个临时编号;
- 在方法中,getMemberGuidByCode函数,主要就是通过token以及code,获取实际的用户信息的过程。
- 关于具体的获取token,以及通过code获取userId的实现方式,网上一搜一大把,临时代码封装的不太好,我就不嫌丑了。
- 截止到此,代码端的工作就完成了。
企业微信的后台配置以及实现效果
- 企业微信添加自建应用
- 自建应用中增加自定义菜单
- 自定义菜单中配置按钮的url
- 在企业微信中,点击自建应用下的按钮
- 查看结果