package com.ynxbd.wx.wxfactory; import com.ynxbd.common.bean.Patient; import com.ynxbd.common.bean.User; import com.ynxbd.common.helper.common.*; import com.ynxbd.common.helper.http.OkHttpHelper; import com.ynxbd.common.result.Result; import com.ynxbd.common.service.PatientService; import com.ynxbd.wx.config.WeChatConfig; import com.ynxbd.wx.wxfactory.base.auth.models.AuthData; import com.ynxbd.wx.wxfactory.base.auth.models.SnsOath2AccessToken; import com.ynxbd.wx.wxfactory.base.auth.models.SnsUserInfo; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.ehcache.Cache; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.*; @Slf4j public class WxAuthHelper { private static final int SESSION_MAX_INACTIVE_INTERVAL = 60 * 60; // session最大存活时间 1H private static final String P_PREFIX = "_@"; // 前缀 private static final String P_SUFFIX = ":"; // 后缀 private static final String AUTH_SESSION_ID_NAME = "SID"; private static final String UNION_ID_NAME = "UID"; public static String auth(HttpServletRequest request, HttpServletResponse response, boolean isUserInfo) { String code = request.getParameter("code"); String state = request.getParameter("state"); // base64 Map paramsMap = getParamsMap(request.getParameter("p")); log.info("[授权] code={}, state={}, paramsMap={}", code, state, JsonHelper.toJsonString(paramsMap)); try { state = state == null ? "" : URLDecoder.decode(Base64Helper.decode(state), "UTF-8"); String enUnionId = paramsMap.get(UNION_ID_NAME); String enAuthSessionId = paramsMap.get(AUTH_SESSION_ID_NAME); String authSessionId = AesWxHelper.decode(enAuthSessionId); paramsMap.get(AUTH_SESSION_ID_NAME); // if (!ObjectUtils.isEmpty(params)) { // int index = params.indexOf("@" + AUTH_SESSION_ID_NAME + ":"); // if (index == -1) index = params.indexOf("%40" + AUTH_SESSION_ID_NAME + ":"); // 防止数据转义失败 // if (index != -1) { // enUnionId = params.substring(0, index); // authSessionId = params.substring(index); // authSessionId = AesWxHelper.decode(authSessionId); // } // } log.info("[授权-解码] enUnionId={}, enAuthSessionId={}, authSessionId={}, state={}", enUnionId, enAuthSessionId, authSessionId, state); SnsOath2AccessToken snsToken = WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code); log.info("[授权-用户]snsToken={}", JsonHelper.toJsonString(snsToken)); if (snsToken != null) { String openid = snsToken.getOpenid(); String unionId = snsToken.getUnionid(); if (unionId == null) { log.info("[测试] enUnionId={}", enUnionId); } if (openid != null) { HttpSession session = request.getSession(); session.setMaxInactiveInterval(SESSION_MAX_INACTIVE_INTERVAL); session.setAttribute("openid", openid); WxCacheHelper.putOpenIdToAIDCache(authSessionId, openid); Cache cache = WxCacheHelper.getUserCacheManager(); if (WeChatConfig.isDevUser(openid) || !cache.containsKey(openid)) { User user = new User(); user.setUnionId(unionId); user.setOpenid(openid); user.setIsSnapShotUser(snsToken.getIsSnapShotUser()); user.setPatientList(new PatientService().queryPatientList(openid, unionId, true)); if (isUserInfo) { SnsUserInfo snsUser = WxFactory.Base.OAuth().snsUserInfo(snsToken.getAccessToken(), openid, "zh_CN", 3); if (snsUser != null) { user.setCountry(snsUser.getCountry()); user.setAvatar(snsUser.getHeadImgUrl()); user.setNickName(snsUser.getNickname_emoji()); user.setProvince(snsUser.getProvince()); user.setGenderByInt(snsUser.getSex()); user.setCity(snsUser.getCity()); user.setLanguage(snsUser.getLanguage()); } } cache.put(openid, user); } else { if (isUserInfo) { User user = cache.get(openid); if (user != null && user.getAvatar() == null && user.getNickName() == null) { SnsUserInfo snsUser = WxFactory.Base.OAuth().snsUserInfo(snsToken.getAccessToken(), openid, "zh_CN", 3); if (snsUser != null) { user.setAvatar(snsUser.getHeadImgUrl()); user.setNickName(snsUser.getNickname_emoji()); user.setGenderByInt(snsUser.getSex()); } } } } } } if (WeChatConfig.HAS_HTTPS_BY_BASE_URL) { // 强制为https String httpsURL = URLHelper.URLToHttps(state); state = httpsURL == null ? "" : httpsURL; } if (state.contains(".html")) { // 网页授权配置 return state; } else { String baseUrl = WeChatConfig.getBaseURL(); if (baseUrl != null && state.contains(baseUrl)) { return state; } } } catch (Exception e) { ErrorHelper.println(e); log.error("[微信][获取重定向链接异常]{}", e.getMessage()); } return null; } public static Result isAuth(HttpServletRequest request, HttpServletResponse response, boolean isPayOAuth) throws Exception { String token = request.getParameter("token"); // 前端缓存 String state = request.getParameter("state"); String isUserInfo = request.getParameter("isUserInfo"); String UID = ParamHelper.filterParamNull(request.getParameter(UNION_ID_NAME), ""); String deState = URLDecoder.decode(Base64Helper.decode(state), "UTF-8"); String authSessionId = null; if (WeChatConfig.IS_ENABLE_GMC && WeChatConfig.IS_GMC_SERVER && !isPayOAuth) { // 开启医共体开关 & 是医共体主服务器 & 不是支付授权 authSessionId = AesWxHelper.decode(request.getHeader(AUTH_SESSION_ID_NAME)); } log.warn("[授权is_auth] token={}, state={}, isUserInfo={}, authSessionId={}, UID={}, deState={}", token, state, isUserInfo, authSessionId, UID, deState); if (WeChatConfig.IS_ENABLE_GMC && !WeChatConfig.IS_GMC_SERVER && !isPayOAuth) { // 开启医共体开关 & 不是医共体主服务器 & 不是支付授权 try { // 请求转发 String serverDomain = WeChatConfig.getDomain(false, false); if (deState != null && serverDomain != null && !deState.contains(serverDomain)) { return Result.error("授权域名不匹配"); } HttpSession session = request.getSession(); String sessionId = session.getId(); log.info("[认证请求转发] [sessionId:{}]URL:[{}]", sessionId, WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true)); String data = OkHttpHelper.postFormStr(WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true) + "wx_auth/is_auth", params -> { params.put("token", token); params.put("state", state); params.put("isUserInfo", isUserInfo); params.put(UNION_ID_NAME, UID); }, headers -> { if (!ObjectUtils.isEmpty(sessionId)) { headers.add(AUTH_SESSION_ID_NAME, AesWxHelper.encode(WeChatConfig.APP_ID + ":" + sessionId)); } }); Result result = Result.dataToResult(data, true); System.out.println(JsonHelper.toJsonString(result)); return Result.dataToResult(data, true); } catch (Exception e) { e.printStackTrace(); return Result.error(e); } } boolean isFindUserInfo = ("true".equals(isUserInfo)); AuthData authData = new AuthData(); String cacheOpenid = authData.decodeToken(token, WeChatConfig.APP_ID); if (cacheOpenid != null) { log.info("[微信token认证] token={}, openid={}", token, cacheOpenid); User user = WxCacheHelper.getCacheUser(cacheOpenid); List patients; if (user == null) { patients = new PatientService().queryPatientList(cacheOpenid, null, true); Cache cache = WxCacheHelper.getUserCacheManager(); User addCache = new User(); addCache.setOpenid(cacheOpenid); addCache.setUnionId(authData.getUnionId()); addCache.setAvatar(authData.getAvatar()); addCache.setNickName(authData.getNickName()); addCache.setPatientList(patients); cache.put(cacheOpenid, addCache); } else { patients = user.getPatientList(); } Map map = new HashMap<>(); map.put("openid", cacheOpenid); map.put("token", token); map.put("enOpenId", AesWxHelper.encode(cacheOpenid, true)); map.put("enUnionId", AesWxHelper.encode(authData.getUnionId(), true)); map.put("date", new Date()); map.put("avatar", authData.getAvatar()); map.put("nickName", authData.getNickName()); map.put("patients", CodeHelper.get28UUID() + Base64Helper.encode(URLEncoder.encode(JsonHelper.toJsonString(patients), "UTF-8"))); map.put("enParams", AesMicroHelper.encode(cacheOpenid)); return Result.success(map); } HttpSession session = request.getSession(); Object sessionOpenId = session.getAttribute("openid"); String openid = sessionOpenId == null ? null : sessionOpenId.toString(); log.info("[微信认证] openid={}, authSessionId={}", openid, authSessionId); if (!ObjectUtils.isEmpty(authSessionId) && ObjectUtils.isEmpty(openid)) { openid = WxCacheHelper.getOpenIdByAIDCache(authSessionId); log.warn("[微信AID认证]openid={}", openid); } if (openid != null) { log.info("[微信认证]openid={}", openid); User user = WxCacheHelper.getCacheUser(openid); if (user == null) { return Result.success(getAuthUrl(request, state, isFindUserInfo, UID, authSessionId)); } else { if (ObjectUtils.isEmpty(openid)) { openid = user.getOpenid(); // sessionId认证openid补充 } } if (isFindUserInfo) { // 更换授权模式,需更新信息 if (user.getNickName() == null || user.getAvatar() == null) { return Result.success(getAuthUrl(request, state, true, UID, authSessionId)); } } Map map = new HashMap<>(); map.put("openid", openid); map.put("token", new AuthData().createToken(WeChatConfig.APP_ID, openid, user.getUnionId(), user.getAvatar(), user.getNickName())); map.put("enOpenId", AesWxHelper.encode(openid, true)); map.put("enUnionId", AesWxHelper.encode(user.getUnionId(), true)); map.put("date", new Date()); map.put("avatar", user.getAvatar()); map.put("nickName", user.getNickName()); map.put("patients", CodeHelper.get28UUID() + Base64Helper.encode(URLEncoder.encode(JsonHelper.toJsonString(user.getPatientList()), "UTF-8"))); map.put("enParams", AesMicroHelper.encode(openid)); return Result.success(map); } return Result.success(getAuthUrl(request, state, isFindUserInfo, UID, authSessionId)); } private static final String OAUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID + "&redirect_uri="; private static String getAuthUrl(HttpServletRequest request, String state, boolean isFindUserInfo, String UID, String SID) { // StringBuffer url = request.getRequestURL(); // String baseUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append(request.getServletContext().getContextPath()).append("/").toString(); if (state == null) { return null; } String api = isFindUserInfo ? "u_auth" : "b_auth"; String scope = isFindUserInfo ? "snsapi_userinfo" : "snsapi_base"; UID = toURLParam(UNION_ID_NAME, UID); String enSID = toURLParam(AUTH_SESSION_ID_NAME, AesWxHelper.encode(SID, true)); String params = enSID + UID; try { params = ObjectUtils.isEmpty(params) ? "" : URLEncoder.encode(params, "UTF-8"); log.warn("[认证链接参数] params={}", params); } catch (Exception e) { log.error(e.getMessage()); } state = OAUTH_URL + WeChatConfig.getBaseURL(WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request)) + "wx_auth/" + api + "?p=" + params + "&response_type=code" + "&scope=" + scope + "&forcePopup=true" + "&state=" + state + // state位置固定 "#wechat_redirect"; log.warn("[认证授权链接]state={}", state); return Base64Helper.encode(state); } // 是否为https请求 public static boolean isHttpsWithProxy(HttpServletRequest request) { // 优先检查代理头(适用于反向代理场景) String forwardedProto = request.getHeader("X-Forwarded-Proto"); if (forwardedProto != null) { return "https".equalsIgnoreCase(forwardedProto); } // 未经过代理,直接检查原生请求 return request.isSecure(); } public static String toURLParam(String key, String val) { if (ObjectUtils.isEmpty(val)) return ""; return P_PREFIX + key + P_SUFFIX + val; } public static Map getParamsMap(String params) { Map paramsMap = new HashMap<>(); try { if (ObjectUtils.isEmpty(params)) { return paramsMap; } params = URLDecoder.decode(params, "UTF-8"); String[] paramsArr = params.split(P_PREFIX); String key, val; String[] keyValueArr; for (String paramItem : paramsArr) { if (paramItem.isEmpty()) { continue; } keyValueArr = paramItem.split(P_SUFFIX, 2); // 分割成最多两部分 if (keyValueArr.length == 2) { key = keyValueArr[0]; val = keyValueArr[1]; val = ParamHelper.filterParamNull(val, null); if (val != null) { paramsMap.put(key, val); } } } } catch (Exception e) { log.error(e.getMessage()); } return paramsMap; } // // 医共体开启 & 不是支付授权 // private static boolean isAuthGMC(boolean isPayOAuth) { // return WeChatConfig.IS_ENABLE_GMC && !isPayOAuth; // } // private static String getOAuthURL(HttpServletRequest request, boolean isPayOAuth) { // boolean isHttps = WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request); // return isAuthGMC(isPayOAuth) // ? "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.GMC_AUTH_APP_ID + "&redirect_uri=" + WeChatConfig.getGMCAuthDomain(isHttps, true) // : "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeChatConfig.APP_ID + "&redirect_uri=" + WeChatConfig.getBaseURL(isHttps); // } // private static SnsOath2AccessToken getOath2AccessToken(String code, boolean isPayOAuth) { // if (isAuthGMC(isPayOAuth)) { // return WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.GMC_AUTH_APP_ID, WeChatConfig.GMC_AUTH_APP_SECRET, code); // } // return WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code); // } // // 获取重定向链接 // private static String getAuthDomain(HttpServletRequest request, boolean isPayOAuth) { // if (isAuthGMC(isPayOAuth)) { // return WeChatConfig.getGMCAuthDomain(true, false); // 强制为https // } // return WeChatConfig.getDomain(WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request)); // 配置中baseURL有"https"时优先级最高 ["@protocol=1"为https;"@protocol=0"为默认的http;] // } }