微信后端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
14 KiB

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.bean.SnsOath2AccessToken;
import com.ynxbd.wx.wxfactory.bean.SnsUserInfo;
import com.ynxbd.wx.wxfactory.bean.auth.AuthData;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.RequestBody;
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.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class WxAuthHelper {
private static final int SESSION_MAX_INACTIVE_INTERVAL = 60 * 60; // session最大存活时间 1H
public static String auth(HttpServletRequest request, HttpServletResponse response, boolean isUserInfo) {
String code = request.getParameter("code");
String state = request.getParameter("state"); // base64
String enuId = request.getParameter("enuId");
log.info("[授权] code={}, state={}, enuId={}", code, state, enuId);
try {
state = state == null ? "" : URLDecoder.decode(Base64Helper.decode(state), "UTF-8");
String enUnionId = null;
String protocolState = null;
if (!ObjectUtils.isEmpty(enuId)) {
int index = enuId.indexOf("@protocolState=");
if (index == -1) index = enuId.indexOf("%40protocolState="); // 防止数据转义失败
if (index != -1) {
enUnionId = enuId.substring(0, index);
protocolState = enuId.substring(index);
}
}
log.info("[授权-解码] enUnionId={}, protocolState={}, state={}", enUnionId, protocolState, state);
SnsOath2AccessToken snsToken = WxFactory.Base.OAuth().oauth2AccessToken(WeChatConfig.APP_ID, WeChatConfig.APP_SECRET, code);
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);
Cache<String, User> 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) {
log.error("[微信][获取重定向链接异常]{}", e.getMessage());
}
return null;
}
public static Result isAuth(HttpServletRequest request, boolean isPayOAuth) throws Exception {
String token = request.getParameter("token"); // 前端缓存
String state = request.getParameter("state");
String isUserInfo = request.getParameter("isUserInfo");
String protocolState = request.getParameter("protocolState");
String enuId = ParamHelper.filterParamNull(request.getParameter("enuId"), "");
String deState = URLDecoder.decode(Base64Helper.decode(state), "UTF-8");
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("授权域名不匹配");
}
RequestBody requestBody = new FormBody.Builder()
.add("token", token)
.add("state", state)
.add("isUserInfo", isUserInfo)
.add("enuId", enuId)
.add("protocolState", protocolState)
.build();
log.info("[认证请求转发] URL:[{}]", WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true));
String data = OkHttpHelper.post(WeChatConfig.getGMCAuthDomain(isHttpsWithProxy(request), true) + "wx_auth/is_auth", requestBody);
return Result.dataToResult(data, true);
} catch (Exception e) {
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<Patient> patients;
if (user == null) {
patients = new PatientService().queryPatientList(cacheOpenid, null, true);
Cache<String, User> 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<String, Object> 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 openid = session.getAttribute("openid");
if (openid != null) {
log.info("[微信认证]openid={}", openid);
User user = WxCacheHelper.getCacheUser((String) openid);
if (user == null) {
return Result.success(getAuthUrl(request, state, isFindUserInfo, enuId, protocolState));
}
if (isFindUserInfo) { // 更换授权模式,需更新信息
if (user.getNickName() == null || user.getAvatar() == null) {
return Result.success(getAuthUrl(request, state, true, enuId, protocolState));
}
}
Map<String, Object> map = new HashMap<>();
map.put("openid", openid);
map.put("token", new AuthData().createToken(WeChatConfig.APP_ID, openid.toString(), user.getUnionId(), user.getAvatar(), user.getNickName()));
map.put("enOpenId", AesWxHelper.encode(openid.toString(), 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.toString()));
return Result.success(map);
}
return Result.success(getAuthUrl(request, state, isFindUserInfo, enuId, protocolState));
}
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 enuId, String protocolState) {
// 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";
state = OAUTH_URL + WeChatConfig.getBaseURL(WeChatConfig.HAS_HTTPS_BY_BASE_URL || isHttpsWithProxy(request)) +
"wx_auth/" + api +
"?state=" + state +
"&response_type=code" +
"&scope=" + scope + "&forcePopup=true" +
"&enuId=" + (enuId == null ? "" : enuId) +
(protocolState == null ? "" : ("%40protocolState=" + protocolState)) +
"#wechat_redirect";
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();
}
// // 医共体开启 & 不是支付授权
// 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;]
// }
}