package com.ruoyi.common.utils; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ruoyi.common.utils.http.HttpUtils; import java.util.HashMap; import java.util.Map; /** * 微信工具类 * * @author ruoyi */ public class WechatUtils { private static final Logger log = LoggerFactory.getLogger(WechatUtils.class); private static final String WECHAT_API_BASE_URL = "https://open.weixin.qq.com"; private static final String WECHAT_API_BASE_URL_SERVER = "https://api.weixin.qq.com"; /** * 获取微信Access Token * * @param appId 微信AppID * @param appSecret 微信AppSecret * @return Access Token */ public static String getAccessToken(String appId, String appSecret) { try { String url = WECHAT_API_BASE_URL_SERVER + "/cgi-bin/token"; String param = "grant_type=client_credential&appid=" + appId + "&secret=" + appSecret; String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("access_token")) { return jsonObject.getString("access_token"); } else { log.error("获取微信Access Token失败: {}", response); return null; } } catch (Exception e) { log.error("获取微信Access Token异常: {}", e.getMessage()); return null; } } /** * 获取微信用户信息 * * @param accessToken Access Token * @param openid 用户OpenID * @return 用户信息 */ public static JSONObject getWechatUserInfo(String accessToken, String openid) { try { String url = WECHAT_API_BASE_URL_SERVER + "/cgi-bin/user/info"; String param = "access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("openid")) { return jsonObject; } else { log.error("获取微信用户信息失败: {}", response); return null; } } catch (Exception e) { log.error("获取微信用户信息异常: {}", e.getMessage()); return null; } } /** * 获取微信网页授权Access Token * * @param appId 微信AppID * @param appSecret 微信AppSecret * @param code 授权码 * @return 网页授权Access Token信息 */ public static JSONObject getWebAccessToken(String appId, String appSecret, String code) { try { String url = WECHAT_API_BASE_URL_SERVER + "/sns/oauth2/access_token"; String param = "appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code"; String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("access_token")) { return jsonObject; } else { log.error("获取微信网页授权Access Token失败: {}", response); return null; } } catch (Exception e) { log.error("获取微信网页授权Access Token异常: {}", e.getMessage()); return null; } } /** * 获取微信网页授权用户信息 * * @param accessToken 网页授权Access Token * @param openid 用户OpenID * @return 用户信息 */ public static JSONObject getWebUserInfo(String accessToken, String openid) { try { String url = WECHAT_API_BASE_URL_SERVER + "/sns/userinfo"; String param = "access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; String response = HttpUtils.sendGet(url, param); JSONObject jsonObject = JSON.parseObject(response); if (jsonObject.containsKey("openid")) { return jsonObject; } else { log.error("获取微信网页授权用户信息失败: {}", response); return null; } } catch (Exception e) { log.error("获取微信网页授权用户信息异常: {}", e.getMessage()); return null; } } /** * 生成微信网页授权URL * * @param appId 微信AppID * @param redirectUri 回调地址 * @param scope 授权范围 (snsapi_base 或 snsapi_userinfo) * @param state 状态参数 * @return 授权URL */ public static String generateAuthUrl(String appId, String redirectUri, String scope, String state) { try { // 清理和验证redirectUri String cleanRedirectUri = cleanRedirectUri(redirectUri); // URL编码 String encodedRedirectUri = java.net.URLEncoder.encode(cleanRedirectUri, "UTF-8"); return WECHAT_API_BASE_URL + "/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + encodedRedirectUri + "&response_type=code&scope=" + scope + "&state=" + state + "#wechat_redirect"; } catch (Exception e) { log.error("生成微信授权URL异常: {}", e.getMessage()); return null; } } /** * 清理和验证redirectUri * * @param redirectUri 原始回调地址 * @return 清理后的回调地址 */ private static String cleanRedirectUri(String redirectUri) { if (StringUtils.isEmpty(redirectUri)) { return redirectUri; } try { // 移除端口号(微信授权不支持端口号) if (redirectUri.contains(":81") || redirectUri.contains(":8080") || redirectUri.contains(":3000")) { redirectUri = redirectUri.replaceAll(":(81|8080|3000|8081|8082|8083|8084|8085|8086|8087|8088|8089|8090)", ""); log.warn("检测到端口号,已自动移除: {}", redirectUri); } // 确保使用HTTPS(生产环境) if (redirectUri.startsWith("http://") && !redirectUri.contains("localhost") && !redirectUri.contains("127.0.0.1")) { redirectUri = redirectUri.replace("http://", "https://"); log.warn("生产环境建议使用HTTPS,已自动转换: {}", redirectUri); } return redirectUri; } catch (Exception e) { log.error("清理redirectUri异常: {}", e.getMessage()); return redirectUri; } } /** * 判断是否为微信浏览器 * * @param userAgent 用户代理字符串 * @return 是否为微信浏览器 */ public static boolean isWechatBrowser(String userAgent) { if (StringUtils.isEmpty(userAgent)) { return false; } return userAgent.toLowerCase().contains("micromessenger"); } }