h5微信登陆、分享、人肉排雷

背景:

微信中打开h5页面,获取用户信息,分享(利用微信jdk,支持分享图片与title的设置)到微信朋友圈与好友。

微信文档

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

1- 接入微信公众平台

登录微信公众平台官网后,在公众平台官网的开发-基本设置页面,勾选协议成为开发者,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。

后台java代码

@RequestMapping("/wechat")
@Controller
public class WechatController {

    private static Logger logger = LoggerFactory.getLogger(WechatController.class);

    private static String token = "shudanlaile2017";

    @RequestMapping(value = "/check")
    public void get(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("WechatController check start ");
        Enumeration pNames = request.getParameterNames();
        while (pNames.hasMoreElements()) {
            String name = (String) pNames.nextElement();
            String value = request.getParameter(name);
            logger.info("name {} value {}",name,value);
        }
        if (StringUtils.isNotBlank(request.getParameter("signature"))) {
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            logger.info("signature{}, timestamp{}, nonce{}, echostr{}", signature, timestamp, nonce, echostr);
            if (SignUtil.checkSignature(signature, timestamp, nonce)) {
                logger.info("数据源为微信后台,将echostr {} 返回", echostr);
                response.getOutputStream().println(echostr);
            }
        }

    }
}

必须把后台的服务搭建起来,开起来,然后再去配置

 

2- 设置网页授权

在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;

授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com无法进行OAuth2.0鉴权

如果是tomcat 容器,把这个文件下载并放在

/home/duke/apache-tomcat-8.5.28/webapps/ROOT
[root@cache ROOT]# ls
MP_verify_6CvHRorvdr0vv2pC.txt

如果是nginx容器

放在

/nginx/html 下面

为了后续开发,可以直接把这些域名授权配置好

 

至此可以进行获取用户信息功能的开发了,注意网站必须搭建在上面配置的授权域名下才行

前端引导用户授权,授权后把code传给后台服务,后台服务根据code 获取用户信息

后台代码

public class WxService {
    private  String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
    private  String userInfoURL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

    private static String APPID = "你的appid";
    private static String SECRET = "你的secret";
    private static Logger logger = LoggerFactory.getLogger(WxService.class);
    RedisProxy proxy_h5 = RedisProxy.getRedisClustor(H5Constants.h5_redis_clusterId);


    public static void main(String[] args) {
        WxService service=new WxService();
      // UserInfoModel model= service.getUserInfoModelBYcode("071vnQRP1uYTQ11rcYPP11DyRP1vnQR8");
       String s1= service.getRankPersent("ofOVy1L0NTyy4BbPmNv4MP2mW5Sw");
        System.out.println("aaa");

    }


    public UserInfoModel getUserInfoModelBYcode(String code) {
        String url = URL.replaceAll("APPID", APPID).replaceAll("CODE", code).replaceAll("SECRET", SECRET);
        logger.info("getUserInfoModelBYcode url {} ",url);
        String s_token = HttpUtil.doGet(url);
        logger.info("getUserInfoModelBYcode s_token {}",s_token);
        Object o = JSON.parseObject(s_token);
        String ACCESS_TOKEN = ((JSONObject) o).getString("access_token");
        String OPENID = ((JSONObject) o).getString("openid");
        String  userInfourl = userInfoURL.replaceAll("ACCESS_TOKEN", ACCESS_TOKEN).replaceAll("OPENID", OPENID);
        String s_user = HttpUtil.doGet(userInfourl);
        logger.info("getUserInfoModelBYcode userInfoURL {}",userInfourl);
        s_user= changeCharset(s_user,"ISO_8859_1","UTF-8");
        logger.info("getUserInfoModelBYcode s_user {}",s_user);
        UserInfoModel model = transfer2UserInfoModel(s_user);
        return model;
    }

    private UserInfoModel transfer2UserInfoModel(String userInfoMap) {
        logger.info("transfer2UserInfoModel {} ",userInfoMap);
        Object o = JSON.parseObject(userInfoMap);
        UserInfoModel model = new UserInfoModel();
        model.setOpenid(((JSONObject) o).getString("openid"));
        model.setCity(((JSONObject) o).getString("city"));
        model.setCountry(((JSONObject) o).getString("country"));
        model.setHeadimgurl(((JSONObject) o).getString("headimgurl"));
        model.setNickname(((JSONObject) o).getString("nickname"));
        model.setPrivilege(((JSONObject) o).getString("privilege"));
        model.setSex(((JSONObject) o).getString("sex"));
        model.setUnionid(((JSONObject) o).getString("unionid"));
        model.setLanguage(((JSONObject) o).getString("language"));
        model.setProvince(((JSONObject) o).getString("province"));
        return model;
    }


    public String getRankPersent(String openid) {

        long all = proxy_h5.zcount(H5Constants.h5_all_user_map_list, 0, 999999999);
        long rank = all;
        try {
            rank = proxy_h5.zrevrank(H5Constants.h5_all_user_map_list, openid);
        } catch (Exception e) {
            rank = all;
            e.printStackTrace();
        }
        if (rank == 0) {
            return "100";
        }
        if (all == 1) {
            return "100";
        }
        if (rank>=all){
            return "10";
        }
        long res = (all - rank-1) * 100 / (all);
        return res + "";

    }

    public String changeCharset(String str, String sourceCharset, String targetCharset)  {
        if (str == null) {
            return null;
        }
        //用旧的字符编码解码字符串。解码可能会出现异常。
        byte[] bs = new byte[0]; //用新的字符编码生成字符串
        try {
            bs = str.getBytes(sourceCharset);
            return new String(bs, targetCharset);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            logger.error("changeCharset {} ",e.toString());
            return null;
        }

    }


}
public class UserInfoModel {
   // openid,nickname,sex,province,city,country,headimgurl,privilege,unionid,language,persent
    @RedisColumn(isRowkey =true,isInversion = true)
    private String openid;  //用户的唯一标识
    @RedisColumn
    private String  nickname;   //用户昵称
    @RedisColumn
    private String  sex;    //用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
    @RedisColumn
    private String  province;   //用户个人资料填写的省份
    @RedisColumn
    private String  city;   //普通用户个人资料填写的城市
    @RedisColumn
    private String  country;    //国家,如中国为CN
    @RedisColumn
    private String  headimgurl;//   用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
    @RedisColumn
    private String  privilege;  //用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
    @RedisColumn
    private String  unionid;    //只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
    @RedisColumn
    private String  language;   //
    @RedisColumn
    private String  persent;    //

    public UserInfoModel(){}

    public UserInfoModel(String openid, String nickname, String sex, String province, String city, String country, String headimgurl, String privilege, String unionid) {
        this.openid = openid;
        this.nickname = nickname;
        this.sex = sex;
        this.province = province;
        this.city = city;
        this.country = country;
        this.headimgurl = headimgurl;
        this.privilege = privilege;
        this.unionid = unionid;
    }

    @Override
    public String toString() {
        return "UserInfoModel{" +
                "openid='" + openid + '\'' +
                ", nickname='" + nickname + '\'' +
                ", sex='" + sex + '\'' +
                ", province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", country='" + country + '\'' +
                ", headimgurl='" + headimgurl + '\'' +
                ", privilege='" + privilege + '\'' +
                ", unionid='" + unionid + '\'' +
                '}';
    }

    public String getPersent() {
        return persent;
    }

    public void setPersent(String persent) {
        this.persent = persent;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getOpenid() {
        return openid;
    }

    public void setOpenid(String openid) {
        this.openid = openid;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getHeadimgurl() {
        return headimgurl;
    }

    public void setHeadimgurl(String headimgurl) {
        this.headimgurl = headimgurl;
    }

    public String getPrivilege() {
        return privilege;
    }

    public void setPrivilege(String privilege) {
        this.privilege = privilege;
    }

    public String getUnionid() {
        return unionid;
    }

    public void setUnionid(String unionid) {
        this.unionid = unionid;
    }
}

3- 调用微信的分享功能

微信文档

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

在微信内打开H5,可以直接调用微信浏览器的的分享按钮,不过分享出去的标题/图片是默认抓取的,效果很差,需要调用其分享朋友圈、好友的功能设置title、图片,则需要以下操作(这是坑最多的地方)

步骤一:绑定域名

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

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

前端的代码是

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

 

后台代码,问题最多的地方就是这里

config:fail,Error: invalid url domain

等错误是因为 上面的 安全域名 js域名没有设置或错误,请检查一下

最蛋疼的错误是这个

config:fail,Error: invalid signature

签名非法,但是并不提示哪里非法, 以下 是常规的网上看到的解决方案,可是我全部试了一遍,还是报错

如果是invalid signature签名错误。建议按如下顺序检查:

1.确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。

2.确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。

3.确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。

4.确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。

5.确保一定缓存access_token和jsapi_ticket。
这个是重点:
确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

教如何验证是否正确方法:
url动态获取的方法是:

  1. $protocol = (!empty($_SERVER[HTTPS]) && $_SERVER[HTTPS] !== off || $_SERVER[SERVER_PORT] == 443) ? "https://" : "http://";
  2.   $url = $protocol.$_SERVER[HTTP_HOST].$_SERVER[REQUEST_URI];

复制代码

步骤:
首先你在页面alert(location.href.split('#')[0]);
然后你再打印出动态获取的url是否和你alert的地址是否一样。一定要一模一样,包括大小写。
如果发现不一样,那就按照自己的需求改。反正要一样。验证签名一定可以通过的。

 

在解决这个问题的过程中,因为我搭建的是nginx 的负载均衡,整个集群有三台机器,之前以为是集群的问题,研究了很久,并且尝试过切到单点模式,不过最终确定,nginx 集群并不影响这里的签名校验。

我这里签名校验失败是因为两个问题,第一,通过

access_token 获取
jsapi_ticket 过程中,服务器没有设置白名单,获取失败获取错误的数据

需要在安全中心-白名单,添加后台服务器的外网ip

 

第二,是前端传入的url 错误,前端调用微信sdk时候,微信后台会根据其标准获取url计算签名,与你传入的签名比对,两处的url需要一致,下面贴出后台与前端的调试通过的代码

前端代码

这里的 时间戳、随机串需要与后台保持一致,签名需要后台获取

function wxShare() {
    let data = {
        url:"https://域名/booklist/index.html"
    }
    axios.post("https://域名/h5/wechat/signature",qs.stringify(data))
    .then(res =>{
        wx.config({
            debug: false, // 开启调试模式,
            appId: res.data.appid, // 必填,企业号的唯一标识,此处填写企业号corpid
            timestamp: res.data.timestamp, // 必填,生成签名的时间戳
            nonceStr: res.data.noncestr, // 必填,生成签名的随机串
            signature: res.data.sgture, // 必填,签名,见附录1
            jsApiList: [
            "checkJsApi",
            "onMenuShareTimeline",
            "onMenuShareAppMessage",
            "onMenuShareQQ",
            "onMenuShareWeibo",
            "onMenuShareQZone",
            "hideMenuItems",
            "showMenuItems",
            "hideAllNonBaseMenuItem",
            "showAllNonBaseMenuItem",
            "translateVoice",
            "startRecord",
            "stopRecord",
            "onVoiceRecordEnd",
            "playVoice",
            "onVoicePlayEnd",
            "pauseVoice",
            "stopVoice",
 "uploadVoice",
            "downloadVoice",
            "chooseImage",
            "previewImage",
            "uploadImage",
            "downloadImage",
            "getNetworkType",
            "openLocation",
            "getLocation",
            "hideOptionMenu",
            "showOptionMenu",
            "closeWindow",
            "scanQRCode",
            "chooseWXPay",
            "openProductSpecificView",
            "addCard",
            "chooseCard",
            "openCard"
            ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
        });
          //处理验证失败的信息
        wx.error(function(res) {
            // alert('验证失败返回的信息:',res)
            console.log("验证失败返回的信息:", res);
            // alert(res)
        });
        wx.ready(function() {
            // alert('签名成功')
            //需在用户可能点击分享按钮前就先调用
            // 自定义“分享给朋友”及“分享到QQ”按钮的分享内容
            wx.onMenuShareAppMessage({
            title: "2019年你打算读多少书?列出你的阅读计划吧!", // 分享标题
            desc: "生成你的2019年阅读计划", // 分享描述
            link: window.location.href, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
            imgUrl:
                "https://域名/images/logo2.png", // 分享图标
            success: function(res) {
                // 设置成功
                console.log('分享给朋友成功')
            },
            cancel: function(res) {
 // alert('用户分享失败',res)
                console.log('用户分享失败')
            }
            });

            // 分享到朋友圈
            wx.onMenuShareTimeline({
            title: "2019年你打算读多少书?列出你的阅读计划吧", // 分享标题
            desc: "生成你的2019年阅读计划", // 分享描述
            link: window.location.href, // 分享链接
            imgUrl:"https://域名/images/logo2.png",
            success: function() {
                console.log("已分享到朋友圈");
            },
            cancel: function() {
                console.log('用户取消分享')
            },
            fail: function(res) {
                console.log(JSON.stringify(res));
                 }
            });
        });
    })
    .catch(err => {

    })
}

 后台java代码

 

  @RequestMapping("/signature")
    @ResponseBody
    public Map<String, Object> signature(HttpServletRequest request) {
        String strUrl=request.getParameter("url");
    //  String  strUrl="https://域名/index.html";
        WinXinEntity wx = WeiXinUnitl.getWinXinEntity(strUrl);
        logger.info("signature url {}  wx {}",strUrl,wx.toString());
        // 将wx的信息到给页面
        Map<String, Object> map = new HashMap<String, Object>();
        String sgture = WXUnitl.getSignature(wx.getTicket(), wx.getNoncestr(), wx.getTimestamp(), strUrl);
        map.put("sgture", sgture.trim());//签名
        map.put("timestamp", wx.getTimestamp().trim());//时间戳
        map.put("noncestr",  wx.getNoncestr().trim());//随即串
        map.put("appid","appID");//appID
        logger.info("signature map {} ",map);
        return map;







public class WeiXinUnitl {
    private static Logger logger = LoggerFactory.getLogger(WeiXinUnitl.class);
    public static void main(String[] args) {
        WinXinEntity wx=  getWinXinEntity("https://域名/index.html");
        System.out.println("111");
        WinXinEntity wx2=  getWinXinEntity("https://域名/index.html");
        System.out.println("111");

    }

    public static WinXinEntity getWinXinEntity(String url) {
        WinXinEntity wx = new WinXinEntity();
        String access_token ="";
        String jsapi_ticket=(String) H5GuavaCache.get(Constant.ticket_guava_key);

        if (jsapi_ticket==null || jsapi_ticket.equals("") || jsapi_ticket.length()<20){
            access_token = getAccessToken();
            jsapi_ticket = getTicket(access_token);
            H5GuavaCache.put(Constant.ticket_guava_key,jsapi_ticket);
            logger.info("getWinXinEntity access_token {} jsapi_ticket  {}  ",access_token,jsapi_ticket);
        }else {
            logger.info("getWinXinEntity ticket {} ",jsapi_ticket);
        }
        Map<String, String> ret = Sign.sign(jsapi_ticket, url);
        wx.setTicket(ret.get("jsapi_ticket"));
        wx.setSignature(ret.get("signature"));
        wx.setNoncestr(ret.get("nonceStr"));
        wx.setTimestamp(ret.get("timestamp"));
        wx.setAccess_token(access_token);
//
        return wx;
    }



    // 获取token
    private static String getAccessToken() {
        String access_token = "";
        String grant_type = "client_credential";//获取access_token填写client_credential
        String AppId="appid";//第三方用户唯一凭证
        String secret="secret";//第三方用户唯一凭证密钥,即appsecret
        //这个url链接地址和参数皆不能变

        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type="+grant_type+"&appid="+AppId+"&secret="+secret;  //访问链接

        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // 必须是get方式请求
            http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            http.connect();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes);
            JSONObject demoJson = JSONObject.parseObject(message);
            access_token = demoJson.getString("access_token");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return access_token;
    }

    // 获取ticket
    private static String getTicket(String access_token) {
        String ticket = null;
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi";// 这个url链接和参数不能变
        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // 必须是get方式请求
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
            http.connect();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            String message = new String(jsonBytes, "UTF-8");
            JSONObject demoJson = JSONObject.parseObject(message);
            ticket = demoJson.getString("ticket");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ticket;
    }
}





public class Sign {


    public static Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String string1;
        String signature = "";

        // 注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url;

        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature=  WXUnitl.getSignature(jsapi_ticket, nonce_str, timestamp, url);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }

    // 生成签名
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    // 生成nonce_str
    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    // 生成timestamp
    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
}

public class WXUnitl {
    public static void main(String[] args) {

        //String s="jsapi_ticket=HoagFKDcsGMVCIY2vOjf9qACR0jcC9h3eaF16vvg8i76KDwtaulU67WbpFYyDgJL72UZZLn8JwBuKDwExrQgBQ&noncestr=359ba6f4-35e8-48d8-9d0e-d72175239174&timestamp=1551669217&url=https%3A%2F%2Factivity.dookbook.com%2Fbooklist%2F";

    String s=  getSignature("","","","");
        System.out.println();
    }

    public static String getSignature(String jsapi_ticket, String nonce_str, String timestamp, String url) {
        // 注意这里参数名必须全部小写,且必须有序
//        String string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url;
        String string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + url;
/
        String signature = "";
        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return signature;
    }

    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

}


public class WinXinEntity {
    private String access_token;
    private String ticket;
    private String noncestr;
    private String timestamp;
    private String str;
    private String signature;
    get set
}





 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值