• 002-JS-SDK开发使用,网页获取授权,扫一扫调用


    一、概述

    在申请响应的公众号之后,实名认证或者企业认证之后,可以进行对应开发

    二、开发步骤

    2.1、开发前提【服务号】-域名设置

      登录后台之后→左侧设置→公众号设置→功能设置,设置好“JS接口安全域名","网页授权域名"对应域名

      

    2.2、开发前提【服务号】-开发者ID设置

      登录后台之后→左侧开发→基本配置→公众号开发信息,设置好“开发者密码(AppSecret)”

    2.3、查看接口信息

      登录后台之后→左侧开发→接口权限→查看已开通的接口授权

    三、调用示例

    JS-SDK

      接口列表:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115 附录2

      详情页:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432

    3.1、示例一、微信网页授权【地址

      1 第一步:用户同意授权,获取code

      2 第二步:通过code换取网页授权access_token

      3 第三步:刷新access_token(如果需要)

      4 第四步:拉取用户信息(需scope为 snsapi_userinfo)

      5 附:检验授权凭证(access_token)是否有效

      1》整体流图

        

      2》详细参看地值

        微信网页授权【地址

    3.2、示例二、微信JS-SDK说明文档【地址 或者地址2

      JSSDK使用步骤【原文地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
        步骤一:绑定域名
          先登录微信公众平台进入“公众号设置”的“功能设置”里填写“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接口列表
    });
    View Code

          签名算法地址,支持方法列表接口

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

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

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

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

      接口调用说明
      所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:

        1.success:接口调用成功时执行的回调函数。
        2.fail:接口调用失败时执行的回调函数。
        3.complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
        4.cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
        5.trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。

        备注:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。
        以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:
        调用成功时:"xxx:ok" ,其中xxx为调用的接口名
        用户取消时:"xxx:cancel",其中xxx为调用的接口名
        调用失败时:其值为具体错误信息

    3.3、示例三、根据2.5.5 以扫一扫为例:

      1》服务器端设置js域名

      2》html页面引用相关js  

    <script src="jquery.min.js" type="text/javascript"></script>
    <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

      3》界面调用

    <input type="button" value="扫一扫" id="btnScanCode" style=" 80px"/>

      4》相关JS代码

    jQuery(document).ready(function () {
        var getWechatSignUrl = "http://test.com/openapi/wechat/getJsApiSign';
    
        // 获取微信签名
        $.ajax({
            url: getWechatSignUrl + "?url=" + encodeURIComponent(window.location.href.split('#')[0]),
            success: function (o) {
                console.log(o);
                if (o.code == 20000) {
                    wxConfig(o.data.appId, o.data.timestamp, o.data.nonceStr, o.data.signature);
                }
            }, error: function (o) {
                alert("出错了" + JSON.stringify(o));
            }
        });
        function wxConfig(_appId, _timestamp, _nonceStr, _signature) {
            var mm = '获取数据:' + encodeURIComponent(window.location.href.split('#')[0])
                + '
    ' + _appId + '
    ' + _timestamp + '
    ' + _nonceStr + '
    ' + _signature;
            console.log(mm);
            wx.config({
                debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                appId: _appId, // 必填,公众号的唯一标识
                timestamp: _timestamp, // 必填,生成签名的时间戳
                nonceStr: _nonceStr, // 必填,生成签名的随机串
                signature: _signature,// 必填,签名
                jsApiList: ["scanQRCode"] // 必填,需要使用的JS接口列表
            });
        }
    
        function scanCode() {
            wx.scanQRCode({
                needResult: 1,
                scanType: ["qrCode", "barCode"],
                success: function (res) {
                    var result = res.resultStr;
                    alert(result);
                },
                fail: function (res) {
                    console.log(res)
                    alert(JSON.stringify(res));
                }
            });
        }
    
        //客户端扫码
        $("#btnScanCode").click(function () {
            scanCode();
        });
    });
    View Code

      5》服务器端接口设计

        全局响应枚举类    

    public enum ResponseEnum {
        //账户类异常10000
        USEREXCEPTION(10000, "账户异常"),
    
        SUCCESS(20000, "操作成功"),
        //参数类异常 30000
        NOTFOUND(30000, "必输参数为空"),
        INPUTERROR(30001, "输入参数格式错误"),
        DATEERROR(30002, "日期区间错误"),
        DATEOUTOF(30003, "日期区间超出范围"),
    
        //系统类未知异常 99999
        ERROR(99999, "系统异常"),;
    
        private int code;
        private String msg;
    
        ResponseEnum(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        @Override
        public String toString() {
            return String.format("[%s:%s]", code, msg);
        }
    }
    View Code

        全局响应类

    public class ResponseResult<T> {
    
        /**
         * 方法返回状态码
         */
        private int code;
    
        /**
         * 方法返回信息
         */
        private String msg;
    
        private T data;
    
        public ResponseResult(){
            setResponseEnum(ResponseEnum.SUCCESS);}
    
        public ResponseResult(ResponseEnum enums){
            setResponseEnum(enums);
        }
    
        public ResponseResult(ResponseEnum enums, T data){
            setResponseEnum(ResponseEnum.SUCCESS);
            this.data = data;
        }
    
        public ResponseResult(T data){
            this(ResponseEnum.SUCCESS,data);
        }
    
    
        public void setResponseEnum(ResponseEnum enums){
            this.code = enums.getCode();
            this.msg = enums.getMsg();
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMsg() {
            return msg;
        }
    
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    View Code

        jsapi结果类

    public class JsApiPageDomain {
        private String appId; // 必填,公众号的唯一标识
        private long timestamp; // 必填,生成签名的时间戳
        private String nonceStr; // 必填,生成签名的随机串
        private String signature;// 必填,签名
        private String[] jsApiList;// 必填,需要使用的JS接口列表
    
        public String getAppId() {
            return appId;
        }
    
        public void setAppId(String appId) {
            this.appId = appId;
        }
    
        public long getTimestamp() {
            return timestamp;
        }
    
        public void setTimestamp(long timestamp) {
            this.timestamp = timestamp;
        }
    
        public String getNonceStr() {
            return nonceStr;
        }
    
        public void setNonceStr(String nonceStr) {
            this.nonceStr = nonceStr;
        }
    
        public String getSignature() {
            return signature;
        }
    
        public void setSignature(String signature) {
            this.signature = signature;
        }
    
        public String[] getJsApiList() {
            return jsApiList;
        }
    
        public void setJsApiList(String[] jsApiList) {
            this.jsApiList = jsApiList;
        }
    }
    View Code

        获取getJsApiSign

        @RequestMapping(value = "/getJsApiSign", method = RequestMethod.GET)
        @ResponseBody
        public ResponseResult<JsApiPageDomain> getJsApiSign(String url) {
            ResponseResult<JsApiPageDomain> result = new ResponseResult<>();
            JsApiPageDomain jsApiPageDomain = null;
            try {
                //返回页面签名等信息
                //时间戳
                long timeStamp = System.currentTimeMillis()/1000L;
                //字母数字随机串
                String nonceStr = RandomStringUtils.randomAlphanumeric(16);
                //获取JsApITicket
                String jsApiTicket = weChatUtil.getJsApiTicket();
                jsApiPageDomain = new JsApiPageDomain();
                jsApiPageDomain.setAppId(globalConfig.getWeChatAppID());
                jsApiPageDomain.setNonceStr(nonceStr);
                jsApiPageDomain.setTimestamp(timeStamp);
                jsApiPageDomain.setSignature(weChatUtil.signature(nonceStr, jsApiTicket, timeStamp, url));
                jsApiPageDomain.setJsApiList(new String[]{"scanQRCode"});
                result.setData(jsApiPageDomain);
            } catch (Exception e) {
                logger.error("getJsApiSign请求异常", e);
            }
            return result;
        }
    View Code

        获取基础access_token【参看地址

        /**
         * 获取access_token
         *
         * @return
         */
        public String getAccessToken() {
            //redis读取
            if (RedisUtil.exist(interfaceAccessTokenKey)) {
                String redis = RedisUtil.findValueFromRedis(interfaceAccessTokenKey);
                if (StringUtils.isNotBlank(redis)) {
                    return redis;
                }
            }
            //执行调用
            ResponseEntity<InterfaceAccessToken> entity = restTemplate.getForEntity(globalConfig.getWechatInterfaceAccessToken(), InterfaceAccessToken.class);
            if (entity != null && entity.getStatusCode() == HttpStatus.OK) {
                if (entity.getBody() != null&&StringUtils.isNotBlank(entity.getBody().getAccess_token())) {
                    RedisUtil.saveToRedis(interfaceAccessTokenKey, entity.getBody().getAccess_token());
                    int tmp = entity.getBody().getExpires_in() - (60 * 5);
                    RedisUtil.setExpire(interfaceAccessTokenKey, tmp>0?tmp:1, TimeUnit.SECONDS);
                    return entity.getBody().getAccess_token();
                }
            }
            return null;
        }
    View Code

        获取jsapi_ticket【参看地址 附录1】

        /**
         * 获取jsapi_ticket
         *
         * @return
         */
        public String getJsApiTicket() {
            String token = getAccessToken();
            if (StringUtils.isBlank(getAccessToken())) {
                return null;
            }
    
            //redis读取
            if (RedisUtil.exist(jsapiTicketKey)) {
                String redis = RedisUtil.findValueFromRedis(jsapiTicketKey);
                if (StringUtils.isNotBlank(redis)) {
                    return redis;
                }
            }
    
            String apiTicket = MessageFormat.format(globalConfig.getWechatJsApiTicket(), token);
            //执行调用
            ResponseEntity<JsApiTicket> entity = restTemplate.getForEntity(apiTicket, JsApiTicket.class);
            if (entity.getStatusCode() == HttpStatus.OK) {
                if (entity.getBody() != null&&StringUtils.isNotBlank(entity.getBody().getTicket())) {
                    RedisUtil.saveToRedis(jsapiTicketKey, entity.getBody().getTicket());
                    //过期时间
                    Long ttl = RedisUtil.ttl(interfaceAccessTokenKey);
                    if (ttl != null && (ttl.longValue() - 1 > 0)) {
                        RedisUtil.setExpire(jsapiTicketKey, ttl.longValue() - 1, TimeUnit.SECONDS);
                    } else {
                        getJsApiTicket();
                    }
                    return entity.getBody().getTicket();
                }
            }
            return null;
        }
    View Code

        注意 基础access_token 和 jsapi_ticket 使用为redis缓存,同时具有相同的失效时间

        签名

        public String signature(String noncestr, String jsapiTicket, long timestamp, String url) {
            TreeMap<String, String> map = new TreeMap<>();//以后扩展方便
            map.put("noncestr", noncestr);
            map.put("jsapi_ticket", jsapiTicket);
            map.put("timestamp", String.valueOf(timestamp));
            map.put("url", url);
            
            StringBuilder sb = new StringBuilder();
            map.forEach((k, v) -> {
                sb.append(k + "=" + v + "&");
            });
            String msg = sb.toString().substring(0, sb.length() - 1);
            System.out.println("加密信息:" + msg);
            //签名
            MessageDigest m = null;
            try {
                m = MessageDigest.getInstance("SHA-1");
                m.update(msg.getBytes("UTF8"));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            byte s[] = m.digest();
            return Hex.encodeHexString(s);
        }
    View Code

        

      

  • 相关阅读:
    美联储主席和欧洲央行说了什么
    12月CPI,PPI有哪些变化
    中国人民银行行长易纲就贯彻落实中央经济工作会议精神接受采访谈
    2018年个人的一些简单预测
    从首套房利率走势看市场
    百城价格房价周期和郑州、武汉房价比较分析
    国际非农超预期美联储主席态度软化,国内适度宽松货币+积极财政仍是主基调
    三大经济体年2018年末形势一览
    从房地产住宅销售面积增速看房地产行业
    枚举类
  • 原文地址:https://www.cnblogs.com/bjlhx/p/8611021.html
Copyright © 2020-2023  润新知