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

2019-07-17  本文已影响0人  初心myp

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

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();
    }

}
上一篇下一篇

猜你喜欢

热点阅读