以微信为入口的微信企业号第三方应用,难免需要获取用户个人信息。企业应用中的URL链接(包括自定义菜单或者消息中的链接),可以通过OAuth2.0验证接口来获取成员的身份信息。

  通过此接口获取成员身份会有一定的时间开销。对于频繁获取成员身份的场景,建议采用如下方案:
1、企业应用中的URL链接直接填写企业自己的页面地址
2、成员跳转到企业页面时,企业校验是否有代表成员身份的cookie,此cookie由企业生成
3、如果没有获取到cookie,重定向到OAuth验证链接,获取成员身份后,由企业生成代表成员身份的cookie
4、根据cookie获取成员身份,进入相应的页面
注意,此URL的域名,必须完全匹配企业应用设置项中的'可信域名',否则获取成员信息时会返回50001错误码。

1,实现思路:


OAuth2验证可以使用多种方式,此处使用注解方式。设计思路是在需要获取用户信息的GET请求上添加注解,然后在调用的时候判断是否包含此注解,然后做处理流程。

每次请求包含2种情况:

1.不需要获取用户信息,直接跳转到指定视图;

2.需要获取用户信息,此处分2种情况:

a.session中存储了之前获取的用户信息,则直接跳转到指定视图;

b.session中不包含用户信息,则需要构造带回调参数的URL去微信API服务器获取code参数,然后通过code参数调用API换取Userid并保存到session,然后再次跳转到初始请求的视图页面。

2,代码


创建拦截器:OAuth2Interceptor





1. package org.oms.qiye.interceptor;  
2.   
3. import java.io.UnsupportedEncodingException;  
4. import java.lang.reflect.Method;  
5.   
6. import javax.servlet.http.HttpServletRequest;  
7. import javax.servlet.http.HttpServletResponse;  
8. import javax.servlet.http.HttpSession;  
9.   
10. import org.springframework.web.method.HandlerMethod;  
11. import org.springframework.web.servlet.HandlerInterceptor;  
12. import org.springframework.web.servlet.ModelAndView;  
13.   
14. public class OAuth2Interceptor implements HandlerInterceptor {  
15.   
16. /**
17.      * 在DispatcherServlet完全处理完请求后被调用
18.      * 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
19.      */  
20. @Override  
21. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  
22. "**执行顺序: 3、afterCompletion**");  
23.   
24.     }  
25.   
26. /**
27.      * 在业务处理器处理请求执行完成后,生成视图之前执行的动作
28.      */  
29. @Override  
30. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView modelAndView) throws Exception {  
31. "**执行顺序: 2、postHandle**");  
32.   
33.     }  
34.   
35. /**
36.      * 在业务处理器处理请求之前被调用 如果返回false 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
37.      * 如果返回true 执行下一个拦截器,直到所有的拦截器都执行完毕 再执行被拦截的Controller 然后进入拦截器链,
38.      * 从最后一个拦截器往回执行所有的postHandle() 接着再从最后一个拦截器往回执行所有的afterCompletion()
39.      */  
40. @Override  
41. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
42. "**执行顺序: 1、preHandle**");  
43.         String url = request.getRequestURL().toString();  
44.   
45.         HttpSession session = request.getSession();  
46. // 先判断是否有注解  
47.   
48.         HandlerMethod handlerMethod = (HandlerMethod) handler;  
49.         Method method = handlerMethod.getMethod();  
50. class);  
51. if (annotation != null) {  
52. "OAuthRequired:你的访问需要获取登录信息!");  
53. "UserId");  
54. if (objUid == null) {  
55.                 String resultUrl = request.getRequestURL().toString();  
56.                 String param=request.getQueryString();  
57. if(param!=null){  
58. "?" + param;  
59.                 }  
60. "resultUrl="+resultUrl);  
61. try {  
62. "utf-8");  
63. catch (UnsupportedEncodingException e) {  
64.                     e.printStackTrace();  
65.                 }  
66. //请求的路径  
67.                 String contextPath=request.getContextPath();  
68. "/oauth2.do?resultUrl=" + resultUrl);  
69. return false;  
70.             }  
71.   
72.         }  
73. return true;  
74.     }  
75.   
76. }


验证OAuth2注解OAuthRequired


1. package org.oms.qiye.interceptor;  
2.   
3. import java.lang.annotation.*;  
4. /**
5.  * 验证OAuth2注解
6.  * @author Sunlight
7.  *
8.  */  
9. @Retention(RetentionPolicy.RUNTIME)  
10. @Target(ElementType.METHOD)  
11. public @interface OAuthRequired {  
12.       
13. }


常量类,此处可以替换为持久化数据读取;



1. package org.oms.qiye.util;  
2.   
3. public class Constants {  
4. /**
5.      * 常量说明:
6.      * 此处定义的常量需要持久化,可以保存在数据库中,在需要的地方读取。
7.      * 在多企业号中,最好以每个应用来定义。
8.      */  
9. public static final int AGENTID = 1;  
10. public static final String TOKEN = "sunlight";  
11. public static final String CORPID = "你的企业号ID";  
12. public static final String Secret = "你的企业号_ACCESS_TOKEN";  
13. public static final String encodingAESKey = "s8vFF4f6AWay3uAdJh79WD6imaam4BV6Kl4eL4UzgfM";  
14. }


OAuth2 处理控制器OAuth2Controller


1. package org.oms.qiye.web;  
2.   
3. import java.io.UnsupportedEncodingException;  
4.   
5. import javax.servlet.http.HttpServletRequest;  
6. import javax.servlet.http.HttpSession;  
7.   
8. import org.oms.qiye.pojo.AccessToken;  
9. import org.oms.qiye.util.Constants;  
10. import org.oms.qiye.util.QiYeUtil;  
11. import org.oms.qiye.util.Result;  
12. import org.springframework.stereotype.Controller;  
13. import org.springframework.web.bind.annotation.RequestMapping;  
14. import org.springframework.web.bind.annotation.RequestParam;  
15. /**
16.  * OAuth2 处理控制器
17.  * @author Sunlight
18.  *
19.  */  
20. @Controller  
21. public class OAuth2Controller {  
22. /**
23.      * 构造参数并将请求重定向到微信API获取登录信息
24.      * 
25.      * @param index
26.      * @return
27.      */  
28. @RequestMapping(value = { "/oauth2.do", "/oauth2" })  
29. public String Oauth2API(HttpServletRequest request, @RequestParam String resultUrl) {  
30. // 此处可以添加获取持久化的数据,如企业号id等相关信息  
31.         String CropId = Constants.CORPID;  
32. "";  
33. if (resultUrl != null) {  
34.            //String reqUrl =request.getLocalAddr();  
35.             //TODO 此处把参数"reqUrl"拼接成你自己的URL
36.             String reqUrl = "hhxxmm.nat123.net/QiyeProject"
37.      
38. "http://" + reqUrl + "/oauth2url.do?oauth2url=" + resultUrl;  
39. "backUrl="+backUrl);  
40.             redirectUrl = oAuth2Url(CropId, backUrl);  
41.         }  
42. return "redirect:" + redirectUrl;  
43.     }  
44.   
45. /**
46.      * 根据code获取Userid后跳转到需要带用户信息的最终页面
47.      * 
48.      * @param request
49.      * @param code
50.      *            获取微信重定向到自己设置的URL中code参数
51.      * @param oauth2url
52.      *            跳转到最终页面的地址
53.      * @return
54.      */  
55. @RequestMapping(value = { "/oauth2url.do" })  
56. public String Oauth2MeUrl(HttpServletRequest request, @RequestParam String code, @RequestParam String oauth2url) {  
57.         AccessToken accessToken = QiYeUtil.getAccessToken(Constants.CORPID, Constants.Secret);  
58.         HttpSession session = request.getSession();  
59. if (accessToken != null && accessToken.getToken() != null) {  
60.             String Userid = getMemberGuidByCode(accessToken.getToken(), code, Constants.AGENTID);  
61. if (Userid != null) {  
62. "UserId", Userid);  
63.             }  
64.         }  
65. // 这里简单处理,存储到session中  
66. return "redirect:" + oauth2url;  
67.     }  
68.   
69. /**
70.      * 构造带员工身份信息的URL
71.      * 
72.      * @param corpid
73.      *            企业id
74.      * @param redirect_uri
75.      *            授权后重定向的回调链接地址,请使用urlencode对链接进行处理
76.      * @param state
77.      *            重定向后会带上state参数,企业可以填写a-zA-Z0-9的参数值
78.      * @return
79.      */  
80. private String oAuth2Url(String corpid, String redirect_uri) {  
81. try {  
82. "utf-8");  
83. catch (UnsupportedEncodingException e) {  
84.             e.printStackTrace();  
85.         }  
86. "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + corpid + "&redirect_uri=" + redirect_uri  
87. "&response_type=code&scope=snsapi_base&state=sunlight#wechat_redirect";  
88. "oauth2Url=" + oauth2Url);  
89. return oauth2Url;  
90.     }  
91.   
92. /**
93.      * 调用接口获取用户信息
94.      * 
95.      * @param token
96.      * @param code
97.      * @param agentId
98.      * @return
99.      * @throws SQLException
100.      * @throws RemoteException
101.      */  
102. public String getMemberGuidByCode(String token, String code, int agentId) {  
103. "code==" + code + "\ntoken=" + token + "\nagentid=" + agentId);  
104.         Result<String> result = QiYeUtil.oAuth2GetUserByCode(token, code, agentId);  
105. "result=" + result);  
106. if (result.getErrcode() == "0") {  
107. if (result.getObj() != null) {  
108. // 此处可以通过微信授权用code还钱的Userid查询自己本地服务器中的数据  
109. return result.getObj();  
110.             }  
111.         }  
112. return "";  
113.     }  
114.   
115. }

需要验证OAuth2控制器UserController



1. package org.oms.qiye.web;  
2.   
3. import javax.servlet.http.HttpServletRequest;  
4. import javax.servlet.http.HttpSession;  
5. import org.oms.qiye.interceptor.OAuthRequired;  
6. import org.springframework.stereotype.Controller;  
7. import org.springframework.ui.Model;  
8. import org.springframework.web.bind.annotation.RequestMapping;  
9. /**
10.  * 需要验证OAuth2控制器
11.  * @author Sunlight
12.  *
13.  */  
14. @Controller  
15. public class UserController {  
16. /**
17.      * 加载个人信息,此处添加了@OAuthRequired注解
18.      * @param model
19.      * @return
20.      */  
21. @RequestMapping(value={"/userInfo.do"})  
22. @OAuthRequired  
23. public String load(HttpServletRequest request,Model model){  
24. "Load a User!");  
25.         HttpSession session = request.getSession();  
26. "Userid", session.getAttribute("Userid"));  
27. return "user";  
28.     }  
29. }

<span style="font-size:18px;">/**
 * 需要验证OAuth2控制器
 * @author Sunlight
 *
 */
@Controller
public class UserController {
	/**
	 * 加载个人信息,此处添加了@OAuthRequired注解
	 * @param model
	 * @return
	 */
	@RequestMapping(value={"/userInfo.do"})
	@OAuthRequired
	public String load(HttpServletRequest request,Model model){
		System.out.println("Load a User!");
		HttpSession session = request.getSession();
		model.addAttribute("Userid", session.getAttribute("Userid"));
		return "user";
	}
}</span>




3,企业号后台配置:

  (1)给应用配置可信域名

java微信企业邮箱发验证码 企业微信outlook验证_拦截器


注:如果你的网络是局域网,映射外网工具可使用nat123
 (2)开启回调模式

java微信企业邮箱发验证码 企业微信outlook验证_拦截器_02

  (3)配置企业服务器:

java微信企业邮箱发验证码 企业微信outlook验证_java_03



 (4)自定义菜单:

按钮名称:个人信息链接 http://hhxxmm.nat123.net/QiyeProject/userInfo.do

java微信企业邮箱发验证码 企业微信outlook验证_System_04


配置好后,手机进入微信企业号,点击按钮“个人信息”,控制台打印出你的userID。

4,总结:

微信提供了OAuth2验证接口,我们可以灵活使用。