H5网页微信/企微的授权、SDK使用

977 阅读4分钟

前言

名词解释:

微信:就是我们常用的个人微信
企微:有企业资质认证的企业微信,可以与个人微信信息互通

首先了解下工作原理

微信授权
  1. 授权时序图

  1. 关键参数说明
/**
 * 微信授权请求链接地址
 * appid: 微信公众号appid
 * redirectUri: 授权成功后的重定向网页地址
 * scope: snsapi_base | snsapi_userinfo
 * extraParam: 额外的参数,会在微信授权成功后,拼接在重定向地址后面
 */
const wxAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize
	  ?appid=${appid}
          &redirect_uri=${redirectUri}
          &response_type=code
          &scope=${scope}
          &state=${extraParam}
          #wechat_redirect`;

/**
 * 微信授权成功后的重定向地址
 * code: 微信授权成功后,返回的临时code口令,后续授权接口请求的参数需要用到
 * extraParam: 上一步发起授权时拼接的参数
 */
const wxAuthRedirectUrl = `https://api.xxx.com/wx/page
          ?code=${code}
          &state=${extraParam}`;

/**
 * 微信获取用户信息的API接口,由于参数涉及到非公开的密钥,所以最好在服务器内发起请求
 * 此接口,仅能获取到用户access_token, openid, 
 * 其中unionid(只有当 scope 为"snsapi_userinfo"时才有返回值)
 * appid: 微信公众号appid
 * secret: 微信公众号密钥,从微信公众号管理后台配置页面获取
 * code: 上一步微信授权成功后的重定向地址拼接的临时code
 */
const api = `https://api.weixin.qq.com/sns/oauth2/access_token
	  ?appid=${appid}
 	  &secret=${secret}
          &code=${code}
          &grant_type=authorization_code`


/**
 * 获取用户详细信息
 * accessToken: 上一步获取到的accessToken
 * openid: 上一步获取到的用户openid(用户在当前公众号内的唯一标识)
 */
const userInfoApi = `https://api.weixin.qq.com/sns/userinfo
	  ?access_token=${accessToken}
 	  &openid=${openid}
          &lang=zh_CN`
企微授权
  1. 授权时序图,几乎和微信授权逻辑一致。

下面是官方提供的授权时序图

  1. 在关键参数上略有差异
/**
 * 企业微信授权请求链接地址
 * corpId: 企微的corpId
 * redirectUri: 授权成功后的重定向网页地址
 * scope: snsapi_base
 * extraParam: 额外的参数,会在微信授权成功后,拼接在重定向地址后面
 */
const wxAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize
	  ?appid=${corpId}
          &redirect_uri=${redirectUri}
          &response_type=code
          &scope=${scope}
          &state=${extraParam}
          #wechat_redirect`;

/**
 * 企业微信授权成功后的重定向地址
 * code: 微信授权成功后,返回的临时code口令,后续授权接口请求的参数需要用到
 * extraParam: 上一步发起授权时拼接的参数
 */
const wxAuthRedirectUrl = `https://xxxxx.com/wx/page
	  ?code=${code}
 	  &state=${extraParam}`;


/**
 * 企业微信获取用户信息的API接口,由于参数涉及到非公开的密钥,所以最好在服务器内发起请求
 * 此接口,仅能获取到用户access_token, openid, unionid
 * corpId: 企业微信的corpId
 * secret: 企业微信账号的密钥,从企微管理后台获取
 * 
 * return access_token
 */
const getAccessTokenApi = `https://qyapi.weixin.qq.com/cgi-bin/gettoken
	  ?corpid=${corpId}
 	  &corpsecret=${secret}`

/**
 * 企业微信获取用户信息的API接口,由于参数涉及到非公开的token,所以最好在服务器内发起请求
 * 此接口,仅能获取到用户的userid
 * accessToken: 上一步获取到的access_token
 * code: 上面重定向Url后携带的code
 * 
 * return access_token
 */
const getUserIdApi = `https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo
	  ?access_token=${accessToken}
 	  &code=${code}`

/**
 * 获取用户详细信息
 * accessToken: 上一步获取到的accessToken
 * userid: 上一步获取到的用户userid(用户在当前企业微信内的唯一标识)
 */
const getUserInfoApi = `https://qyapi.weixin.qq.com/cgi-bin/user/get
	  ?access_token=${accessToken}
 	  &userid=${userid}`
微信SDK注册
1. 引入JS文件
// 标签方式,在入口index.html中引入
// 备用地址//res2.wx.qq.com/open/js/jweixin-1.6.0.js
<script src="//res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
// 或者使用动态创建script的方式引入
const script = document.createElement('script');
script.src = '';

2. JSSDK加载完成后,进行初始配置
wx.config({
  debug: true, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来
  appId: '', // 必填,公众号的唯一标识
  timestamp: , // 必填,生成签名的时间戳,由后端服务定义好
  nonceStr: '', // 必填,生成签名的随机串,由后端服务定义好
  signature: '',// 必填,签名,后端接口,通过timestamp,nonceStr调用微信API生成
  jsApiList: [] // 必填,需要使用的 JS 接口列表
});

3. 通过 ready 接口处理成功验证
wx.ready(function(){
  // config信息验证后会执行 ready 方法,所有接口调用都必须在 config 接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在 ready 函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在 ready 函数中。
});

4. 通过 error 接口处理失败验证
wx.error(function(res){
  // config信息验证失败会执行 error 函数,如签名过期导致验证失败,具体错误信息可以打开 config 的debug模式查看,也可以在返回的 res 参数中查看,对于 SPA 可以在这里更新签名。
});
企微SDK注册
与微信SDK注册方式完全一致
区别:
1. 初始化配置的签名接口不同
2. 可使用的API是独立的,参考官方文档使用

正确的使用姿势

微信授权
  1. 前端页面重定向至 -> 后端A接口
// 微信回调后端的请求地址
const redirectUrl = location.href;
// 跳转后端授权地址
location.href = `https://api.xxxx.com/wxauth
	  ?redirectUrl=${encodeURIComponent(redirectUrl)}`;
  1. 后端A接口 -> 重定向微信授权网址 -> 回调到后端B接口->通过授权token获取微信用户信息 -> 后端B接口重定向前端页面地址,URL后面拼接用户unionid/sessionid
// 字符串拼接用的是JS写法(偷个懒)
// 接口A
@RequestMapping(value = "/wxauth",method = RequestMethod.GET)
private RES wxauth(@RequestParam("redirectUrl") String redirectUrl){
	// 缓存redirectUrl
    Redis.set(uuid, redirectUrl);
    String redi = `https://open.weixin.qq.com/connect/oauth2/authorize
    	  ?appid=${appid}
      	  &redirect_uri='/wxlogin' // 接口B
      	  &response_type=code
      	  &scope=${scope}
      	  &state=${uuid}
      	  #wechat_redirect
        `
    // 跳转微信授权页面
    return 'redirect:' + redirectUrl;
}


// 接口B 来自微信的回调,获取到临时code参数
@RequestMapping(value = "/wxlogin",method = RequestMethod.GET)
private RES wxlogin(@RequestParam("code") String code, @RequestParam("state") String state){

    //1.登录凭证验证,获取access_token、openid
    String tokenRes = HttpUtil.sendGet(
	`https://api.weixin.qq.com/sns/oauth2/access_token
            ?appid=${appid}
            &secret=${secret}
            &code=${code}
            &grant_type=authorization_code`);
    //2.获取用户的unionid、头像、昵称信息(前提:网页授权作用域必须为snsapi_userinfo)
    String userInfoRes = HttpUtil.sendGet(
	`https://api.weixin.qq.com/sns/userinfo
            ?access_token=${accessToken}
            &openid=${openid}
            &lang=zh_CN`);
    try {
        WxMiniUser wxMiniUser = JSON.parseObject(userInfoRes, WxMiniUser.class);
        return 'redirect:' + redirectUrl + '?sessionid=' + sessionId;
    }catch (Exception e){
        e.printStackTrace();
    }
}
  1. 页面入口处,获取URL上的参数unionid/sessionid,请求接口获取当前用户相关的业务信息
setup(){
  const {sessionid} = route.query
  const userInfo = getUserInfo(sessionid);
  return ()=> <div>{userInfo.nickname}</div>
}
企微授权

总体流程和微信授权一致,注意区分appid、corpId

微信/企微SDK的使用

SDK的使用,主要关注一个调用时机问题。

  1. 页面加载完成后,立即要调用的API,统一放到ready的回调中去
  1. 由用户行为:点击、滑动...交互操作,可以直接调用,做好异常处理就行(比如SDK注册失败后,页面是否允许用户正常浏览,相关的API调用直接跳过,还是....)

经验汇总

微信授权快照说明与方案

当开发者在网页中在不规范使用发起 snsapi_userinfo 网页授权时,微信将默认打开网页快照页模式进行基础浏览。

如上图所示,当我们在已进入页面,且用户还未产生任何交互时,进行了微信授权跳转,微信就会生成快照页来阻止我们获取用户信息。

注意:此时我们在快照页内所获取的头像、昵称、openId、unionId 均为虚拟账号数据,可能会对产生垃圾数据

不规范的行为有:

  1. 强制登录: 在用户打开网页时立即要求用户授权,用户拒绝后无法使用网页提供的服务;
  2. 违规收集个人信息: 未在网页提前告知使用个人信息的目的、方式和范围;
  3. 非必要收集: 非必要获取用户信息的网页,如文章、视频等,要求用户在浏览内容前登录;
  4. 差别对待微信用户: 同样的网页在浏览器内可以无需登录直接访问,在微信内却要求用户先登录才可访问。

目前能规避微信H5页面生成快照的方式

  1. 自定义游客模式页面,即用户每次进入页面时,都会进入无用户信息的游客页面,用户需要通过页面中的按钮等引导操作,触发微信授权后,才能进行后续的操作。(如果已授权的用户,除了没授权弹窗外,其他流程和游客模式一致)

  2. 静默授权与信息授权搭配使用,即通过静默授权获取到openid,进行接口请求,判断是否已存在当前用户的unionid,如果有,则直接返回当前用户的信息,如果没有,再跳转至游客模式页面,进行重新授权。
    ps: 这种方案,需要后端存储一份openid -> unionid的映射表,且之前是有存储过用户对应此公众号的openid的。

微信内置浏览器的localStorage、cookie 都存在不稳定的情况,无法长期存储、或切换微信账号后用户信息混乱问题,在微信还未提供明确的官方说明时,不推荐使用。

参考资料:

developers.weixin.qq.com/doc/offiacc…