【微信公众号开发】五、微信网页授权获取用户openId

前端页面如何进行网页授权,如何获取用户的Openid?

【微信公众号开发】五、微信网页授权获取用户openId

By img Microanswer Create at:Jul 5, 2019, 11:04:45 AM 

Tags: 网页授权 openid 微信公众号 微信开发

前端页面如何进行网页授权,如何获取用户的Openid?


重要声明:本文章仅仅代表了作者个人对此观点的理解和表述。读者请查阅时持自己的意见进行讨论。

目录

本系列博文还包含了下面的博客:

  1. 【微信公众号开发】一、运作及配置流程简介
  2. 【微信公众号开发】二、解析微信请求及响应消息
  3. 【微信公众号开发】三、解析微信事件XML数据消息及响应
  4. 【微信公众号开发】四、公众号按钮设置及自己的微信按钮编辑器
  5. 【微信公众号开发】五、微信网页授权获取用户openId(本文)
  6. 【微信公众号开发】六、微信JS的使用
  7. 【微信公众号开发】七、微信JS需要注意的坑
  8. 【微信公众号开发】八、微信JS发起支付

“如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。”

这是来自微信官方文档中的文案描述。是的,如果不能在自己的网页中获取到微信用户的一些信息,那么要它来有何用?!。为了能够尽快的无误的接通此功能,本文将对此功能的开发做一点介绍和建议。

一、首要注意事项

在进行网页授权之前,必须要先进行后台的一些设置,才能正确的进行授权。

1、回调的域名

在进行网页授权回调时,不是你想要哪一个域名进行回调就能回调的。你必须自己在微信后台设定好可以进行授权回调的域名。设置位置也非常容易找到。登入微信后台定位到:【设置】->【公众号设置】->【功能设置】->【网页授权域名】。然后点击对应的设置按钮即可进行设置。

点击【设置】按钮后,会弹出一个界面,要你输入准许进行回调的域名,这里要求输入的是一个字符串,不需要 http 前缀。也不需要 / 结尾。当你填写完成后,还要将界面上提供的授权文件放入你的后台服务里,并且要保证在域名根目录能访问到,如图:

图1

2、授权scope

授权有 2 种方式,一种静默授权,一种需要用户手动同意的授权,这两种授权的区别在下表进行了介绍:

方式代码区别适用场景
静默授权snsapi_base用户必须已关注公众号公众号内按钮跳转的页面可使用这种授权
提示授权snsapi_userinfo用户不需要关注公众号,这种会显示提示框给用户是否允许授权通过分享出去的页面建议使用这种授权

其中 snsapi_userinfo 方式授权还有个十分特别的特性:如果用户是已经关注了公众号,在进行授权时使用这种方式,也是进行静默授权的方式,不会有提示出现。那么,事情就开始变得有趣起来了,关注了就静默,没关注就带提示。所以我们可以直接全部采用 snsapi_userinfo 方式进行授权,同时兼容没关注和关注了的用户。

二、开始授权

当一切注意事项都了解且配置妥当后,便可以开始进行代码的编写了。我们只需要将用户引导进入这个地址:

<!-- 授权地址 -->
https://open.weixin.qq.com/connect/oauth2/authorize

<!-- 必须参数 -->
appid:               公众号的appid
redirect_uri:        授权完成后要跳转的地址,此地址必须是第一节里面配置的域名下的
response_type:       一直填写 "code"
state:               自定义值,此值只能是128个字长度以内,否则将会报错。
"#wechat_redirect":  所有参数拼接完成后,将此字符串追加在后面。

<!-- 一个完整的可以授权的地址示列(scope为snsapi_userinfo): -->
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

1、前台跳转授权

javascript里,常常使用下面的代码进行页面跳转,从而实现开始授权:

window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";

2、后台重定向授权

有时候你觉得把这么长一串链接交给前段去拼接,然后跳转,这事有点太不优雅了。你希望前端只需要跳转一个比较短的链接地址就实现网页授权就好了。你可以写一个自己的url接口,然后在后端实现重定向:

// 封装了微信网页授权的接口
@RequestMapping("/webAuth.html")
public void getUserInfoByCode(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 逻辑代码
    // ...

    // 后端进行重定向
    response.sendRedirect(
            "https://open.weixin.qq.com/connect/oauth2/authorize" +
                "?appid=wx********0" +
                "&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php" +
                "&response_type=code" +
                "&scope=snsapi_userinfo" +
                "&state=STATE" +
                "#wechat_redirect"
    );
}

无论哪一种方式,都避免不了重定向。使用后端会增加重定向次数,但对用户来说影响不大(如果网络够好)。只要能完成功能,采取什么方法还需开发者自己衡量。

上方示列的授权URL当其成功后,就会跳转到:http://nba.bluewebgame.com/oauth_response.php 页面,并且将授权成功后的一些必要参数传递给该地址,这些参数有:

参数说明
code用于换取凭据,这个凭据是用来获取用户信息的凭据。code值有效期5分钟,每个code还只能用1次
state提交授权时传递的是什么state内容,这里就是什么state内容。

3、特别注意

授权回调这件事情是没有任何问题的,但通常如果你的网页是一个单页面应用,并且你的页面路由模式是 HASH 路由模式(通常你不指定默认就是hash模式),那么这时候你可能就要注意一些事情了。最常见的情况可能就是,你并不能在页面内通过路由提供的api正常获取到网页上的参数值。

下面来看案例:

// 现在已有一个正式环境页面,地址:http://www.demo.com/webapp/#/home
// 看吧,这通常是一个单页面应用的hash路由模式的url地址。
// 此时遇到页面需要授权获取openid了,你准确无误的进行了重定向授权:
// 啪啪啪啪~~
// 授权完成。
// 又回到了你的回调地址,如果你的回调地址就是这个页面本身,那么你此时的地址应该不出意外变成了:
// http://www.demo.com/webapp/?code=ewfiojxwf23r43cmg3&state=xxxx#/home
// 我知道你发现了什么,授权结果参数为啥跑到中间那一截去了?
// 而通常单页面应用提供的路由api能获取的参数是:
// http://www.demo.com/webapp/#/home?name=xxx&age=10
// 是的,能获取的参数在最后面。
// 而授权过后参数在中间,为啥会出现这样的事情?
// 因为:hash路由的url中包含#符号,而一个合法的url解析规则只会认为 # 号的前面部分有效,因此
// 授权回调时会把#号前面识别为有效url,后面的不处理,参数拼接到#号前面的url的后面。

那么当你了解了这件事情,你也许就知道该怎么在页面内去获取这些回调授权后得到的参数了。下面是一个示列获取解决方案:

/**
 * 获取指定url里的参数。如果不指定,将使用当前页面的url。
 * @param url
 */
function url2query (url) {
    url = url || window.location.href;

    var paths = url.split('#'); // 考虑到单页面应用支持,先按#号分割。

    var query = {};
    // 然后进行处理
    for (var i = 0; i < paths.length; i++) {
        var url_s = paths[i];
        var ff = url_s.split('?');
        if (ff.length >= 2) {
            var msearch = ff[ff.length - 1];
            if (msearch) {
                var kvs = msearch.split('&');
                for (var index = 0; index < kvs.length; index++) {
                    var kv = kvs[index].split('=');
                    query[kv[0]] = kv[1];
                }
            }
        }
    }
    return query;
}

// 使用方法:
var query = url2query();
var code = query.code;
var state = query.state;
var name = query.name;

三、获取用户的OpenId和微信信息

1、获取凭据和openid

完成了授权,获取到了 code 后,现在你就可以用 code 这个参数值来获取用户的微信信息了。但正如上边表格里说明的,code是用来换取凭据的,用户信息只有通过凭据拿到。那么就先来获取凭据:

使用微信提供的地址及参数:

<!-- 换取凭据的接口地址 -->
https://api.weixin.qq.com/sns/oauth2/access_token

<!-- 需要参数 -->
appid:       公众号的appid
secret:      公众号的appsecret
code:        通过授权获取到的code,既上一步拿到的code
grant_type:  一直填值"authorization_code"

使用Java执行Get请求,换取凭据

Map<String, String> param = new HashMap<>();
param.put("appid",        "自己公众号的appid");
param.put("secret",       "自己公众号的appsecret");
param.put("code",         "通过授权获取到的code");
param.put("grant_type",   "authorization_code");

String result = HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/access_token", param);

// 得到返回的凭据
// {
//     "access_token":"ACCESS_TOKEN",  // 网页授权接口调用凭证,也就是获取用户信息的凭据
//     "expires_in":7200,              // access_token 的过期时间
//     "refresh_token":"REFRESH_TOKEN",// access_token 过期后可通过此值获取新的
//     "openid":"OPENID",              // 用户的 openId,这个id是此用户再公众号里的唯一标示
//     "scope":"SCOPE"                 // 用户授权的作用域
//  }

现在你已经拿到了用户的openid了,它足以确定用户在此公众号内的唯一性了,每一个微信号,在公众号里对应唯一一个openid。如果还要进一步获取微信用户信息(比如注册时需要一些基本信息),则可以继续向下阅读。

要注意,这个返回的凭据名称也叫 access_token, 这个名字和请求微信API接口的access_token是一样的,但它们的用途确大不一样。这里的access_token是用来获取用户信息的,而另一个是用来调用微信接口进行校验的,所以不要把它们搞混淆了。

这个access_token的有效期为2个小时,在2个小时的时间内,你都可以用这个access_token去获取用户的信息。当你发现它过期了,你应该使用 refresh_token 去重新获取一个新的 access_token

2、刷新access_token

下面的代码示列了如何通过 refresh_token 刷新凭据 access_token:

Map<String, String> param = new HashMap<>();
param.put("appid",         "自己公众号的appid");
param.put("grant_type",    "refresh_token");
param.put("refresh_token", "还未过期的refresh_token");

String result = HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/refresh_token", param);

// 得到新的凭据
// { 
//     "access_token":"ACCESS_TOKEN",   // 网页授权接口调用凭证,也就是获取用户信息的凭据
//     "expires_in":7200,               // access_token 的过期时间
//     "refresh_token":"REFRESH_TOKEN", // access_token 过期后可通过此值获取新的
//     "openid":"OPENID",               // 用户的 openId,这个id是此用户再公众号里的唯一标示
//     "scope":"SCOPE"                  // 用户授权的作用域
// }

然后使用新获取到的access_token去获取用户信息就好了。而 refresh_token 也不是永久有效的,它的过期时间为 30 天,当 refresh_token 也过期了,就必须让用户再进行一次授权操作了。

3、获取用户基本信息

最后一步,获取用户的基本信息,微信提供了接口允许开发者获取用户的一些基本信息,接口:

<!-- 接口地址 -->
https://api.weixin.qq.com/sns/userinfo

<!-- 参数 -->
access_token: 使用code获取到的凭据
openid:       用户在公众号里的唯一标示
lang:         国内就填写:"zh_CN"。zh_CN 简体,zh_TW 繁体,en 英语

使用java进行Get请求,获取用户基本信息:

Map<String, String> param = new HashMap<>();
param.put("access_token",  "code换取到的凭据");
param.put("openid",        "用户在公众号里的唯一标示");
param.put("lang",          "zh_CN");

String result = HttpUtil.get("https://api.weixin.qq.com/sns/userinfo", param);

// 得到用户的基本信息
// {
//     "openid":" OPENID",
//     "nickname": NICKNAME,             // 微信昵称
//     "sex":"1",                        // 性别,值为1时是男性,值为2时是女性,值为0时是未知
//     "province":"PROVINCE"             // 用户个人资料填写的省份
//     "city":"CITY",                    // 用户个人资料填写的城市
//     "country":"COUNTRY",              // 国家,如中国为CN
//     "headimgurl": "http://t****e/64", // 微信头像,最后一个数值代表正方形头像大小(有0、46、64、
                                         // 96、132数值可选,0代表640*640正方形头像),用户没有头像时
                                         // 该项为空。若用户更换头像,原有头像URL将失效。
//     "privilege":["PRIVILEGE1"],       // 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
//     "unionid": "o******"              // 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
// }
Full text complete, Reproduction please indicate the source. Help you? Not as good as one:
Comment(Comments need to be logged in. You are not logged in.)
You need to log in before you can comment.

Comments (0 Comments)