Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -35,6 +35,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
@@ -123,23 +124,30 @@ public class AuthController {
|
||||
}
|
||||
|
||||
// ========== 小程序登录相关 ==========
|
||||
@PermitAll
|
||||
@PostMapping("/get-access-token")
|
||||
@Operation(summary = "小程序调用凭据")
|
||||
public CommonResult<String> getAccessToken() {
|
||||
return success(authService.getAccessToken());
|
||||
}
|
||||
|
||||
|
||||
@PermitAll
|
||||
@PostMapping("/get-session")
|
||||
@Operation(summary = "小程序session获取")
|
||||
public CommonResult<WxLoginRespVO> getSession(@RequestBody @Valid WxLoginReqVO reqVO) {
|
||||
return success(authService.getSession(reqVO));
|
||||
}
|
||||
|
||||
@PermitAll
|
||||
@PostMapping("/get-phone-number")
|
||||
@Operation(summary = "小程序手机号解密及绑定用户")
|
||||
public CommonResult<WxLoginRespVO> getPhoneNumber(@RequestBody @Valid WxLoginReqVO reqVO) {
|
||||
return success(authService.getPhoneNumber(reqVO));
|
||||
public CommonResult<AuthLoginRespVO> getPhoneNumber(@RequestBody @Valid WxLoginReqVO reqVO) {
|
||||
AuthLoginRespVO phoneNumber = authService.getPhoneNumber(reqVO);
|
||||
if (phoneNumber != null) {
|
||||
return success(phoneNumber);
|
||||
} else {
|
||||
return error(405, "该手机号不是平台用户");
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 短信登录相关 ==========
|
||||
|
||||
@@ -19,7 +19,7 @@ public class WxLoginReqVO {
|
||||
|
||||
private String iv;
|
||||
|
||||
private String session_key;
|
||||
private String sessionKey;
|
||||
|
||||
private String rawData;
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "解密手机号")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class WxUserPhoneRespVO {
|
||||
|
||||
private String phoneNumber;
|
||||
|
||||
private String purePhoneNumber;
|
||||
|
||||
private String countryCode;
|
||||
|
||||
private Watermark watermark;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public static class Watermark {
|
||||
private String timestamp;
|
||||
private String appid;
|
||||
}
|
||||
}
|
||||
@@ -91,5 +91,5 @@ public interface AdminAuthService {
|
||||
|
||||
String getAccessToken();
|
||||
|
||||
WxLoginRespVO getPhoneNumber(@Valid WxLoginReqVO reqVO);
|
||||
AuthLoginRespVO getPhoneNumber(@Valid WxLoginReqVO reqVO);
|
||||
}
|
||||
|
||||
@@ -342,16 +342,25 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxLoginRespVO getPhoneNumber(WxLoginReqVO reqVO) {
|
||||
public AuthLoginRespVO getPhoneNumber(WxLoginReqVO reqVO) {
|
||||
try {
|
||||
// 解密手机号数据
|
||||
String decryptedData = decryptWxData(reqVO.getEncryptedData(), reqVO.getSession_key(), reqVO.getIv(), wxMiniAppId);
|
||||
|
||||
// 解析JSON数据并返回
|
||||
return JSONUtil.toBean(decryptedData, WxLoginRespVO.class);
|
||||
String decryptedData = decryptWxData(reqVO.getEncryptedData(), reqVO.getSessionKey(), reqVO.getIv(), wxMiniAppId);
|
||||
// 解析JSON数据
|
||||
WxUserPhoneRespVO bean = JSONUtil.toBean(decryptedData, WxUserPhoneRespVO.class);
|
||||
// 绑定用户
|
||||
String phoneNumber = bean.getPhoneNumber();
|
||||
AdminUserDO user = userService.getUserByMobile(phoneNumber);
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
user.setOpenid(reqVO.getOpenid());
|
||||
userService.updateByid(user);
|
||||
|
||||
return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
|
||||
} catch (Exception e) {
|
||||
log.error("[getPhoneNumber][解密手机号失败,encryptedData: {}, sessionKey: {}, iv: {}]",
|
||||
reqVO.getEncryptedData(), reqVO.getSession_key(), reqVO.getIv(), e);
|
||||
log.error("[getPhoneNumber][解密手机号失败,encryptedData: {}, sessionKey: {}, iv: {}]",
|
||||
reqVO.getEncryptedData(), reqVO.getSessionKey(), reqVO.getIv(), e);
|
||||
throw new RuntimeException("解密手机号失败", e);
|
||||
}
|
||||
}
|
||||
@@ -360,11 +369,11 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
* 解密微信小程序数据
|
||||
* 根据微信官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
|
||||
* 参考Node.js实现:WXBizDataCrypt
|
||||
*
|
||||
*
|
||||
* @param encryptedData 加密数据
|
||||
* @param sessionKey 会话密钥
|
||||
* @param iv 初始向量
|
||||
* @param appId 小程序appId
|
||||
* @param sessionKey 会话密钥
|
||||
* @param iv 初始向量
|
||||
* @param appId 小程序appId
|
||||
* @return 解密后的数据
|
||||
*/
|
||||
private String decryptWxData(String encryptedData, String sessionKey, String iv, String appId) {
|
||||
@@ -373,25 +382,25 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||
byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
|
||||
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
|
||||
byte[] ivBytes = Base64.getDecoder().decode(iv);
|
||||
|
||||
|
||||
// 2. 使用AES-128-CBC解密
|
||||
AES aes = new AES("CBC", "PKCS7Padding", sessionKeyBytes, ivBytes);
|
||||
byte[] decryptedBytes = aes.decrypt(encryptedBytes);
|
||||
|
||||
|
||||
// 3. 转换为字符串
|
||||
String decoded = new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
// 4. 解析JSON并验证watermark
|
||||
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(decoded);
|
||||
cn.hutool.json.JSONObject watermark = jsonObject.getJSONObject("watermark");
|
||||
|
||||
|
||||
if (watermark == null || !appId.equals(watermark.getStr("appid"))) {
|
||||
throw new RuntimeException("Illegal Buffer: watermark appid验证失败");
|
||||
}
|
||||
|
||||
|
||||
return decoded;
|
||||
} catch (Exception e) {
|
||||
log.error("[decryptWxData][解密失败,encryptedData: {}, sessionKey: {}, iv: {}, appId: {}]",
|
||||
log.error("[decryptWxData][解密失败,encryptedData: {}, sessionKey: {}, iv: {}, appId: {}]",
|
||||
encryptedData, sessionKey, iv, appId, e);
|
||||
throw new RuntimeException("Illegal Buffer: 数据解密失败", e);
|
||||
}
|
||||
|
||||
@@ -214,4 +214,5 @@ public interface AdminUserService {
|
||||
*/
|
||||
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
||||
|
||||
void updateByid(AdminUserDO user);
|
||||
}
|
||||
|
||||
@@ -569,6 +569,11 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateByid(AdminUserDO user) {
|
||||
userMapper.updateById(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密码进行加密
|
||||
*
|
||||
|
||||
@@ -277,6 +277,9 @@ yudao:
|
||||
enable: true
|
||||
ignore-urls:
|
||||
- /jmreport/* # 积木报表,无法携带租户编号
|
||||
- /admin-api/system/auth/get-access-token
|
||||
- /admin-api/system/auth/get-session
|
||||
- /admin-api/system/auth/get-phone-number
|
||||
ignore-visit-urls:
|
||||
- /admin-api/system/user/profile/**
|
||||
- /admin-api/system/auth/**
|
||||
|
||||
Reference in New Issue
Block a user