微信公众号-网页端开发

marvin

marvin

微信公众号-网页端开发

Oauth2

在进行网页端开发之前我们需要先了解一下 Oauth2 的原理, Oauth2 解决的是三方登录问题, 而微信的网页端开发的认证就是基于 Oauth2的, 比如用户在微信上登录的时候并不是通过我们服务器的账号体系进行登录, 而是借助微信的服务器进行登录的. 这个时候就需要用到 Oauth2 协议, 其工作流程如下:

  1. 用户发送认证请求到服务器
  2. 服务器将用户重定向到第三方的认证页面
  3. 用户输入用户名密码向第三方服务器发送一个认证请求
  4. 第三方服务器通过回调信息给服务器分配一个识别码
  5. 服务器通过识别码向第三方服务器申请令牌
  6. 第三方服务器将令牌返回给服务器
  7. 服务器刷新用户页面

这里的第三方服务器在这里指的就是微信的服务器, 为了模拟微信的环境, 我们需要下载一个微信开发者工具, 打开公众号网页菜单, 在 url 里面输入我们本地的 server 端地址(localhost:8000), 这样实现了 Oauth2 的环境模拟

我们在将用户进行重定向的同时, 需要带着一些信息, 这样微信服务器才能知道应该回调的服务器地址是什么, 这些信息包含:

  • redirect, 回调的服务器地址
  • state, 回调需要带的一些参数
  • scope, 认证的类型, 微信支持多种认证类型

下面我们一步一步来看

将用户重定向到微信认证页面

这里我们只需要将用户重定向到微信的 Oauth2 页面即可, 为此可以新增一个 service:

redirectToOauth2(res: Response, req: Request): void {
  const url = new URL('https://open.weixin.qq.com/connect/oauth2/authorize');
  url.searchParams.append('appid', config.appid);
  url.searchParams.append(
    'redirect_uri',
    `${req.protocol}://${req.get('host')}/wechat/Oauth2Callback`,
  );
  url.searchParams.append('response_type', 'code');
  url.searchParams.append('scope', 'snsapi_userinfo');
  url.searchParams.append('state', '');
  res.redirect(url.toString() + '#wechat_redirect');
}

这里我们只需要按照微信的官方文档的说明, 提供 appid, 一个接收 code 的回调, 然后设定响应类型和scope即认证类型, state即可. 最后拼上一个 hash 将用户重定向.

通过回调处理微信的 code

我们前面添加了一个 wechat/Oauth2Callback 路由, 因此我们需要定义一个 controller:

@Get('Oauth2Callback')
oauth2Callback(@Req() req: Request, @Res() res: Response) {
  this.wechatService.handleWebToken(req, res);
}

这里只是调用了服务的 handleWebToken, 来看 handleWebToken 的定义:

async handleWebToken(req: Request, res: Response): Promise<void> {
  const { code } = req.query;
  const { data } = await axios.get(
    `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${config.appid}&secret=${config.appsecret}&code=${code}&grant_type=authorization_code`,
  );
  const { access_token: accessToken, openid } = data;
  const userResponse = await axios.get(
    ` https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openid}&lang=zh_CN`,
  );
  res.send(userResponse.data);
}

这里我们通过 code 去向微信获取一个临时的网页授权的票据, 同时它会返回一个用户标识, 然后我们通过这个临时票据去请求用户的信息.

jssdk

jssdk 用于我们在微信中调用微信提供的一些原生功能, 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验. 在使用之前我们需要获取 jsapi_ticket, jsapi_ticket是公众号用于调用微信JS接口的临时票据, 并且要通过 sha1 签名算法生成一与认证微信服务器一样的参数信息, 微信还规定, 出于安全考虑,开发者必须在服务器端实现签名的逻辑, 为此我们这里添加一个 service:

async getJsConfig(req: Request, res: Response): Promise<void> {
  const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`.split(
    '#',
  )[0];
  const { access_token }: any = await this.getToken();
  const {
    data: { ticket: jsapi_ticket },
  } = await axios.get(
    `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${access_token}&type=jsapi`,
  );

  const nonceStr = this.uuid();
  const timestamp = +new Date();
  const str = [nonceStr, jsapi_ticket, timestamp, url].sort().join('');
  const signature = crypto.createHash('sha1').update(str).digest('hex');

  res.send({
    data: {
      nonceStr,
      timestamp,
      signature,
      appId: config.appid,
    },
  });
}

我们这里通过 uuid 生成了一个随机字符串, 然后将sha1签名的参数返回给网页, 然后网页端调用微信的 jssdk 的相关函数就可以调用微信的功能了.