farm模块-小程序登录
This commit is contained in:
@@ -35,6 +35,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
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.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
@@ -123,23 +124,30 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ========== 小程序登录相关 ==========
|
// ========== 小程序登录相关 ==========
|
||||||
|
@PermitAll
|
||||||
@PostMapping("/get-access-token")
|
@PostMapping("/get-access-token")
|
||||||
@Operation(summary = "小程序调用凭据")
|
@Operation(summary = "小程序调用凭据")
|
||||||
public CommonResult<String> getAccessToken() {
|
public CommonResult<String> getAccessToken() {
|
||||||
return success(authService.getAccessToken());
|
return success(authService.getAccessToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PermitAll
|
||||||
@PostMapping("/get-session")
|
@PostMapping("/get-session")
|
||||||
@Operation(summary = "小程序session获取")
|
@Operation(summary = "小程序session获取")
|
||||||
public CommonResult<WxLoginRespVO> getSession(@RequestBody @Valid WxLoginReqVO reqVO) {
|
public CommonResult<WxLoginRespVO> getSession(@RequestBody @Valid WxLoginReqVO reqVO) {
|
||||||
return success(authService.getSession(reqVO));
|
return success(authService.getSession(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PermitAll
|
||||||
@PostMapping("/get-phone-number")
|
@PostMapping("/get-phone-number")
|
||||||
@Operation(summary = "小程序手机号解密及绑定用户")
|
@Operation(summary = "小程序手机号解密及绑定用户")
|
||||||
public CommonResult<WxLoginRespVO> getPhoneNumber(@RequestBody @Valid WxLoginReqVO reqVO) {
|
public CommonResult<AuthLoginRespVO> getPhoneNumber(@RequestBody @Valid WxLoginReqVO reqVO) {
|
||||||
return success(authService.getPhoneNumber(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 iv;
|
||||||
|
|
||||||
private String session_key;
|
private String sessionKey;
|
||||||
|
|
||||||
private String rawData;
|
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();
|
String getAccessToken();
|
||||||
|
|
||||||
WxLoginRespVO getPhoneNumber(@Valid WxLoginReqVO reqVO);
|
AuthLoginRespVO getPhoneNumber(@Valid WxLoginReqVO reqVO);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,16 +342,25 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WxLoginRespVO getPhoneNumber(WxLoginReqVO reqVO) {
|
public AuthLoginRespVO getPhoneNumber(WxLoginReqVO reqVO) {
|
||||||
try {
|
try {
|
||||||
// 解密手机号数据
|
// 解密手机号数据
|
||||||
String decryptedData = decryptWxData(reqVO.getEncryptedData(), reqVO.getSession_key(), reqVO.getIv(), wxMiniAppId);
|
String decryptedData = decryptWxData(reqVO.getEncryptedData(), reqVO.getSessionKey(), reqVO.getIv(), wxMiniAppId);
|
||||||
|
// 解析JSON数据
|
||||||
// 解析JSON数据并返回
|
WxUserPhoneRespVO bean = JSONUtil.toBean(decryptedData, WxUserPhoneRespVO.class);
|
||||||
return JSONUtil.toBean(decryptedData, WxLoginRespVO.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_USERNAME);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[getPhoneNumber][解密手机号失败,encryptedData: {}, sessionKey: {}, iv: {}]",
|
log.error("[getPhoneNumber][解密手机号失败,encryptedData: {}, sessionKey: {}, iv: {}]",
|
||||||
reqVO.getEncryptedData(), reqVO.getSession_key(), reqVO.getIv(), e);
|
reqVO.getEncryptedData(), reqVO.getSessionKey(), reqVO.getIv(), e);
|
||||||
throw new RuntimeException("解密手机号失败", 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
|
* 根据微信官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
|
||||||
* 参考Node.js实现:WXBizDataCrypt
|
* 参考Node.js实现:WXBizDataCrypt
|
||||||
*
|
*
|
||||||
* @param encryptedData 加密数据
|
* @param encryptedData 加密数据
|
||||||
* @param sessionKey 会话密钥
|
* @param sessionKey 会话密钥
|
||||||
* @param iv 初始向量
|
* @param iv 初始向量
|
||||||
* @param appId 小程序appId
|
* @param appId 小程序appId
|
||||||
* @return 解密后的数据
|
* @return 解密后的数据
|
||||||
*/
|
*/
|
||||||
private String decryptWxData(String encryptedData, String sessionKey, String iv, String appId) {
|
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[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
|
||||||
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
|
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
|
||||||
byte[] ivBytes = Base64.getDecoder().decode(iv);
|
byte[] ivBytes = Base64.getDecoder().decode(iv);
|
||||||
|
|
||||||
// 2. 使用AES-128-CBC解密
|
// 2. 使用AES-128-CBC解密
|
||||||
AES aes = new AES("CBC", "PKCS7Padding", sessionKeyBytes, ivBytes);
|
AES aes = new AES("CBC", "PKCS7Padding", sessionKeyBytes, ivBytes);
|
||||||
byte[] decryptedBytes = aes.decrypt(encryptedBytes);
|
byte[] decryptedBytes = aes.decrypt(encryptedBytes);
|
||||||
|
|
||||||
// 3. 转换为字符串
|
// 3. 转换为字符串
|
||||||
String decoded = new String(decryptedBytes, StandardCharsets.UTF_8);
|
String decoded = new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
// 4. 解析JSON并验证watermark
|
// 4. 解析JSON并验证watermark
|
||||||
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(decoded);
|
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(decoded);
|
||||||
cn.hutool.json.JSONObject watermark = jsonObject.getJSONObject("watermark");
|
cn.hutool.json.JSONObject watermark = jsonObject.getJSONObject("watermark");
|
||||||
|
|
||||||
if (watermark == null || !appId.equals(watermark.getStr("appid"))) {
|
if (watermark == null || !appId.equals(watermark.getStr("appid"))) {
|
||||||
throw new RuntimeException("Illegal Buffer: watermark appid验证失败");
|
throw new RuntimeException("Illegal Buffer: watermark appid验证失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
return decoded;
|
return decoded;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[decryptWxData][解密失败,encryptedData: {}, sessionKey: {}, iv: {}, appId: {}]",
|
log.error("[decryptWxData][解密失败,encryptedData: {}, sessionKey: {}, iv: {}, appId: {}]",
|
||||||
encryptedData, sessionKey, iv, appId, e);
|
encryptedData, sessionKey, iv, appId, e);
|
||||||
throw new RuntimeException("Illegal Buffer: 数据解密失败", e);
|
throw new RuntimeException("Illegal Buffer: 数据解密失败", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,4 +214,5 @@ public interface AdminUserService {
|
|||||||
*/
|
*/
|
||||||
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
||||||
|
|
||||||
|
void updateByid(AdminUserDO user);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -569,6 +569,11 @@ public class AdminUserServiceImpl implements AdminUserService {
|
|||||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateByid(AdminUserDO user) {
|
||||||
|
userMapper.updateById(user);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对密码进行加密
|
* 对密码进行加密
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user