All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redkalex.weixin.WeiXinMPService Maven / Gradle / Ivy

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.redkalex.weixin;

import org.redkale.util.Utility;
import org.redkale.convert.json.JsonConvert;
import org.redkale.service.RetResult;
import org.redkale.util.AutoLoad;
import org.redkale.service.Service;
import static org.redkale.convert.json.JsonConvert.TYPE_MAP_STRING_STRING;
import java.io.*;
import java.security.*;
import java.util.*;
import java.util.logging.*;
import javax.annotation.*;
import org.redkale.service.Local;
import org.redkale.util.*;
import static org.redkale.util.Utility.getHttpContent;

/**
 * 微信服务号Service
 *
 * 详情见: https://redkale.org
 *
 * @author zhangjx
 */
@Local
@AutoLoad(false)
public class WeiXinMPService implements Service {

    protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());

    private final boolean finest = logger.isLoggable(Level.FINEST);

    private final boolean finer = logger.isLoggable(Level.FINER);

    //配置集合, key: appid
    protected Map appidElements = new HashMap<>();

    //配置集合 key: clientid
    protected Map clientidElements = new HashMap<>();

    @Resource
    protected JsonConvert convert;

    @Resource(name = "property.weixin.mp.conf") //公众号配置文件路径
    protected String conf = "config.properties";

    @Resource(name = "APP_HOME")
    protected File home;

    @Resource(name = "property.weixin.mp.clientid") //客户端ID
    protected String clientid = "";

    @Resource(name = "property.weixin.mp.appid") //公众账号ID
    protected String appid = "";

    @Resource(name = "property.weixin.mp.appsecret") // 
    protected String appsecret = "";

    @Resource(name = "property.weixin.mp.token")
    protected String mptoken = "";

    @Override
    public void init(AnyValue conf) {
        if (this.conf != null && !this.conf.isEmpty()) { //存在微信公众号配置
            try {
                File file = (this.conf.indexOf('/') == 0 || this.conf.indexOf(':') > 0) ? new File(this.conf) : new File(home, "conf/" + this.conf);
                InputStream in = (file.isFile() && file.canRead()) ? new FileInputStream(file) : getClass().getResourceAsStream("/META-INF/" + this.conf);
                if (in == null) return;
                Properties properties = new Properties();
                properties.load(in);
                in.close();
                this.appidElements = MpElement.create(logger, properties, home);
                MpElement defElement = this.appidElements.get("");
                if (defElement != null && (this.appid == null || this.appid.isEmpty())) {
                    this.clientid = defElement.clientid;
                    this.appid = defElement.appid;
                    this.appsecret = defElement.appsecret;
                    this.mptoken = defElement.mptoken;
                }
                this.appidElements.values().forEach(element -> clientidElements.put(element.clientid, element));
                
            } catch (Exception e) {
                logger.log(Level.SEVERE, "init weixinmp conf error", e);
            }
        }
    }

    //-----------------------------------微信服务号接口----------------------------------------------------------
    //仅用于 https://open.weixin.qq.com/connect/oauth2/authorize  &scope=snsapi_base
    //需要在 “开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名
    public RetResult getMPOpenidByCode(String code) throws IOException {
        if (code != null) code = code.replace("\"", "").replace("'", "");
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code";
        String json = getHttpContent(url);
        if (finest) logger.finest(url + "--->" + json);
        Map jsonmap = convert.convertFrom(TYPE_MAP_STRING_STRING, json);
        return new RetResult<>(jsonmap.get("openid"));
    }

    public RetResult getMPWxunionidByCode(String code) {
        try {
            Map wxmap = getMPUserTokenByCode(code);
            final String unionid = wxmap.get("unionid");
            if (unionid != null && !unionid.isEmpty()) return new RetResult<>(unionid);
            return new RetResult<>(1011002);
        } catch (IOException e) {
            return new RetResult<>(1011001);
        }
    }

    public Map getMPUserTokenByCode(String code) throws IOException {
        return getMPUserTokenByCode(appid, appsecret, code);
    }

    public Map getMPUserTokenByCode(String clientid, String code) throws IOException {
        MpElement element = this.clientidElements.get(clientid);
        return getMPUserTokenByCode(element == null ? appid : element.appid, element == null ? appsecret : element.appsecret, code);
    }

    public Map getMPUserTokenByCodeAndAppid(String appid, String code) throws IOException {
        MpElement element = this.appidElements.get(appid);
        return getMPUserTokenByCode(element == null ? appid : element.appid, element == null ? appsecret : element.appsecret, code);
    }

    private Map getMPUserTokenByCode(String appid0, String appsecret0, String code) throws IOException {
        if (code != null) code = code.replace("\"", "").replace("'", "");
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid0 + "&secret=" + appsecret0 + "&code=" + code + "&grant_type=authorization_code";
        String json = getHttpContent(url);
        if (finest) logger.finest(url + "--->" + json);
        Map jsonmap = convert.convertFrom(TYPE_MAP_STRING_STRING, json);
        return getMPUserTokenByOpenid(jsonmap.get("access_token"), jsonmap.get("openid"));
    }

    public Map getMPUserTokenByOpenid(String access_token, String openid) throws IOException {
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid;
        String json = getHttpContent(url);
        if (finest) logger.finest(url + "--->" + json);
        Map jsonmap = convert.convertFrom(TYPE_MAP_STRING_STRING, json.replaceFirst("\\[.*\\]", "null"));
        return jsonmap;
    }

    public String verifyMPURL(String msgSignature, String timeStamp, String nonce, String echoStr) {
        return verifyMPURL0(mptoken, msgSignature, timeStamp, nonce, echoStr);
    }

    public String verifyMPURL(String clientid, String msgSignature, String timeStamp, String nonce, String echoStr) {
        MpElement element = this.clientidElements.get(clientid);
        return verifyMPURL0(element == null ? mptoken : element.mptoken, msgSignature, timeStamp, nonce, echoStr);
    }

    public String verifyMPURLByAppid(String appid, String msgSignature, String timeStamp, String nonce, String echoStr) {
        MpElement element = this.appidElements.get(appid);
        return verifyMPURL0(element == null ? mptoken : element.mptoken, msgSignature, timeStamp, nonce, echoStr);
    }

    private String verifyMPURL0(String mptoken, String msgSignature, String timeStamp, String nonce, String echoStr) {
        String signature = sha1(mptoken, timeStamp, nonce);
        if (!signature.equals(msgSignature)) throw new RuntimeException("signature verification error");
        return echoStr;
    }

    /**
     * 用SHA1算法生成安全签名
     * 

* @param strings String[] * * @return 安全签名 */ protected static String sha1(String... strings) { try { Arrays.sort(strings); MessageDigest md = MessageDigest.getInstance("SHA-1"); for (String s : strings) md.update(s.getBytes()); return Utility.binToHexString(md.digest()); } catch (Exception e) { throw new RuntimeException("SHA encryption to generate signature failure", e); } } public static class MpElement { public String clientid = ""; public String appid = ""; public String appsecret = ""; public String mptoken = ""; public static Map create(Logger logger, Properties properties, File home) { String def_clientid = properties.getProperty("weixin.mp.clientid", "").trim(); String def_appid = properties.getProperty("weixin.mp.appid", "").trim(); String def_appsecret = properties.getProperty("weixin.mp.appsecret", "").trim(); String def_mptoken = properties.getProperty("weixin.mp.mptoken", "").trim(); final Map map = new HashMap<>(); properties.keySet().stream().filter(x -> x.toString().startsWith("weixin.mp.") && x.toString().endsWith(".appid")).forEach(appid_key -> { final String prefix = appid_key.toString().substring(0, appid_key.toString().length() - ".appid".length()); String clientid = properties.getProperty(prefix + ".clientid", def_clientid).trim(); String appid = properties.getProperty(prefix + ".appid", def_appid).trim(); String appsecret = properties.getProperty(prefix + ".appsecret", def_appsecret).trim(); String mptoken = properties.getProperty(prefix + ".mptoken", def_mptoken).trim(); if (appid.isEmpty() || appsecret.isEmpty()) { logger.log(Level.WARNING, properties + "; has illegal weixinmp conf by prefix" + prefix); return; } MpElement element = new MpElement(); element.clientid = clientid; element.appid = appid; element.appsecret = appsecret; element.mptoken = mptoken; map.put(appid, element); if (def_appid.equals(appid)) map.put("", element); }); return map; } @Override public String toString() { return JsonConvert.root().convertTo(this); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy