Spring boot +vue实现微信的分享

微信的分享

准备工作

申请配置微信公众号平台中的JS接口安全域名和网页授权域名,填写你得域名,例如:http://baidu.com/ 这种格式的 后面不要带参数之内的
在这里插入图片描述
还有在开发–>基本配置中将你的服务器设置成ip白名单;还有设置开发者密码,开发时需要用到的
在这里插入图片描述

后台代码

啥也不说了,直接上代码了。有兴趣的朋友可以去看看官方文档

package com.tcm.Wechat.controller;

import com.alibaba.fastjson.JSONObject;
import com.tcm.Wechat.util.ASEUtil;
import com.tcm.Wechat.util.HttpGet;
import com.tcm.Wechat.util.Parm;
import com.tcm.common.util.ObjectParserUtile;
import com.tcm.common.util.RedisUtil;
import org.jboss.logging.Logger;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/weiXin")
public class WeiXinController {

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

    @Resource
    private RedisUtil redisUtil;

    // 获取微信的openid
    @PostMapping("/wxOpeid")
    public Map WxOpenid(@RequestBody String code) throws Exception {
        Map params = new HashMap();
        Map map = new HashMap<>();
        // 微信公众号平台的 开发者密码
        params.put("secret", Parm.APPSECRET);
        //  微信公众号平台的 开发者id
        params.put("appid", Parm.APPID);
        // 获取access_token填写client_credential
        params.put("grant_type", "client_credential");
        // code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
        params.put("code", code.subSequence(0, code.length() - 1));
        System.out.println(params);
        // 获取用户的openid
        String result = HttpGet.httpRequestToString(
                "https://api.weixin.qq.com/sns/oauth2/access_token", params);
        System.out.println("result>>>>>" + result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        if (jsonObject.get("openid").toString() != null) {
            // 加密openid 加密方式随意的,加密是因为分享链接中的openid会传播出去 所以需要加密一下
            String openid = ASEUtil.AESencrypt(jsonObject.get("openid").toString());
            logger.info("openid>>>>>:"+openid);
            map.put("openid", openid);
            map.put("status", 200);
        } else {
            map.put("openid", null);
            map.put("status", 500);
        }
        return map;
    }

    /**
     * 第一步:用户同意授权,获取code(引导关注者打开如下页面:)
     * 获取 code、state
     */
    @PostMapping("/code")
    public static String getStartURLToGetCode() {
        Map<String, String> params = new HashMap<String, String>();
        params.put("redirect_uri", Parm.REDIRECT_URI);
        params.put("appid", Parm.APPID);
        params.put("response_type", "code");
        // 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
        params.put("scope", "snsapi_base ");
        // 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
        params.put("state", "STATE#wechat_redirect");
        //  调起授权页面
        String result = HttpGet.httpRequestToString("https://open.weixin.qq.com/connect/oauth2/authorize", params);
        System.out.println(result);
        return result;
    }

    // 获取微信token
    @PostMapping("/access_token")
    public String access_Token() {
        Map<String, String> params = new HashMap<String, String>();
        // 判断是否在redis缓存中 存在  false 不存在 true 存在
        boolean access_Token = redisUtil.hashKey("access_Token");
        System.out.println(access_Token);
        if (access_Token == false) {
            // 开发者密码
            params.put("secret", Parm.APPSECRET);
            // 开发者id
            params.put("appid", Parm.APPID);
            // 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
            params.put("grant_type", "client_credential");
            // 获取token 两个小时过期
            String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/token", params);
            System.out.println("result>>>>>>:" + result);
            JSONObject jsonObject = JSONObject.parseObject(result);
            System.out.println("jsonObject:>>>>" + jsonObject);
            String token = jsonObject.get("access_token").toString();
            System.out.println("token:>>>>" + token);
            // 放入数据时设置redis缓存过期时间  一个半小时
            redisUtil.set("access_Token", token, 5400);
            return token;
        } else {
            return redisUtil.get("access_Token").toString();
        }
    }

    // 获取微信ticket
    @PostMapping("/get_ticket")
    public String getTicket() {
        // 判断是否在redis缓存中 存在  false 不存在 true 存在
        boolean access_Token = redisUtil.hashKey("get_ticket");
        if (access_Token == false) {
           // 获取token
            String token = access_Token();
            Map<String, String> params = new HashMap<String, String>();
            // 调用类型
            params.put("type", "jsapi");
            params.put("access_token", token);
            params.put("grant_type", "client_credential");
            String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/ticket/getticket", params);
            JSONObject jsonTicket = JSONObject.parseObject(result);
            // 放入数据时设置redis缓存过期时间  一个半小时
            redisUtil.set("get_ticket", jsonTicket.get("ticket").toString(), 5400);
            return jsonTicket.get("ticket").toString();
        } else {
            // 获取redis缓存的数据
            return redisUtil.get("get_ticket").toString();
        }

    }

    // 微信签名算法
    @RequestMapping(value = "/get_signature")
    public Object getSignature(@RequestBody String url) throws UnsupportedEncodingException {
        // 前端的页面地址
        url = URLDecoder.decode(url.subSequence(0, url.length() - 1).toString(), "UTF-8");
        System.out.println("不存在");
        Map<String, String> ret = new HashMap<String, String>();
        System.out.println("url>>>>>:" + url);
        String wxTicket = getTicket();
        // 获取随机数 我用的是 UUID
        String nonce_str = create_nonce_str();
        // 时间戳 要秒为单位的
        String timestamp = create_timestamp();
        String str;
        String signature = "";
        //注意这里参数名必须全部小写,且必须有序
        str = "jsapi_ticket=" + wxTicket +
                "&noncestr=" + nonce_str +
                "&timestamp=" + timestamp +
                "&url=" + url;

        try {
            // 微信签名 就是将上面的参数str加密 
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(str.getBytes("UTF-8"));
            // 微信分享需要的签名
            signature = byteToHex(crypt.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", wxTicket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        ret.put("appId", Parm.APPID);
        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;
    }

    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

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

    // 获取用户信息
    @PostMapping("/userinfo")
    public JSONObject user_info(@RequestParam String openid) throws Exception {
        Map<String, String> params = new HashMap<String, String>();
        String token = access_Token();
        params.put("access_token", token);
        params.put("openid", openid);
        params.put("lang", "zh_CN");
        String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/user/info", params);
        System.out.println("result>>>>:" + result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        return jsonObject;
    }

    // 批量获取用户信息
    @PostMapping("/userInfos")
    public JSONObject user_infos(@RequestParam String token) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("access_token", token);
        String result = HttpGet.httpRequestToString("https://api.weixin.qq.com/cgi-bin/user/info/batchget", params);
        System.out.println("result>>>>:" + result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        return jsonObject;
    }

}

vue前端

vue需要安装一下 微信的依赖

 cnpm install weixin-js-sdk

urlZy.js (转义js,后台加密的openid传到前端时 有些特殊字符被转义了,如果不转回来的话,后台解密就会有问题,所以保险起见转换一下吧)

let urlzy = function urlzy(url) {
    // 将转义字符转换成对应的特殊 字符
    url = url.replace("%20", "+").replace("%3D", "=").replace("%2F", "/").replace("%3F", "?").replace("%25", "%").replace("%23", "#").replace("%26", "&amp;");
    return url;
}
export default {
    urlzy     //多个方法在此处json中export出去
}

openidApi.js (获取用户的openid,并加密)

// 后台接口
import { wxOpeid } from "../../api/api";
// 分享的js
import configApi from "./configApi";
// 转义字符
import urlzy from "./urlZy";

// 截取当前连接中的 code值
function getUrlKey(name) {
    console.log("getUrlKey");
    return (
        decodeURIComponent(
            (new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
                location.href
            ) || [, ""])[1].replace(/\+/g, "%20")
        ) || null
    );
}
// 获取code
function getCodeApi() {
    console.log("getCodeApi");
    let urlNow = encodeURIComponent(window.location.href);
    let scope = "snsapi_userinfo"; //snsapi_userinfo   //静默授权 用户无感知
    let appid = ""; // 开发者id 你可以直接写在前端 也可以 通过后台接口 传
    let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${urlNow}&response_type=code&scope=${scope}&state=STATE#wechat_redirect"`;
    let code = window.location.replace(url);
    console.log(code);
    let cade = getUrlKey(code);
    console.log(cade);
}
// 获取openid
let getOpenid = function getOpenid() {
    console.log("getOpenid");
    var that = this;
    that.loading = true;
    //返回值
    let code = getUrlKey("code");
    if (code) {
        console.log(code);
        wxOpeid(code).then(ref => {
            if (ref.data.status == 200) {
                // 清除缓存中的 openid
                sessionStorage.removeItem("openid");
                // 将openid 放入缓存中
                sessionStorage.setItem("openid", urlzy.urlzy(ref.data.openid));
                // 分享js
                configApi.fenxiang();
            } else {
                that.$message.error("网络不好,请刷新或重新进入");
            }
        });
    } else {
        getCodeApi();
    }
}

export default {
    getOpenid
}

configApi.js (最新的那两个分享接口,暂时没发现他有分享成功后的回调函数,只有配置分享标题等成功后的回调函数)

// 引入微信
import wx from "weixin-js-sdk";
import urlzy from "./urlZy";
import { get_signature, wxShare } from "../../api/api";

let fenxiang = function fenxiang() {
    console.log("分享");
    var that = this;
    let urls = location.href.split("#")[0];
    console.log(urls);
    // 判断访问的地址 是否 有openid  有就是分享出去的链接
    if (urls.indexOf("openid") != -1) {
        // 将链接中的转义字符转成字符串
        let lurl = urlzy.urlzy(urls);
        console.log(lurl);
        var openid = location.href.split("&")[1].split("=");
        console.log("openid:" + openid);
        var plat = location.href.split("&")[2].split("=");
        var platform;
        console.log("plat:" + plat);
        if (plat[1] == 1) {
            platform = "微信好友";
        } else if (plat[1] == 2) {
            platform = "微信朋友圈";
        } else if (plat[1] == 3) {
            platform = "QQ空间";
        } else if (plat[1] == 4) {
            platform = "QQ";
        } else if (plat[1] == 5) {
            platform = "微博";
        }
        let params = {
            browseOpenid: sessionStorage.getItem("openid"),  // 浏览人的openid
            openid: urlzy.urlzy(openid[1]),  // 分享人的openid
            status: 0,  // 0 分享链接处  1 通过其他人链接进来的
            url: lurl,  // 分享的链接
            platform: platform   // 分享的平台
        };
        console.log(params);
        // 保存浏览信息
        wxShare(params).then(ref => {
            if (ref.data.status == 200) {
                // 跳转链接  去掉openid 等参数
                window.location.href = location.href.split("&")[0];
            }
        });
    } else {
        // 获取微信分享签名的后台接口   传当前页面url地址
        get_signature(encodeURIComponent(location.href.split("#")[0])).then(ref => {
            console.log(ref);
            // 微信jsapi中的config接口注入权限验证配置
            wx.config({
                debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                appId: ref.data.appId, // 必填,公众号的唯一标识
                timestamp: ref.data.timestamp, // 必填,生成签名的时间戳
                nonceStr: ref.data.nonceStr, // 必填,生成签名的随机串
                signature: ref.data.signature, // 必填,签名
                jsApiList: [
                    "onMenuShareWeibo", //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
                    "onMenuShareAppMessage", // 微信好友
                    "onMenuShareTimeline", // 微信朋友圈
                    "onMenuShareQQ", // QQ
                    "onMenuShareQZone", //  QQ空间
                    "chooseWXPay" //微信支付
                ] // 必填,需要使用的JS接口列表
            });
        });
        // 检验js接口
        wx.checkJsApi({
            jsApiList: [
                "onMenuShareWeibo", //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
                "onMenuShareAppMessage", // 微信好友
                "onMenuShareTimeline", // 微信朋友圈
                "onMenuShareQQ", // QQ
                "onMenuShareQZone", //  QQ空间
                "chooseWXPay" //微信支付
            ],
            success: function (res) {
                //这里没返应 也没提示,也不会进断点
            }
        });
        //通过ready接口处理成功验证
        wx.ready(function () {
            // 修改分享链接 将当前用户的openid 添加到分享链接中 方便记录谁通过该用户的分享进入
            let linkurl = location.href.split("&")[0] + "&openid=" + sessionStorage.getItem("openid");
            console.log(linkurl);
            // 获取“分享给朋友”按钮点击状态及自定义分享内容接口(即将废弃)
            wx.onMenuShareAppMessage({
                title: "国医小镇预报名", // 分享标题
                desc: "国医小镇预报名", // 分享描述
                link: linkurl + "&app=1", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
                success: function (ref) {
                    setTimeout(function () {
                        //回调要执行的代码
                        // status 1 分享 0 预览(通过他人分享进入的)
                        let params = {
                            openid: sessionStorage.getItem("openid"),
                            status: 1,
                            url: linkurl,
                            platform: "微信好友"
                        };
                        console.log(params);
                        wxShare(params).then(ref => {
                            if (ref.data.status == 200) {
                                // 跳转链接  去掉openid 等参数
                                window.location.href = location.href.split("&")[0];
                            }
                         });
                    }, 500);
                    // 用户确定分享后执行的回调函数
                },
                cancel: function (res) {
                    // 用户取消分享后执行的回调函数
                }
            });
            // 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口(即将废弃)
            wx.onMenuShareTimeline({
                title: "国医小镇预报名", // 分享标题
                desc: "国医小镇预报名", // 分享描述
                link: linkurl + "&app=2", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
                success: function (ref) {
                    setTimeout(function () {
                        //回调要执行的代码
                        // status 1 分享 0 预览(通过他人分享进入的)
                        let params = {
                            openid: sessionStorage.getItem("openid"),
                            status: 1,
                            url: linkurl,
                            platform: "微信朋友圈"
                        };
                        console.log(params);
                        wxShare(params).then(ref => {
                            if (ref.data.status == 200) {
                                // 跳转链接  去掉openid 等参数
                                window.location.href = location.href.split("&")[0];
                            }
                        });
                    }, 500);
                    // 用户确定分享后执行的回调函数
                },
                cancel: function (res) {
                    // 用户取消分享后执行的回调函数
                }
            });
            // 获取“分享到QQ空间”按钮点击状态及自定义分享内容接口(即将废弃)
            wx.onMenuShareQZone({
                title: "国医小镇预报名", // 分享标题
                desc: "国医小镇预报名", // 分享描述
                link: linkurl + "&app=3", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
                success: function (ref) {
                    setTimeout(function () {
                        //回调要执行的代码
                        // status 1 分享 0 预览(通过他人分享进入的)
                        let params = {
                            openid: sessionStorage.getItem("openid"),
                            status: 1,
                            url: linkurl,
                            platform: "QQ空间"
                        };
                        console.log(params);
                        wxShare(params).then(ref => { 
                            if (ref.data.status == 200) {
                                // 跳转链接  去掉openid 等参数
                                window.location.href = location.href.split("&")[0];
                            }
                        });
                    }, 500);
                    // 用户确定分享后执行的回调函数
                },
                cancel: function (res) {
                    // 用户取消分享后执行的回调函数
                }
            });
            // 获取“分享到QQ”按钮点击状态及自定义分享内容接口(即将废弃)
            wx.onMenuShareQQ({
                title: "国医小镇预报名", // 分享标题
                desc: "国医小镇预报名", // 分享描述
                link: linkurl + "&app=4", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
                success: function (ref) {
                    setTimeout(function () {
                        //回调要执行的代码
                        // status 1 分享 0 预览(通过他人分享进入的)
                        let params = {
                            openid: sessionStorage.getItem("openid"),
                            status: 1,
                            url: linkurl,
                            platform: "QQ"
                        };
                        console.log(params);
                        wxShare(params).then(ref => {
                            if (ref.data.status == 200) {
                                // 跳转链接  去掉openid 等参数
                                window.location.href = location.href.split("&")[0];
                            }
                         });
                    }, 500);
                    // 用户确定分享后执行的回调函数
                },
                cancel: function (res) {
                    // 用户取消分享后执行的回调函数
                }
            });
            // 分享微博
            wx.onMenuShareWeibo({
                title: "国医小镇预报名", // 分享标题
                desc: "国医小镇预报名", // 分享描述
                link: linkurl + "&app=5", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: "http://hd.guoyixiaozhen.com/file/tb.png", // 分享图标
                success: function (ref) {
                    setTimeout(function () {
                        //回调要执行的代码
                        let params = {
                            openid: sessionStorage.getItem("openid"),
                            status: 1,
                            url: linkurl,
                            platform: "微博"
                        };
                        wxShare(params).then(ref => { 
                            if (ref.data.status == 200) {
                                // 跳转链接  去掉openid 等参数
                                window.location.href = location.href.split("&")[0];
                            }
                        });
                    }, 500);
                    // 用户确定分享后执行的回调函数
                },
                cancel: function (res) {
                    // 用户取消分享后执行的回调函数
                }
            });
            // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
        });

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

export default {
    fenxiang     //多个方法在此处json中export出去
}
发布了5 篇原创文章 · 获赞 4 · 访问量 1117

猜你喜欢

转载自blog.csdn.net/qq_36731585/article/details/101698818