6.3.4 案例:通过网页授权获取用户信息

为了让读者进一步理解并掌握网页授权流程及其实现,下面来看一个演示案例。

1.编写授权回调请求处理程序

如果要在网页中得到用户信息,就必须先引导用户进入网页授权页面;用户同意授权后会跳转到回调地址redirect_uri,redirect_uri是授权回调请求处理程序的访问地址;在处理程序中,开发者能够获取到code,再通过code获取access_token,最终得到用户信息。

授权回调请求处理程序的实现如下:

  1. 1 package org.liufeng.course.servlet;
  2. 2 import java.io.IOException;
  3. 3 import javax.servlet.ServletException;
  4. 4 import javax.servlet.http.HttpServlet;
  5. 5 import javax.servlet.http.HttpServletRequest;
  6. 6 import javax.servlet.http.HttpServletResponse;
  7. 7 import org.liufeng.course.pojo.SNSUserInfo;
  8. 8 import org.liufeng.course.pojo.WeixinOauth2Token;
  9. 9 import org.liufeng.course.util.AdvancedUtil;
  10. 10 
  11. 11 /**
  12. 12  * 授权后的回调请求处理
  13. 13  *
  14. 14  * @author liufeng
  15. 15  * @date 2013-11-12
  16. 16  */
  17. 17 public class OAuthServlet extends HttpServlet {
  18. 18  private static final long serialVersionUID = -1847238807216447030L;
  19. 19 
  20. 20  public void doGet(HttpServletRequest request, HttpServletResponse response)
  21. 21  throws ServletException, IOException {
  22. 22  request.setCharacterEncoding("gb2312");
  23. 23  response.setCharacterEncoding("gb2312");
  24. 24 
  25. 25  // 用户同意授权后,能获取到code
  26. 26  String code = request.getParameter("code");
  27. 27 
  28. 28  // 用户同意授权
  29. 29  if (!"authdeny".equals(code)) {
  30. 30  // 获取网页授权access_token
  31. 31  WeixinOauth2Token weixinOauth2Token =
  32.    AdvancedUtil.getOauth2A ccessToken("APPID", "APPSECRET", code);
  33. 32  // 网页授权接口访问凭证
  34. 33  String accessToken = weixinOauth2Token.getAccessToken();
  35. 34  // 用户标识
  36. 35  String openId = weixinOauth2Token.getOpenId();
  37. 36  // 获取用户信息
  38. 37  SNSUserInfo snsUserInfo = AdvancedUtil.getSNSUserInfo(accessToken,
  39. openId);
  40. 38 
  41. 39  // 设置要传递的参数
  42. 40  request.setAttribute("snsUserInfo", snsUserInfo);
  43. 41  }
  44. 42  // 跳转到index.jsp
  45. 43  request.getRequestDispatcher("index.jsp").forward(request, response);
  46. 44 }
  47. 45 }

上代码是通过Servlet接收和处理授权回调请求,由于授权完成后是通过URL传递code参数,只需要在doGet()方法中进行处理即可。

第26行代码用于获取code参数。

第29行是根据code判断用户是否同意授权,如果code等于"authdeny"表示用户不同意授权,否则表示同意授权。如果用户不同意授权,则直接跳转到目标页面;如果用户同意授权,则可以调用已经封装好的方法获取用户信息。

第31行是根据APPID、APPSECRET和code获取网页授权access_token,同时也能获取到用户的OpenID。

第37行是获取用户信息。

第40行代码是将用户信息放到request对象中,这样可以传递到目标页面。如果网页授权作用域为snsapi_base,则不能获取用户信息,只需将OpenID放到request对象中即可。最后,通过第43行代码跳转到目标页面。

接下来,需要在项目的web.xml中增加关于OAuthServlet类的配置,代码如下:

  1. <servlet>
  2. <servlet-name>oauthServlet</servlet-name>
  3. <servlet-class>
  4. org.liufeng.course.servlet.OAuthServlet
  5. </servlet-class>
  6. </servlet>
  7.  
  8. <servlet-mapping>
  9. <servlet-name>oauthServlet</servlet-name>
  10. <url-pattern>/oauthServlet</url-pattern>
  11. </servlet-mapping>

2.编写目标页面

用户授权完成后,会通过redirect_uri跳转到目标页面index.jsp,该页面的代码如下:

  1. 1 <%@ page language="java" pageEncoding="gb2312"%>
  2. 2 <%@ page import="org.liufeng.course.pojo.SNSUserInfo;"%>
  3. 3 <html>
  4. 4 <head>
  5. 5  <title>OAuth2.0网页授权</title>
  6. 6  <meta name="viewport" content="width=device-width,user-scalable=0">
  7. 7  <style type="text/css">
  8. 8  *{margin:0; padding:0}
  9. 9  table{border:1px dashed #B9B9DD;font-size:12pt}
  10. 10  td{border:1px dashed #B9B9DD;word-break:break-all; word-wrap:break-word;}
  11. 11  </style>
  12. 12 </head>
  13. 13 <body>
  14. 14  <%
  15. 15  // 获取由OAuthServlet传入的参数
  16. 16  SNSUserInfo user = (SNSUserInfo)request.getAttribute("snsUserInfo");
  17. 17  if(null != user) {
  18. 18  %>
  19. 19  <table width="100%" cellspacing="0" cellpadding="0">
  20. 20  <tr><td width="20%">属性</td><td width="80%">值</td></tr>
  21. 21  <tr><td>OpenID</td><td><%=user.getOpenId()%></td></tr>
  22. 22  <tr><td>昵称</td><td><%=user.getNickname()%></td></tr>
  23. 23  <tr><td>性别</td><td><%=user.getSex()%></td></tr>
  24. 24  <tr><td>国家</td><td><%=user.getCountry()%></td></tr>
  25. 25  <tr><td>省份</td><td><%=user.getProvince()%></td></tr>
  26. 26  <tr><td>城市</td><td><%=user.getCity()%></td></tr>
  27. 27  <tr><td>头像</td><td><%=user.getHeadImgUrl()%></td></tr>
  28. 28  <tr><td>特权</td><td><%=user.getPrivilegeList()%></td></tr>
  29. 29  </table>
  30. 30  <%
  31. 31  }
  32. 32  else
  33. 33  out.print("未获取到用户信息!");
  34. 34  %>
  35. 35 </body>
  36. 36 </html>

上述目标页面的实现比较简单,只是简单展示了当前用户的详细信息。第6行的meta标签用于设置网页的宽度以及是否可缩放,这样页面就能自适应各种大小屏幕的手机。如果不进行此项设置,则页面显示在手机上会非常小,不方便查看。"width=device-width"是将网页宽度设置为手机屏幕的宽度,“user-scalable=0”是禁止用户手动缩放。第16~28行的作用是将获取到的用户信息显示在页面上。

3.构造网页授权链接

在6.3.2节曾介绍过网页授权相关内容,网页授权链接的格式如下:

  1. https:// open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=
  2. REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

我们要根据实际情况替换链接中的APPID、REDIRECT_URI和SCOPE。相信读者对APPID和SCOPE已经非常熟悉了,这里需要注意的是REDIRECT_URI,它表示授权回调地址(OAuthServlet的访问地址),但是要进行URL编码。假如项目部署到BAE上以后,OAuthServlet的访问地址如下:

  1. http:// 0.weixinmptest.duapp.com/oauthServlet

则对上述地址进行URL编码得到的结果就是REDIRECT_URI的值,URL编码方法如下:

  1. /**
  2. * URL编码(utf-8)
  3. *
  4. * @param source
  5. * @return
  6. */
  7. public static String urlEncodeUTF8(String source) {
  8. String result = source;
  9. try {
  10. result = java.net.URLEncoder.encode(source, "utf-8");
  11. } catch (UnsupportedEncodingException e) {
  12. e.printStackTrace();
  13. }
  14. return result;
  15. }
  16.  
  17. String oauthUrl = "http:// 0.weixinmptest.duapp.com/oauthServlet"
  18. System.out.println(urlEncodeUTF8(oauthUrl));

编码后的结果如下:

  1. http%3A%2F%2F0.weixinmptest.duapp.com%2FoauthServlet

最终得到的网页授权链接如下:

  1. https:// open.weixin.qq.com/connect/oauth2/authorize?appid=wx9fd67526e31e66bb&redirect_uri=
  2. http%3A%2F%2F0.weixinmptest.duapp.com%2FoauthServlet
  3. &response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

在公众账号中,可以通过文本消息、图文消息或view菜单按钮将网页授权链接提供给用户。对于view菜单按钮,只需要将它的url属性值设置成上面的链接即可,用户点击菜单后就会进入到网页授权界面。

4.测试体验

为了演示方便,笔者直接将网页授权链接以文本消息的形式提供给用户,完整案例的演示结果如图6-5所示。

6.3.4 案例:通过网页授权获取用户信息 - 图1

图6-5 通过OAuth2.0网页授权获取用户信息演示