feat(qrLogin): 新增微信扫码登录功能及H5页面支持
- 在QrLoginController中添加微信扫码登录确认接口和获取微信网页授权URL接口 - QrLoginGenerateResponse新增微信扫码登录H5页面URL及微信公众号AppID字段 - QrLoginService接口新增wechatScanConfirm方法实现微信扫码登录确认逻辑 - QrLoginServiceImpl完成基于unionId和openId的用户绑定验证与登录状态更新 - WxService扩展获取微信公众号AppID、AppSecret及AccessToken方法,支持多租户 - 新增WechatScanRequest和WechatScanResponse用于微信扫码登录请求和响应数据封装 - 生成微信扫码登录H5页面URL并包含token参数用于前端跳转确认登录 - 实现通过微信授权码获取用户unionId和openId,从而完成平台用户绑定验证 - 完善异常处理及日志记录,确保微信扫码登录流程稳定可靠
This commit is contained in:
@@ -3,6 +3,8 @@ package com.gxwebsoft.auto.controller;
|
||||
import com.gxwebsoft.auto.dto.QrLoginConfirmRequest;
|
||||
import com.gxwebsoft.auto.dto.QrLoginGenerateResponse;
|
||||
import com.gxwebsoft.auto.dto.QrLoginStatusResponse;
|
||||
import com.gxwebsoft.auto.dto.WechatScanRequest;
|
||||
import com.gxwebsoft.auto.dto.WechatScanResponse;
|
||||
import com.gxwebsoft.auto.service.QrLoginService;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
@@ -28,6 +30,12 @@ public class QrLoginController extends BaseController {
|
||||
@Autowired
|
||||
private QrLoginService qrLoginService;
|
||||
|
||||
@Autowired
|
||||
private com.gxwebsoft.common.system.service.WxService wxService;
|
||||
|
||||
@Autowired
|
||||
private javax.servlet.http.HttpServletRequest request;
|
||||
|
||||
/**
|
||||
* 生成扫码登录token
|
||||
*/
|
||||
@@ -85,4 +93,40 @@ public class QrLoginController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信扫码登录确认(H5页面调用)
|
||||
*/
|
||||
@Operation(summary = "微信扫码登录确认")
|
||||
@PostMapping("/wechat-scan")
|
||||
public ApiResult<?> wechatScanConfirm(@Valid @RequestBody WechatScanRequest request) {
|
||||
try {
|
||||
WechatScanResponse response = qrLoginService.wechatScanConfirm(request);
|
||||
return success("操作成功", response);
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信网页授权 URL(用于 H5 扫码页面重定向)
|
||||
*/
|
||||
@Operation(summary = "获取微信网页授权URL")
|
||||
@GetMapping("/wechat-oauth-url")
|
||||
public ApiResult<?> getWechatOAuthUrl(@Parameter(description = "扫码登录token") @RequestParam String token) {
|
||||
try {
|
||||
String appId = wxService.getOfficialAppId(getTenantId());
|
||||
// 回调地址,指向 H5 扫码确认页面
|
||||
String redirectUri = java.net.URLEncoder.encode(
|
||||
"https://" + request.getHeader("Host") + "/wx-scan?token=" + token,
|
||||
java.nio.charset.StandardCharsets.UTF_8);
|
||||
// 构造微信 OAuth 授权 URL
|
||||
String oauthUrl = String.format(
|
||||
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=%s#wechat_redirect",
|
||||
appId, redirectUri, token);
|
||||
return success("获取成功", oauthUrl);
|
||||
} catch (Exception e) {
|
||||
return fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,12 @@ public class QrLoginGenerateResponse {
|
||||
@Schema(description = "过期时间(秒)")
|
||||
private Long expiresIn;
|
||||
|
||||
@Schema(description = "微信扫码登录H5页面URL")
|
||||
private String wechatScanUrl;
|
||||
|
||||
@Schema(description = "微信公众号AppID")
|
||||
private String wechatAppId;
|
||||
|
||||
// 保持向后兼容的构造函数
|
||||
public QrLoginGenerateResponse(String token, String qrCodeContent, Long expiresIn) {
|
||||
this.token = token;
|
||||
|
||||
31
src/main/java/com/gxwebsoft/auto/dto/WechatScanRequest.java
Normal file
31
src/main/java/com/gxwebsoft/auto/dto/WechatScanRequest.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.gxwebsoft.auto.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 微信扫码登录请求(用于 H5 页面回调)
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-04-06
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "微信扫码登录请求")
|
||||
public class WechatScanRequest {
|
||||
|
||||
@Schema(description = "扫码登录token")
|
||||
@NotBlank(message = "token不能为空")
|
||||
private String token;
|
||||
|
||||
@Schema(description = "微信公众号授权code")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "微信unionId(如果已获取)")
|
||||
private String unionId;
|
||||
|
||||
@Schema(description = "微信openId")
|
||||
private String openId;
|
||||
|
||||
}
|
||||
47
src/main/java/com/gxwebsoft/auto/dto/WechatScanResponse.java
Normal file
47
src/main/java/com/gxwebsoft/auto/dto/WechatScanResponse.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package com.gxwebsoft.auto.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 微信扫码登录响应
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2026-04-06
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "微信扫码登录响应")
|
||||
public class WechatScanResponse {
|
||||
|
||||
@Schema(description = "状态:success-登录成功,bind_required-需要绑定账号,not_bound-账号未绑定")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "JWT访问令牌")
|
||||
private String accessToken;
|
||||
|
||||
@Schema(description = "用户信息")
|
||||
private Object userInfo;
|
||||
|
||||
@Schema(description = "提示信息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Integer tenantId;
|
||||
|
||||
public static WechatScanResponse success(String accessToken, Object userInfo, Integer tenantId) {
|
||||
return new WechatScanResponse("success", accessToken, userInfo, "登录成功", tenantId);
|
||||
}
|
||||
|
||||
public static WechatScanResponse needBind(String message) {
|
||||
return new WechatScanResponse("bind_required", null, null, message, null);
|
||||
}
|
||||
|
||||
public static WechatScanResponse notBound(String message) {
|
||||
return new WechatScanResponse("not_bound", null, null, message, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.gxwebsoft.auto.service;
|
||||
import com.gxwebsoft.auto.dto.QrLoginConfirmRequest;
|
||||
import com.gxwebsoft.auto.dto.QrLoginGenerateResponse;
|
||||
import com.gxwebsoft.auto.dto.QrLoginStatusResponse;
|
||||
import com.gxwebsoft.auto.dto.WechatScanRequest;
|
||||
import com.gxwebsoft.auto.dto.WechatScanResponse;
|
||||
|
||||
/**
|
||||
* 扫码登录服务接口
|
||||
@@ -43,4 +45,12 @@ public interface QrLoginService {
|
||||
*/
|
||||
boolean scanQrCode(String token);
|
||||
|
||||
/**
|
||||
* 微信扫码登录确认(H5页面调用)
|
||||
*
|
||||
* @param request 微信扫码登录请求
|
||||
* @return WechatScanResponse
|
||||
*/
|
||||
WechatScanResponse wechatScanConfirm(WechatScanRequest request);
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import cn.hutool.core.lang.UUID;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.gxwebsoft.auto.dto.*;
|
||||
import com.gxwebsoft.auto.service.QrLoginService;
|
||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
@@ -13,6 +15,8 @@ import com.gxwebsoft.common.core.security.JwtSubject;
|
||||
import com.gxwebsoft.common.core.security.JwtUtil;
|
||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.common.system.entity.UserOauth;
|
||||
import com.gxwebsoft.common.system.service.UserOauthService;
|
||||
import com.gxwebsoft.common.system.service.UserService;
|
||||
import com.gxwebsoft.common.system.service.WxService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -24,6 +28,7 @@ import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.gxwebsoft.common.core.constants.RedisConstants.*;
|
||||
import static com.gxwebsoft.common.core.constants.PlatformConstants.MP_OFFICIAL;
|
||||
|
||||
/**
|
||||
* 扫码登录服务实现
|
||||
@@ -47,6 +52,9 @@ public class QrLoginServiceImpl implements QrLoginService {
|
||||
@Autowired
|
||||
private WxService wxService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private UserOauthService userOauthService;
|
||||
|
||||
private static final String QR_LOGIN_TOKEN = "QR_LOGIN_TOKEN";
|
||||
|
||||
@Override
|
||||
@@ -88,6 +96,23 @@ public class QrLoginServiceImpl implements QrLoginService {
|
||||
// 小程序码生成失败不影响整体功能,继续返回其他信息
|
||||
}
|
||||
|
||||
// 生成微信扫码登录 H5 页面 URL
|
||||
try {
|
||||
String appId = wxService.getOfficialAppId(tenantId);
|
||||
String baseUrl = configProperties.getFileServer();
|
||||
if (StrUtil.isBlank(baseUrl)) {
|
||||
baseUrl = "https://server.gxwebsoft.com";
|
||||
}
|
||||
// 微信扫码后跳转的 H5 确认页面
|
||||
String wechatScanUrl = baseUrl + "/wx-scan?token=" + token;
|
||||
response.setWechatScanUrl(wechatScanUrl);
|
||||
response.setWechatAppId(appId);
|
||||
log.info("生成微信扫码登录URL: {}", wechatScanUrl);
|
||||
} catch (Exception e) {
|
||||
log.warn("生成微信扫码URL失败: {}", e.getMessage());
|
||||
// 不影响整体功能
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -290,4 +315,121 @@ public class QrLoginServiceImpl implements QrLoginService {
|
||||
}
|
||||
return uploadPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WechatScanResponse wechatScanConfirm(WechatScanRequest request) {
|
||||
String token = request.getToken();
|
||||
if (StrUtil.isBlank(token)) {
|
||||
return WechatScanResponse.notBound("二维码参数错误");
|
||||
}
|
||||
|
||||
String redisKey = QR_LOGIN_TOKEN_KEY + token;
|
||||
QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class);
|
||||
|
||||
if (qrLoginData == null) {
|
||||
return WechatScanResponse.notBound("二维码已过期,请刷新重试");
|
||||
}
|
||||
|
||||
// 检查是否过期
|
||||
if (DateUtil.date().after(DateUtil.parseDateTime(qrLoginData.getExpireTime()))) {
|
||||
redisUtil.delete(redisKey);
|
||||
return WechatScanResponse.notBound("二维码已过期,请刷新重试");
|
||||
}
|
||||
|
||||
String unionId = request.getUnionId();
|
||||
String openId = request.getOpenId();
|
||||
Integer tenantId = qrLoginData.getTenantId();
|
||||
|
||||
// 如果没有直接传 unionId,但有 code,需要通过 code 获取
|
||||
if (StrUtil.isBlank(unionId) && StrUtil.isNotBlank(request.getCode())) {
|
||||
try {
|
||||
JSONObject userAccessToken = wxService.getOfficialUserAccessToken(request.getCode(), tenantId);
|
||||
unionId = userAccessToken.getString("unionid");
|
||||
openId = userAccessToken.getString("openid");
|
||||
log.info("通过授权码获取到 unionId: {}, openId: {}", unionId, openId);
|
||||
} catch (Exception e) {
|
||||
log.error("通过授权码获取用户信息失败: {}", e.getMessage());
|
||||
return WechatScanResponse.notBound("微信授权失败,请重试");
|
||||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(unionId) && StrUtil.isBlank(openId)) {
|
||||
return WechatScanResponse.notBound("无法获取微信用户信息");
|
||||
}
|
||||
|
||||
User user = null;
|
||||
|
||||
// 优先通过 unionId 查找用户
|
||||
if (StrUtil.isNotBlank(unionId)) {
|
||||
user = userService.getOne(new LambdaQueryWrapper<User>()
|
||||
.eq(User::getUnionid, unionId)
|
||||
.eq(User::getDeleted, 0)
|
||||
.last("limit 1"));
|
||||
log.info("通过 unionId {} 查找用户: {}", unionId, user != null ? user.getUsername() : "未找到");
|
||||
}
|
||||
|
||||
// 如果通过 unionId 没找到,尝试通过 openId 查找
|
||||
if (user == null && StrUtil.isNotBlank(openId)) {
|
||||
// 尝试从 sys_user 表的 openid 字段查找
|
||||
user = userService.getOne(new LambdaQueryWrapper<User>()
|
||||
.eq(User::getOpenid, openId)
|
||||
.eq(User::getDeleted, 0)
|
||||
.last("limit 1"));
|
||||
log.info("通过 openId {} 查找用户: {}", openId, user != null ? user.getUsername() : "未找到");
|
||||
}
|
||||
|
||||
// 如果还没找到,尝试从 sys_user_oauth 表查找
|
||||
if (user == null && (StrUtil.isNotBlank(unionId) || StrUtil.isNotBlank(openId))) {
|
||||
try {
|
||||
LambdaQueryWrapper<UserOauth> wrapper = new LambdaQueryWrapper<>();
|
||||
if (StrUtil.isNotBlank(unionId)) {
|
||||
wrapper.eq(UserOauth::getUnionid, unionId);
|
||||
} else {
|
||||
wrapper.eq(UserOauth::getOauthId, openId);
|
||||
}
|
||||
wrapper.eq(UserOauth::getDeleted, 0);
|
||||
|
||||
UserOauth userOauth = null;
|
||||
if (userOauthService != null) {
|
||||
userOauth = userOauthService.getOne(wrapper);
|
||||
}
|
||||
|
||||
if (userOauth != null && userOauth.getUserId() != null) {
|
||||
user = userService.getAllByUserId(String.valueOf(userOauth.getUserId()));
|
||||
log.info("通过 UserOauth 查找到用户: {}", user != null ? user.getUsername() : "未找到");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("通过 UserOauth 查找用户失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
return WechatScanResponse.notBound("该微信未绑定平台账号,请先在平台注册并绑定微信");
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if (user.getStatus() != null && user.getStatus() != 0) {
|
||||
return WechatScanResponse.notBound("账号已被冻结");
|
||||
}
|
||||
|
||||
// 生成 JWT token
|
||||
JwtSubject jwtSubject = new JwtSubject(user.getUsername(), user.getTenantId());
|
||||
String accessToken = JwtUtil.buildToken(jwtSubject, configProperties.getTokenExpireTime(), configProperties.getTokenKey());
|
||||
|
||||
// 更新扫码登录数据
|
||||
qrLoginData.setStatus(QR_LOGIN_STATUS_CONFIRMED);
|
||||
qrLoginData.setUserId(user.getUserId());
|
||||
qrLoginData.setUsername(user.getUsername());
|
||||
qrLoginData.setAccessToken(accessToken);
|
||||
qrLoginData.setTenantId(user.getTenantId());
|
||||
// 更新Redis中的数据
|
||||
redisUtil.set(redisKey, qrLoginData, 60L, TimeUnit.SECONDS);
|
||||
|
||||
log.info("微信扫码登录成功,用户 {} 确认扫码登录,token: {}", user.getUsername(), token);
|
||||
|
||||
// 清除敏感信息
|
||||
user.setPassword(null);
|
||||
|
||||
return WechatScanResponse.success(accessToken, user, user.getTenantId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gxwebsoft.common.system.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -9,6 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -28,6 +30,7 @@ public class WxService {
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
private static final String ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN";
|
||||
private static final String MP_OFFICIAL_ACCESS_TOKEN_KEY = "MP_OFFICIAL_ACCESS_TOKEN";
|
||||
|
||||
/**
|
||||
* 获取微信AccessToken(使用默认租户)
|
||||
@@ -107,4 +110,160 @@ public class WxService {
|
||||
throw new RuntimeException("获取微信AccessToken失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信公众号 AppID
|
||||
*/
|
||||
public String getOfficialAppId() {
|
||||
return getOfficialAppId(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信公众号 AppID(支持指定租户ID)
|
||||
*/
|
||||
public String getOfficialAppId(Integer tenantId) {
|
||||
if (tenantId == null) {
|
||||
tenantId = 10048;
|
||||
}
|
||||
JSONObject setting = settingService.getBySettingKey("wx-official");
|
||||
if (setting == null) {
|
||||
throw new RuntimeException("请先配置微信公众号");
|
||||
}
|
||||
String appId = setting.getString("appId");
|
||||
if (StrUtil.isBlank(appId)) {
|
||||
throw new RuntimeException("微信公众号配置不完整");
|
||||
}
|
||||
return appId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信公众号 AppSecret
|
||||
*/
|
||||
public String getOfficialAppSecret() {
|
||||
return getOfficialAppSecret(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信公众号 AppSecret(支持指定租户ID)
|
||||
*/
|
||||
public String getOfficialAppSecret(Integer tenantId) {
|
||||
if (tenantId == null) {
|
||||
tenantId = 10048;
|
||||
}
|
||||
JSONObject setting = settingService.getBySettingKey("wx-official");
|
||||
if (setting == null) {
|
||||
throw new RuntimeException("请先配置微信公众号");
|
||||
}
|
||||
String appSecret = setting.getString("appSecret");
|
||||
if (StrUtil.isBlank(appSecret)) {
|
||||
throw new RuntimeException("微信公众号配置不完整");
|
||||
}
|
||||
return appSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信公众号 AccessToken(用于 API 调用,非用户授权)
|
||||
*/
|
||||
public String getOfficialAccessToken() {
|
||||
return getOfficialAccessToken(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信公众号 AccessToken(支持指定租户ID)
|
||||
*/
|
||||
public String getOfficialAccessToken(Integer tenantId) {
|
||||
if (tenantId == null) {
|
||||
tenantId = 10048;
|
||||
}
|
||||
|
||||
String key = MP_OFFICIAL_ACCESS_TOKEN_KEY + ":" + tenantId;
|
||||
|
||||
// 从缓存获取
|
||||
String cachedToken = redisTemplate.opsForValue().get(key);
|
||||
if (StrUtil.isNotBlank(cachedToken)) {
|
||||
try {
|
||||
JSONObject tokenData = JSON.parseObject(cachedToken);
|
||||
String accessToken = tokenData.getString("access_token");
|
||||
if (StrUtil.isNotBlank(accessToken)) {
|
||||
log.debug("从缓存获取公众号access_token: {}", accessToken);
|
||||
return accessToken;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析缓存的公众号access_token失败: {}", e.getMessage());
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存中没有,重新获取
|
||||
try {
|
||||
String appId = getOfficialAppId(tenantId);
|
||||
String appSecret = getOfficialAppSecret(tenantId);
|
||||
|
||||
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
|
||||
+ appId + "&secret=" + appSecret;
|
||||
String response = HttpUtil.get(apiUrl);
|
||||
|
||||
JSONObject result = JSON.parseObject(response);
|
||||
String accessToken = result.getString("access_token");
|
||||
if (StrUtil.isNotBlank(accessToken)) {
|
||||
JSONObject tokenData = new JSONObject();
|
||||
tokenData.put("access_token", accessToken);
|
||||
tokenData.put("expires_in", result.get("expires_in"));
|
||||
redisTemplate.opsForValue().set(key, tokenData.toJSONString(), 7000L, TimeUnit.SECONDS);
|
||||
log.info("获取新的公众号access_token成功: {}", accessToken);
|
||||
return accessToken;
|
||||
} else {
|
||||
throw new RuntimeException("获取公众号AccessToken失败: " + response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取微信公众号AccessToken失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("获取微信公众号AccessToken失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过授权码获取用户 AccessToken(用户授权后使用)
|
||||
* 用于 OAuth2 授权流程,获取用户信息
|
||||
*/
|
||||
public JSONObject getOfficialUserAccessToken(String code, Integer tenantId) {
|
||||
if (tenantId == null) {
|
||||
tenantId = 10048;
|
||||
}
|
||||
try {
|
||||
String appId = getOfficialAppId(tenantId);
|
||||
String appSecret = getOfficialAppSecret(tenantId);
|
||||
|
||||
String apiUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
|
||||
+ appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code";
|
||||
|
||||
String response = HttpUtil.get(apiUrl);
|
||||
log.info("获取用户AccessToken响应: {}", response);
|
||||
|
||||
JSONObject result = JSON.parseObject(response);
|
||||
String accessToken = result.getString("access_token");
|
||||
if (StrUtil.isBlank(accessToken)) {
|
||||
String errMsg = result.getString("errmsg");
|
||||
throw new RuntimeException("获取用户授权失败: " + errMsg);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户授权AccessToken失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("获取用户授权失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信公众号用户信息
|
||||
*/
|
||||
public JSONObject getOfficialUserInfo(String accessToken, String openId) {
|
||||
try {
|
||||
String apiUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
|
||||
String response = HttpUtil.get(apiUrl);
|
||||
log.info("获取用户信息响应: {}", response);
|
||||
return JSON.parseObject(response);
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户信息失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("获取用户信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user