微信公众号网页开发:OAuth2.0鉴权获取用户信息

文章目录

       最近在学习微信公众号网页的开发,在公众号通过按钮访问网页时需要获取用户信息。但是访问时,无法将用户相关信息传递到所访问的网页中。

      在查阅开发文档后发现微信网页中如果需要获取用户信息需要通过Oauth2.0鉴权,获取code,然后通过code获取用户的openid和access_token(这里的access_token与全局的不同仅用于获取用户信息),在通过openid和access_token来获取用户信息。具体获取步骤如下:

 

设置授权回调页面域名

      测试号中找到“体验接口权限表-网页服务-网页帐号-网页授权获取用户基本信息”后面的修改设置,公众号中找到“开发-接口权限-网页服务-网页授权-网页授权获取用户基本信息”点击修改设置,如下图:

注:这里设置的是域名不是地址,如你需要使用鉴权地址为”http://www.niconi.cn/xxx”那么将”www.niconi.cn”填入即可。因为微信公众号的安全级别非常高,只有认证的公众号以及测试号可以使用Oauth2.0鉴权验证权限。请确保拥有此权限在进行相关操作。

 

获取code

      因为微信公众号的安全级别非常高,所以Oauth2.0鉴权页面只能通过微信客户端打开(开发过程中可以使用微信web开发者工具访问)。

      访问Oauth2.0鉴权页面有两种方式,一种是通过客户端直接访问,另一种是通过302重定向跳转访问。具体的访问参数如下:

访问地址:https://open.weixin.qq.com/connect/oauth2/authorize

访问方式:Get

参数列表:

  • appid:微信公众号的appid。
  • redirect_uri:回调地址,用户确认后会访问此地址。
  • response_type:填写code用来获取code。
  • scope:snsapi_userinfo需要用户确认,可获取头像昵称地址等信息。snsapi_base不需要用户确认,只能获取openid。
  • state:自定义信息,会将state的参数加在回调地址后面,可空。
  • #wechat_redirect:直接添加到地址结尾。必须添加,否者会提示没有权限。

 

回调地址跳转相关:

  • 如果使用snsapi_userinfo方式,那么用户在首次访问时会进入授权页面,用户同意授权后会跳转到redirect_uri所填写的地址,并附带相关的参数(如我填写的回调地址为http://www.niconi.cn/userinfo那么将将跳转到http://www.niconi.cn/userinfo?code=所取回的CODE&state=所填写state内容)。
  • 如果使用snsapi_base方式,那么将直接跳转到回调地址并附带参数。此方式取回的CODE并不能用于获取用户信息,只能获取openid。

 

注:CODE有效时间为5分钟,且使用一次之后将失效

示例拼接地址(红色部分为替换为自己的信息):

snsapi_userinfo方式

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=URL&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect

snsapi_base方式:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=URL&response_type=code&scope=snsapi_base&state=123#wechat_redirect

 

获取网页授权access_token

      通过上一步获取CODE后就可以用来换取网页授权的access_token和openid了。如果使用snsapi_base方式,则到此步骤将结束。居然访问参数如下:

访问地址:https://api.weixin.qq.com/sns/oauth2/access_token

访问方式:Get

参数列表:

  • appid:微信公众号的appid。
  • secret:微信公众号的appsecret
  • code:获取到的CODE
  • grant_type:固定填写authorization_code

返回内容:

1
2
3
4
5
6
7
{
  "access_token": "用于网页授权的access_token",
  "expires_in": 超时时间默认7200秒,
  "refresh_token": "用于刷新access_token的refresh_token",
  "openid": "用户的openid",
  "scope": "用户授权作用域"
}

引用:由于公众号的secret和获取 到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。

 

获取用户信息

      取到access_token和openid后就可以用来获取用户信息了。具体获取方式如下:

访问地址:https://api.weixin.qq.com/sns/userinfo

访问方式:Get

参数列表:

  • access_token:取回的access_token,非全局access_token。
  • openid:获取到的openid
  • lang:zh_CN 简体,zh_TW 繁体,en 英语

返回内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "openid": "OPENID",
  "nickname": "用户昵称",
  "sex": "1.男 2.女 0.未知",
  "province": "省份",
  "city": "城市",
  "country": "国家",
  "headimgurl": "用户头像地址",
  "privilege": [
    "用户特权信息"
  ],
  "unionid": "绑定后用户才会出现此字段"
}

 

示例代码片段Java

       代码中使用到了阿里的HttpUtils工具类,获取地址为:Github地址,相关依赖请参考:依赖地址。Gson的jar包请自行搜索获取。

       因示例代码使用Maven工程的ssm框架进行开发,请根据自己情况进行适当修改。

WeChatUtils.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
 
import java.util.HashMap;
import java.util.Map;
 
public class WechatUtils {
    private static Logger logger = Logger.getLogger(WechatUtils.class);
    private final static String APPID = "你的APPID";
    private final static String SECTET = "你的APPSECTET";
    private final static String HOST = "https://api.weixin.qq.com";
    private final static String PATH_SNS_USERINFO = "/sns/userinfo";
    private final static String PATH_SNS_ACCESSTOKEN = "/sns/oauth2/access_token";
    private final static String METHOD_GET = "GET";
    private static Map<String, String> headers = new HashMap<>();
 
    /**
     * 拼接授权页面URL,使用snsapi_userinfo方式
     * @param url 回调页面地址
     * @return 返回完整的授权页面地址
     */
    public static String getUserCodeUrl(String url) {
        return "https://open.weixin.qq.com/connect/oauth2/authorize?" + "appid=" + APPID + "&redirect_uri=" + url + "&response_type=code&scope=snsapi_userinfo#wechat_redirect";
    }
 
    /**
     * 获取用于网页授权的access_token
     * @param code 返回的code
     * @return 成功返回包含access_token和openid的Map
     */
    public static Map<String, String> getSnsAccessToken(String code) {
        HashMap<String, String> querys = new HashMap<>();
        Map<String, String> returnMap = new HashMap<>();
        querys.put("appid", APPID);
        querys.put("secret", SECTET);
        querys.put("code", code);
        querys.put("grant_type", "authorization_code");
        try {
            HttpResponse response = HttpUtils.doGet(HOST, PATH_SNS_ACCESSTOKEN, METHOD_GET, headers, querys);
            String json = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSONObject.parseObject(json);
            returnMap.put("access_token", jsonObject.getString("access_token"));
            returnMap.put("openid", jsonObject.getString("openid"));
        } catch (Exception e) {
            logger.info("获取Oauth2 Access Token失败:" + e);
        }
        return returnMap;
    }
 
    /**
     * 获取用户信息接口
     * @param accessToken 网页授权的access_token
     * @param openid      用户的openid
     * @return 成功返回包含用户信息的json字符串,失败返回空字符串
     */
    public static String getSnsUserInfo(String accessToken, String openid) {
        HashMap<String, String> querys = new HashMap<>();
        querys.put("access_token", accessToken);
        querys.put("openid", openid);
        querys.put("lang", "zh_CN");
        String jsonText = "";
        try {
            HttpResponse response = HttpUtils.doGet(HOST, PATH_SNS_USERINFO, METHOD_GET, headers, querys);
            jsonText = EntityUtils.toString(response.getEntity());
        } catch (Exception e) {
            logger.info("获取用户基本信息失败:" + e);
        }
        return jsonText;
    }
}

实体类UsetInfo.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.Serializable;
 
public class UserInfo implements Serializable {
 
    //用户的标识,对当前公众号唯一  
    private String openid;
    //用户的昵称  
    private String nickname;
    //用户的性别,值为1时是男性,值为2时是女性,值为0时是未知  
    private int sex;
    //用户的语言,简体中文为zh_CN  
    private String language;
    //用户所在城市  
    private String city;
    //用户所在省份  
    private String province;
    //用户所在国家  
    private String country;
    //用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。  
    private String headimgurl;
    //用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)  
    private String[] privilege;
    //只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。  
    private String unionid;
 
    //getter setter方法请自行添加  
}

Service层WeChatServiceImpl.java 接口根据方法自行编写即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import cn.niconi.wechat.pojo.UserInfo;
import cn.niconi.wechat.service.WeChatService;
import cn.niconi.wechat.utils.WechatUtils;
import com.google.gson.Gson;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
 
@Service
public class WeChatServiceImpl implements WeChatService {
    private Logger logger = Logger.getLogger(WeChatServiceImpl.class);
 
    /**
     * 获取用户信息
     * @param openid 用户openid
     * @return 成功返回用户信息实体类,失败返回空
     */
    @Override
    public UserInfo getUserInfo(String accessToken, String openid) {
        UserInfo userInfo = null;
        String userInfoJson = WechatUtils.getSnsUserInfo(accessToken, openid);
        if (!userInfoJson.isEmpty()) {
            //使用Gson将返回的信息转换为实体类  
            userInfo = new Gson().fromJson(userInfoJson, UserInfo.class);
        }
        return userInfo;
    }
}

Controller层 WeChatController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import cn.niconi.wechat.pojo.UserInfo;
import cn.niconi.wechat.service.WeChatService;
import cn.niconi.wechat.utils.WechatUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
 
@Controller
@RequestMapping(value = "/wechat")
public class WeChatController {
    private Logger logger = Logger.getLogger(WeChatController.class);
    @
            Autowired
    private WeChatService weChatService;
 
    /**
     * 用于重定向授权页面,所有需要授权的页面均通过本接口跳转
     * @param request
     * @param response
     * @param url      回调地址
     */
    @RequestMapping(value = "/login")
    public void login(HttpServletRequest request, HttpServletResponse response, String url) {
        try {
            response.sendRedirect(WechatUtils.getUserCodeUrl(url));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 用户信息获取页面示例接口
     * @param request
     * @param response
     * @param map      用来接收code
     * @return
     */
    @RequestMapping(value = "/userinfo", headers = "Accept=text/html;charset=UTF-8")
    public ModelAndView userinfo(HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> map) {
        ModelAndView modelAndView = new ModelAndView("userinfo");
        UserInfo userInfo = null;
        //获取code  
        String code = map.get("code") != null ? map.get("code").toString() : "";
        if ("".equals(code)) {
            //如果code为空则返回空页面  
            return modelAndView;
        } else {
            //获取access_token和openid  
            Map<String, String> accessTokenMap = WechatUtils.getSnsAccessToken(code);
            //获取用户信息  
            userInfo = weChatService.getUserInfo(accessTokenMap.get("access_token"), accessTokenMap.get("openid"));
        }
        if (userInfo != null) {
            //将用户信息添加到attribute  
            try {
                modelAndView.addObject("openid", new String(userInfo.getOpenid().getBytes("iso8859-1"), "UTF-8"));
                modelAndView.addObject("name", new String(userInfo.getNickname().getBytes("iso8859-1"), "UTF-8"));
                modelAndView.addObject("sex", userInfo.getSex() == 0 ? "未知" : userInfo.getSex() == 1 ? "男" : "女");
                modelAndView.addObject("city", new String(userInfo.getCity().getBytes("iso8859-1"), "UTF-8"));
                modelAndView.addObject("province", new String(userInfo.getProvince().getBytes("iso8859-1"), "UTF-8"));
                modelAndView.addObject("country", new String(userInfo.getCountry().getBytes("iso8859-1"), "UTF-8"));
                modelAndView.addObject("headimgurl", userInfo.getHeadimgurl());
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else {
            modelAndView.addObject("message", "请先关注公众号再访问。");
        }
        return modelAndView;
    }
}

userinfo.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
<c:if test="${openid != null}">
    openid:${openid}<br/>
    昵称:${name}<br/>
    性别:${sex}<br/>
    国家:${country}<br/>
    省份:${province}<br/>
    城市:${city}<br/>
    头像:<img src="${headimgurl}">
</c:if>
${message}
</body>
</html>

使用微信web开发者工具访问以下地址:

http://域名/wechat/login?url=http://域名/wechat/userinfo

原文链接:,转发请注明来源!

发表评论

要发表评论,您必须先登录