微信开发之签名校验及获取openId

我们要用微信jsapi,以及获取用户openid,就要进行签名校验。

先捋一下应用jssdk的整个流程:

步骤一:绑定域名

先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。

备注:登录后可在“开发者中心”查看对应的接口权限。

步骤二:引入JS文件

在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js

备注:支持使用 AMD/CMD 标准模块加载方法加载

步骤三:通过config接口注入权限验证配置

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。

wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});

签名算法见文末的附录1,所有JS接口列表见文末的附录2

步骤四:通过ready接口处理成功验证

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

步骤五:通过error接口处理失败验证

wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});

步骤一,二,四,五这里不在说明,主要讲的是第三步,通过config接口注入权限验证配置。

首先,准备好认证过的微信服务号(认证请看我的另一篇博客“微信公众号认证及支付开通流程”),假设公众号appid为wx0123456789abcdef,公众号开发者密码AppSecret为12345678(32位)。

引用wechat.js文件,该文件为微信jssdk相关配置,getsign()方法是调用服务器get接口,jsApiList中是需要使用的js接口 列表

function getsign(){
    $.get('/signature?url='+window.location.href.split('#')[0],function(data){
       var json=JSON.parse(data);
       Timestamp=json.Timestamp;
       Signature=json.Signature;
       wx.config({
           beta:true,
           debug: true,
           appId: AppId, //'<%= AppId %>',
           timestamp: Timestamp, //'<%= Timestamp %>',
           nonceStr: Noncestr, //'<%= Noncestr %>',
           signature: Signature, //'<%= Signature %>',
           jsApiList: [
               'checkJsApi'
           ]
       });
       wx.ready(function() {
       });
      wx.error(function (res) {
         alert("调用微信jsapi返回的状态:"+res.errMsg);
      });
    });
}

下面是服务器端代码实现,这里我用的是node来实现的(还未入门),这里随机字符串应该是随机的,我懒省事就直接写了一个,SHA1加密代码是网上copy的

var http = require("http")  
var https=require("https")
var fs = require("fs")
var process=require("process");
var urllib = require('url');  
var jsapi_ticket='';
var access_token='';
https.createServer(function(req,res){
    var response=res;
    var path = urllib.parse(req.url);
    //2小时获取一次jsticket
    setInterval(function(){
        jsapi_ticket='';
        access_token='';
        if(path.pathname=='/signature'){
            //获取token
            gettoken(path,response);
        }else if(path.pathname == "/"){
            sendFile(res,"/join_cyiot.html")
        }else{
            sendFile(res,path.pathname)
        }
    },72000000)
    if(path.pathname=='/signature'){
        gettoken(path,response);
    }else if(path.pathname == "/"){
        sendFile(res,"/join_cyiot.html")
    }else if(path.pathname == "/getOpenId"){
        //根据code获取openid
        var code=path.query.substr(path.query.indexOf('code=')+5);
        var ip = req.headers['x-forwarded-for']||req.ip||req.connection.remoteAddress||req.socket.remoteAddress||eq.connection.socket.remoteAddress||'';
        if(ip.split(',').length>0){
            ip = ip.split(',')[0]
        }
        ip=ip.substr(ip.indexOf('f:')+2);
        https.get('https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx0123456789abcdef&secret=12345678&code='+code+'&grant_type=authorization_code',function(req,res){
            var openid='';
            req.on('data',function(data){
                openid+=data;
            });
            req.on('end',function(){
                var openidobj=JSON.parse(openid);
                openidobj.ip=ip;
                response.end(JSON.stringify(openidobj))
            })
        })
    }else{
        sendFile(res,path.pathname)
    } 
}).listen(8007);
function gettoken(path,response){
    https.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx0123456789abcdef&secret=12345678',function(req,res){  
        var jsondata='';  
        req.on('data',function(data){  
            jsondata+=data;  
        });  
        req.on('end',function(){
            var json=JSON.parse(jsondata);
            access_token=json.access_token;
            if(jsapi_ticket==''||access_token==''){
                //获取jsticket
                https.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+json.access_token+'&type=jsapi',function(req,res){  
                    jsondata='';  
                    req.on('data',function(data){  
                        jsondata+=data;  
                    });  
                    req.on('end',function(){
                        var jsonTicket= JSON.parse(jsondata); 
                        jsapi_ticket=jsonTicket.ticket;
                        //时间戳,秒
                        var Timestamp=parseInt(new Date().getTime()/1000);
                        var Noncestr="asdffdsadfasf";//随机字符串
                        //算签名并返回
                        var Signature=SHA2("jsapi_ticket="+jsonTicket.ticket+"&noncestr="+Noncestr+"&timestamp="+Timestamp+"&"+path.query);
                        response.end(JSON.stringify({Timestamp:Timestamp,Signature:Signature,jsonTicket:jsonTicket,url:path.query}));                      
                    });
                }); 
            }else{
                //时间戳,秒
                var Timestamp=parseInt(new Date().getTime()/1000);
                var Noncestr="asdffdsadfasf";//随机字符串
                //算签名并返回
                var Signature=SHA2("jsapi_ticket="+jsapi_ticket+"&noncestr="+Noncestr+"&timestamp="+Timestamp+"&"+path.query);
                response.end(JSON.stringify({Timestamp:Timestamp,Signature:Signature,jsonTicket:jsapi_ticket,url:path.query}));
            }          
        });

    });
}
function sendFile(res,path){  
    var path = process.cwd()+path;  
    fs.readFile(path,function(err,stdout,stderr){  
        if(!err){  
            var data = stdout;  
            var type = path.substr(path.lastIndexOf(".")+1,path.length)  
            res.writeHead(200,{'Content-type':"text/"+type});
            res.write(data);  
        }  
        res.end();  
    })  
}  
// SHA1  
function add(x, y) {  
    return((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) ^ (x & 0x80000000) ^ (y & 0x80000000);  
}  
  
function SHA1hex(num) {  
    var sHEXChars = "0123456789abcdef";  
    var str = "";  
    for(var j = 7; j >= 0; j--)  
        str += sHEXChars.charAt((num >> (j * 4)) & 0x0F);  
    return str;  
}  
  
function AlignSHA1(sIn) {  
    var nblk = ((sIn.length + 8) >> 6) + 1,  
        blks = new Array(nblk * 16);  
    for(var i = 0; i < nblk * 16; i++) blks[i] = 0;  
    for(i = 0; i < sIn.length; i++)  
        blks[i >> 2] |= sIn.charCodeAt(i) << (24 - (i & 3) * 8);  
    blks[i >> 2] |= 0x80 << (24 - (i & 3) * 8);  
    blks[nblk * 16 - 1] = sIn.length * 8;  
    return blks;  
}  
  
function rol(num, cnt) {  
    return(num << cnt) | (num >>> (32 - cnt));  
}  
  
function ft(t, b, c, d) {  
    if(t < 20) return(b & c) | ((~b) & d);  
    if(t < 40) return b ^ c ^ d;  
    if(t < 60) return(b & c) | (b & d) | (c & d);  
    return b ^ c ^ d;  
}  
  
function kt(t) {  
    return(t < 20) ? 1518500249 : (t < 40) ? 1859775393 :  
        (t < 60) ? -1894007588 : -899497514;  
}  
  
function SHA1(sIn) {  
    var x = AlignSHA1(sIn);  
    var w = new Array(80);  
    var a = 1732584193;  
    var b = -271733879;  
    var c = -1732584194;  
    var d = 271733878;  
    var e = -1009589776;  
    for(var i = 0; i < x.length; i += 16) {  
        var olda = a;  
        var oldb = b;  
        var oldc = c;  
        var oldd = d;  
        var olde = e;  
        for(var j = 0; j < 80; j++) {  
            if(j < 16) w[j] = x[i + j];  
            else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);  
            t = add(add(rol(a, 5), ft(j, b, c, d)), add(add(e, w[j]), kt(j)));  
            e = d;  
            d = c;  
            c = rol(b, 30);  
            b = a;  
            a = t;  
        }  
        a = add(a, olda);  
        b = add(b, oldb);  
        c = add(c, oldc);  
        d = add(d, oldd);  
        e = add(e, olde);  
    }  
    SHA1Value = SHA1hex(a) + SHA1hex(b) + SHA1hex(c) + SHA1hex(d) + SHA1hex(e);  
    return SHA1Value.toUpperCase();  
}  
  
function SHA2(sIn) {  
    return SHA1(sIn).toLowerCase();  
} 

根据请求返回的随机字符串,时间戳,加上签名和appid进行校验即可,整个流程不算复杂,但是自己做下来报了非常多次错误,大小写以及一些细节都要注意,格式与上述代码中一致即可。

获取openid的js代码

function getQueryString(name) {
                var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
                var r = window.location.search.substr(1).match(reg);
                if (r != null) return unescape(r[2]); return null;
            }
            var code = getQueryString("code");
            var redirecturl=encodeURIComponent('微信后台配置的授权域名') ;
            if(!code){
                window.location.href='https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx0123456789abcdef&redirect_uri='+redirecturl+'&response_type=code&scope=snsapi_base&state=0#wechat_redirect'
            }else{
                $.get('/getOpenId?code='+code+'',function(data){    
                    window.localStorage.setItem('openidobj',data);
                })
            }

上述方法中微信提供的获取code的接口中,scope参数为应用授权作用域,它的值有两种,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )

其他也就没啥了,按照微信官方文档,一步步细心点就可以了。

原文地址:https://www.cnblogs.com/xianghuali/p/8509411.html