Java实现微信分享自定义文案和图片

【链接】微信公众平台接口测试帐号申请

http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

首先获取access_token,然后获取jsapi_ticket,最后获取签名

jsapi_ticket

生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。

1.参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):获取access_token

2.用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

成功返回如下JSON:

{"errcode":0,"errmsg":"ok","ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA","expires_in":7200}

获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。

签名算法

签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。

即signature=sha1(string1)。 示例:

noncestr=Wm3WZYTPz0wzccnWjsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qgtimestamp=1414587457url=http://mp.weixin.qq.com?params=value

步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

步骤2. 对string1进行sha1签名,得到signature:

0f9de62fce790f9a083d5c99e95740ceb90c27ed

注意事项

1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

2.签名用的url必须是调用JS接口页面的完整URL。

3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

如出现invalid signature 等错误详见附录5常见错误及解决办法。

Java代码如下:

//需要准备APP_ID 和SECRET 
private static final String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
private static final String GET_SIGNATURE_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
private static final String APP_ID = "******";
private static final String SECRET = "*****";

//提供的主方法
    @RequestMapping(value = "/getSignature", method = RequestMethod.POST)
    @ResponseBody
    public Map WeixinController(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", "*");
        Map ret = new HashMap();
        //获取前台传来的三个参数
        String timestamp = request.getParameter("timestamp");
        String nonce_str = request.getParameter("nonce_str");
        String url = request.getParameter("url");
        System.out.println("url"+url+"==============="+nonce_str+"============"+timestamp);
        String accessToken = null;

//        accessToken = redisTemplate.opsForValue().get("accessToken")+"";
        accessToken =  this.getToken(GET_TOKEN_URL, APP_ID, SECRET);// 获取token
        String ticket = this.getTicket(GET_SIGNATURE_URL,accessToken);    // 获取ticket
        String signature = this.getSignature(ticket,url,nonce_str,timestamp); //获取签名

        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        return ret;
    }

工具类(获取token、ticket、sign方法)

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @FileName: WeChatSignUtil
 * @Description: 微信签名的工具类
 * @author: <a href="mailto: muyuanpei@camelotchina.com">myp</a>
 * @create: 2019-07-17 15:56
 * @Copyright: (c) 2018年 北京柯莱特科技有限公司
 */
@Slf4j
public class WeChatSignUtil {

    // 获取access_token
    public static String getToken(String apiurl, String appid, String secret){
        //拼接访问地址
        String turl = String.format("%s?grant_type=client_credential&appid=%s&secret=%s", apiurl,appid, secret);

        HttpClient client = new DefaultHttpClient();
        //get请求
        HttpGet get = new HttpGet(turl);
        // 初始化解析json格式的对象
        JsonParser jsonparer = new JsonParser();
        String result = null;
        try
        {
            HttpResponse res = client.execute(get);
            String responseContent = null; // 初始化响应内容
            HttpEntity entity = (HttpEntity) res.getEntity();
            //设置编码格式
            responseContent = EntityUtils.toString((org.apache.http.HttpEntity) entity, "UTF-8");
            // 将json字符串转换为json对象
            JsonObject json = jsonparer.parse(responseContent).getAsJsonObject();

            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
            {
                if (json.get("errcode") != null){
                    // 错误时微信会返回错误码等信息,{"errcode":40013,"errmsg":"invalid appid"}
                }
                else{
                    // 正常情况下{"access_token":"ACCESS_TOKEN","expires_in":7200}
                    result = json.get("access_token").getAsString();
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        finally{
            // 关闭连接 ,释放资源
            client.getConnectionManager().shutdown();
            return result;
        }
    }

    // 获取getTicket
    public static String getTicket(String apiurl,String access_token){

        String turl = String.format("%s?access_token=%s&type=jsapi",apiurl,access_token);

        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(turl);

        JsonParser jsonparer = new JsonParser();// 初始化解析json格式的对象
        String result = null;
        try
        {
            HttpResponse res = client.execute(get);
            String responseContent = null; // 响应内容
            HttpEntity entity = res.getEntity();
            responseContent = EntityUtils.toString(entity, "UTF-8");
            JsonObject json = jsonparer.parse(responseContent).getAsJsonObject();

            // 将json字符串转换为json对象
            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                if (json.get("errcode") == null){
                    // 错误时微信会返回错误码等信息,{"errcode":40013,"errmsg":"invalid appid"}
                }
                else{
                    // 正常情况下{"access_token":"ACCESS_TOKEN","expires_in":7200}
                    result = json.get("ticket").getAsString();
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            // 关闭连接 ,释放资源
            client.getConnectionManager().shutdown();
            return result;
        }
    }

    /**
     * <p>Discription: [生成签名]</p>
     *
     * @param ticket  jsapi_ticket
     * @param url url
     * @param nonce_str 随机字符串
     * @param timestamp 时间戳
     * @return 签名结果
     */
    public static String getSignature(String ticket,String url,String nonce_str,String timestamp) {
        Map<String, String> signParams = new HashMap<>();
        signParams.put("noncestr",nonce_str);
        signParams.put("jsapi_ticket",ticket);
        signParams.put("timestamp",timestamp);
        signParams.put("url", url);
        // 对请求参数排序,并生成签名原文
        String original = sort(signParams);
        // 使用sha1生成签名
        String signStr = DigestUtils.shaHex(original);
        return signStr;
    }

    /***
     *  MD5
     * @param original 原文
     * @return 哈希结果
     */
    private static String md5(String original) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest messageDigest;
        messageDigest = MessageDigest.getInstance("MD5");
        messageDigest.update(original.getBytes("UTF-8"));
        return byte2hex(messageDigest.digest());
    }

    /***
     *  sha1
     * @param original 原文
     * @return 加密结果
     */
    private static String sha1(String original){
        String sign = DigestUtils.shaHex(original);
        return sign;
    }

    /**
     * <p>Discription: [转换为16进制]</p>
     *
     * @param bytes 二进制
     * @return 16进制
     */
    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }

    /**
     * <p>Discription: [字典排序]</p>
     *
     * @param params 参数集合
     * @return 排序后的字符串,格式为:a=value1&b=value2&Secret
     */
    private static String sort(Map<String, String> params) {
        String[] keys = params.keySet().toArray(new String[0]);
        Arrays.sort(keys);

        StringBuilder query = new StringBuilder();
        for (String key : keys) {
            if (query.length() > 0) {
                query.append("&");
            }

            String value = params.get(key);
            query.append(key).append("=").append(value);
        }
        return query.toString();
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容