网站服务部小组文档

移动端网站微信授权的处理方法

目的

众所周知, 人们的生活越来越离不开微信。分享到微信朋友圈是目前公司移动端项目推广与传播的主要途径之一。其中微信互动、微信小游戏等都涉及到微信授权。可以预测到未来还会有更多的微信授权相关的项目,所以急需让每个移动端开发人员熟悉微信授权的整个开发流程。

微信授权简介

如果用户在微信中(Web微信除外)访问公众号的第三方网页,公众号开发者可以通过此接口获取当前用户基本信息(包括昵称、头像、性别、城市、国家)。利用用户信息,可以实现体验优化、用户来源统计等功能。 我们日常所涉及到的微信授权项目都是属于网易游戏玩家俱乐部这个公众号下面的第三方网页,即我们所做的授权是网页授权网页授权获取用户基本信息,无需消息交互,只需用户进入到公众号的第三方网页,弹出用户微信授权界面,用户点击授权之后,即可获取用户的基本信息(此过程甚至不需要用户已经关注了该公众号)。

项目开发流程

第一步:配置授权回调域名

微信OAuth2.0授权登录让微信用户使用微信身份安全登录第三方应用或网站,在微信用户授权登录已接入微信OAuth2.0的第三方应用后,第三方可以获取到用户的接口调用凭证(access_token),通过access_token可以进行微信开放平台授权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放功能等。

在微信公众号请求用户网页授权之前,开发者需要先到公众平台网站的我的服务页中配置授权回调域名。请注意,这里填写的域名不要加http://

授权回调域名配置规范为全域名,比如需要网页授权的域名为:nie.163.com,配置以后此域名下面的页面http://nie.163.com/weixin/tjl/http://nie.163.com/weixin/oversea_korea/ 都可以进行OAuth2.0鉴权。但http://ldxy.163.comhttp://zd.163.com 无法进行OAuth2.0鉴权。

其实配置授权回调域名这一步已经不需要我们去配置,网易游戏玩家俱乐部已经帮我们配置好了,域名就是:nie.163.com

以后涉及到微信授权的站点,都放在FTP目录:/nie_all/nie/weixin/

第二步:建立index.html和app.html

  • 约定index.html和app.html页面均放于FTP目录:/nie_all/nie/weixin/具体项目名称
  • index.html

是一个入口页面,默认进来的时候,url是不存在type=user参数,也就是直接跳转。当程序需要获取当前用户的详细信息时,获取不到用户的昵称(也就说明用户未授权),即会跳转到有带type=user的url,去显示“用户授权页面”,用户只需授权一次。如果用户已授权,则相应的应用授权作用域为snsapi_base(不弹出授权页面,直接跳转);如果用户未授权,则相应的应用授权作用域为snsapi_userinfo(弹出授权页面,可通过openid拿到昵称、性别、所在地)。

<!--20150516 说明:微信授权重定向地址现在要改为http://game.163.com/weixin/xxx/xxx.html。开发页面依然放在nie_all/nie/weixin/下面。-->
<!DOCTYPE HTML>
<html>
<head>
<title>《猎魔通缉令》官方网站-全球首款3D漫画冒险手游</title>
<meta name="keywords" content="猎魔通缉令,猎魔,漫画手游,二次元手游,漫画风手游,网易手游,韩国手游,网易" />
<meta name="description" content="《猎魔通缉令》是风靡韩国的第一动作RPG手游,长期占据畅销榜和下载榜双榜前十。韩国累积拥有近千万游戏用户!游戏是全球首款采用漫画贴图技术的动作手游,独特精致的二次元漫画风,极致畅爽的战斗关卡,将给玩家热血爆棚的游戏体验!" />
<meta name="format-detection" content="telephone=no"/>
<meta name="format-detection" content="address=no"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta charset="gb2312">
</head>
<body>
<div style="font-size:36px;display:none;color:#ff0000;" id="tips">这是一个穿越页面,当你看到这个页面时候请耐心等待,如果右上角有关闭按钮就点击关闭吧</div>
<script>
//请求超过6秒钟,显示提示信息
setTimeout(function(){
    document.getElementById('tips').style.display='block';
}, 6000);
//获取URL参数
var params = function(u, p){
        var m=new RegExp("(?:&|/?)"+p+"=([^&$]+)").exec(u);
        return m?m[1]:'';
}
//设置一个nie参数,是与后面的shareUrl对应的
var box = "?nie=163";
//这里的bid就是微信朋友圈分享出来的链接中所带的用户ID 该ID是后台数据库中唯一的ID
var bid = params(location.search, "bid");
//当程序需要获取当前用户的详细信息时,获取不到用户的昵称(也就说明用户未授权),即会跳转到有带type=user的url 此时的type为user 用户授权之后 type为空
var type = params(location.search, "type");
if(bid) {
    box = box+'&bid='+bid;
}
var toLink = '';
if(type=='user') {
  //update on 15/06/2015 - suquhong
  //这里只是通过url的type参数来判断要重定向到哪个地址。
  //type=='user'时,box组装uinfo=1,此时下面跳转地址的scope=snsapi_userinfo,代表需要获取微信用户的所有详细信息(包括微信id,昵称,头像,性别,城市,国家等)--会弹出绿色的授权提示框,让用户确认授权

    box = box+'&uinfo=1';
    box = encodeURIComponent(box);
    toLink="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx85f583832dbd07e9&redirect_uri=http%3A%2F%2Fgame.163.com%2Fweixin%2Ftjl%2Fapp.html" + box + "&response_type=code&scope=snsapi_userinfo&state=163#wechat_redirect";
} else {
  //type!=='user'时,box组装uinfo=2,此时下面跳转地址的scope=snsapi_base,代表不需要获取微信用户的所有详细信息,只需要拿到微信用户的微信ID。--不会弹出绿色的授权提示框
  //所以前端要根据不同的需求去做不同的跳转,有些项目只需要拿到微信用户的微信id即可,就跳到下面的这个地址就行,上面那个地址直接不管它。

    box = box+'&uinfo=2';
    box = encodeURIComponent(box);
    toLink="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx85f583832dbd07e9&redirect_uri=http%3A%2F%2Fgame.163.com%2Fweixin%2Ftjl%2Fapp.html" + box + "&response_type=code&scope=snsapi_base&state=163#wechat_redirect";
}
setTimeout(function(){
    location.href=toLink;
}, 200);
</script>
</body>
</html>
  • app.html

是主要的内容页,其中依赖的JS就是主程序了。

<script charset="gb2312" type="text/javascript" src="js/app/index.js"></script>

第三步:书写主程序index.js

其中包含的主要接口有:获取当前用户详细信息用户加入活动用户投票(点踩or点赞)获取某个用户的投票列表 等等。 下面,我们先来定义两个对象,存储当前用户(简称用户)的信息和分享这条朋友圈的用户(简称朋友)的信息 注:以下接口只是案列,不是通用的,不同的项目要请平台开发相应的接口!

  • 用户 - 对象
var user = {
  //是否为该朋友投过票
  voted: false,
  //是否参加了该活动
  isact: false,
  //当前用户ID
  user_id: 0,
  //用户同意授权后的code换取的 接口调用凭证token
  weixin_token: '',
  //当前用户昵称
  nickname: '',
  //当前用户头像
  headimgurl: ''
}
  • 朋友 - 对象
var friend = {
  //朋友的ID
  friend_id: 0,
  //朋友的昵称
  nickname: '',
  //朋友的头像
  headimgurl: ''
}
  • 获取当前用户详细信息 - 案列接口
function getUserInfo() {
  //获取分享链接中用户的ID
  var bid = params(location.search, "bid");
  //用户同意授权 获取code
  var code = params(location.search, "code");
  //获得链接中的uinfo,uinfo为1代表需要授权获取用户昵称等信息,uinfo为2代表不需要授权获取用户昵称等信息
  var uinfo = params(location.search, "uinfo");
  $.ajax({
    url: "http://liemo.webapp.163.com/user/login",
    data: {
      'view_user_id': bid,
      'code': code,
      'need_userinfo': uinfo
    },
    async: false,
    dataType: "jsonp",
    success: function(data) {
      if (data.success == true) {
        user.voted = data.voted;
        user.isact = data.isact;
        user.user_id = data.user_id;
        user.weixin_token = data.weixin_token;
        user.nickname = data.nickname;
        user.headimgurl = data.headimgurl;
        if (!user.headimgurl) {
          //当获取不到用户的头像时,建议给一个默认的图片作头像,这个看项目需求
          user.headimgurl = defPic;
        }
        //如果是从官方二维码扫进来的,默认访问自己的页面
        if (bid == '') {
          bid = user.user_id;
        }
        //从哪里进来的 也就是朋友的ID
        friend.friend_id = bid;
        if (!user.nickname) {
          setTimeout(function() {
            //如果获取不到昵称则跳转回授权页面获取用户信息
            location.href = "index.html?type=user&bid=" + bid + '&_t=' + Math.random();
          }, 600);
          return;
        }
        //分享图片设置为用户头像,这个看项目需求
        sharePic = user.headimgurl;
        //设置分享路径 带上当前用户ID
        if (user.user_id) {
          shareUrl = shareUrl + '&bid=' + user.user_id;
        }
        //loading resources...   
        load.init();
      } else {
        if (data.msg == 'invalid code') {
          //code无效错误--这个根据项目需求要跳转到哪里
          //location.href="index.html?bid="+bid+'&sltype='+sltype;
          location.href = "index.html";
          return;
        }
        alert(data.msg);
      }
    },
    error: function() {
      alert('网络信号不好,请刷新再试');
    }
  });
}
  • 用户加入活动 - 案例接口
function attend() {
  $.ajax({
    url: "http://liemo.webapp.163.com/user/attend",
    data: {
      'user_id': user.user_id,
      'token': user.weixin_token
    },
    async: false,
    dataType: "jsonp",
    success: function(result) {
      if (result.success) {
        //已加入活动,后台记录当前的用户已经加入该活动,相应会给TA进行一些功能的操作(视不同项目而不同)
        user.isact = true;
      } else {
        if (result.msg == 'invalid code') {
          location.href = "index.html";
          return;
        }
        dealError(result);
      }
    },
    error: function() {
      alert("网络信号不好,请刷新再试");
    }
  });
}
  • 用户投票(点踩or点赞) - 案例接口
//friend_id:被投票用户ID
//vote_type:投票类型 -1否定 1赞同
function vote(friend_id, vote_type) {
  $.ajax({
    url: "http://liemo.webapp.163.com/user/help_pick",
    data: {
      'user_id': user.user_id, //当前用户ID
      'token': user.weixin_token,
      'from_user_id': friend_id,
      'vote_type': vote_type
    },
    async: false,
    dataType: "jsonp",
    success: function(result) {
      if (result.success) {
        //投票成功
      } else {
        if (result.msg == 'invalid code') {
          location.href = "index.html";
          return;
        }
        dealError(result);
      }
    },
    error: function() {
      alert("网络信号不好,请刷新再试");
    }
  });
}
  • 获取某个用户的投票列表 - 案例接口
//uid-具体查看某个用户的投票列表
function getVoteList(uid){
  $.ajax({
    url: "http://liemo.webapp.163.com/user/score_info",
    data: {
      'user_id': user.user_id,
      'token': user.weixin_token,
      'view_user_id': uid
    },
    async: false,
    dataType: "jsonp",
    success: function(result) {
      if (result.success) {
        //投票列表
        var voteList = result.score_list;
        if (voteList.length > 0) {
          //遍历投票列表
          $("ul#firend-list").html('');
          $.each(voteList, function(index, item) {
            if(!item.headimgurl){
              item.headimgurl = defPic;
            }
            if(item.vote_type>0){
              if(item.nickname==user.nickname){
                var str = pageCtrl.support[item.vote_text_type-1];
                str = str.replace("TA", "你"); 
                $("ul#firend-list").append('<li><img src="'+item.headimgurl+'" alt="我" class="head-img"><span class="weixin-info"><span class="weixin-name">我</span> <span class="message">'+ str +'</span></span><span class="score" id="scoreY">+1</span></li>');
              }else{
                var str = pageCtrl.support[item.vote_text_type-1];
                $("ul#firend-list").append('<li><img src="'+item.headimgurl+'" alt="'+item.nickname+'" class="head-img"><span class="weixin-info"><span class="weixin-name">'+item.nickname+'</span> <span class="message">'+ str +'</span></span><span class="score" id="scoreY">+1</span></li>');
              }
            }else{
              if(item.nickname==user.nickname){
                var str = pageCtrl.opposition[item.vote_text_type-1];
                str = str.replace("TA", "你"); 
                $("ul#firend-list").append('<li><img src="'+item.headimgurl+'" alt="我" class="head-img"><span class="weixin-info"><span class="weixin-name">我</span> <span class="message">'+ str +'</span></span><span class="score" id="scoreN">-1</span></li>');
              }else{
                var str = pageCtrl.opposition[item.vote_text_type-1];
                $("ul#firend-list").append('<li><img src="'+item.headimgurl+'" alt="'+item.nickname+'" class="head-img"><span class="weixin-info"><span class="weixin-name">'+item.nickname+'</span> <span class="message">'+ str +'</span></span><span class="score" id="scoreN">-1</span></li>');
              }
            }
          });
        }else{
          $("ul#firend-list").html('<center style="color:#ffffff;line-height:40px;font-size:24px;padding-top:40px;">空空如也~<br>你赶紧支持TA吧~</center>');
        }
        setTimeout(function(){
          pageCtrl.fn.seeFriendList();
        }, 300);
      } else {
        if (result.msg == 'invalid code') {
            location.href = "index.html";
            return;
          }
          dealError(result);
      }
    },
    error: function() {
      alert("网络信号不好,请刷新再试");
    }
  });
}

第四步:调试代码

微信授权项目因为要放到FTP/nie_all/nie/weixin/上面才能真实体验。所以推荐用Fiddler2工具来调试,在本地模拟线上环境,调试成功后再发布到FTP上面。 移动端调试方法: http://res.nie.netease.com/comm/doc/tools/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E8%B0%83%E8%AF%95%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.html

反馈与建议

POPO:gzsuqiuhong@corp.netease.com


感谢阅读