完善小程序解密获取手机号,更新系统用户名功能

This commit is contained in:
wanggeng 2021-08-03 10:58:12 +08:00
parent 12fc61234a
commit 23e8164b2d
14 changed files with 277 additions and 104 deletions

View File

@ -15,7 +15,7 @@ import org.springframework.context.annotation.Configuration;
**/ **/
@Configuration @Configuration
@ConfigurationProperties(prefix = "open-platform.wechat.mini-app") @ConfigurationProperties(prefix = "open-platform.wechat.mini-app")
public class MiniAppProperties { public class MiniappProperties {
private Boolean active; private Boolean active;
private String authorizeUrl; private String authorizeUrl;

View File

@ -62,7 +62,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
"/approute/**", "/approute/**",
"/wechat/**", "/wechat/**",
"/wechat/route/**", "/wechat/route/**",
"/wechat/miniapp/**", "/wechat-miniapp/**",
"/route/file/**", "/route/file/**",
"/api/sms/getverificationcode/*", "/api/sms/getverificationcode/*",
"/api/user/getsignintype/**") "/api/user/getsignintype/**")

View File

@ -2,8 +2,8 @@ package ink.wgink.login.wechat.controller.app;
import ink.wgink.annotation.CheckRequestBodyAnnotation; import ink.wgink.annotation.CheckRequestBodyAnnotation;
import ink.wgink.interfaces.consts.ISystemConstant; import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.login.wechat.pojo.vos.update.MiniappUpdatePhoneVO;
import ink.wgink.login.wechat.service.sign.IMiniappSignService; import ink.wgink.login.wechat.service.sign.IMiniappSignService;
import ink.wgink.module.wechat.pojo.vos.miniapp.MiniappUpdatePhoneVO;
import ink.wgink.pojo.result.ErrorResult; import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.pojo.result.SuccessResultData; import ink.wgink.pojo.result.SuccessResultData;
import io.swagger.annotations.*; import io.swagger.annotations.*;

View File

@ -7,7 +7,7 @@ import ink.wgink.login.wechat.pojo.vos.sign.MiniappLoginVO;
import ink.wgink.login.wechat.service.sign.IMiniappSignService; import ink.wgink.login.wechat.service.sign.IMiniappSignService;
import ink.wgink.pojo.result.ErrorResult; import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.pojo.result.SuccessResultData; import ink.wgink.pojo.result.SuccessResultData;
import ink.wgink.properties.wechat.miniapp.MiniAppProperties; import ink.wgink.properties.wechat.miniapp.MiniappProperties;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponse;
@ -34,7 +34,7 @@ import org.springframework.web.bind.annotation.RestController;
public class SignMiniappController { public class SignMiniappController {
@Autowired @Autowired
private MiniAppProperties miniAppProperties; private MiniappProperties miniAppProperties;
@Autowired @Autowired
private IMiniappSignService miniAppSignService; private IMiniappSignService miniAppSignService;

View File

@ -0,0 +1,81 @@
package ink.wgink.login.wechat.pojo.bos.miniapp;
/**
* @ClassName: MiniappPhoneBO
* @Description: 小程序手机号
* @Author: wanggeng
* @Date: 2021/8/3 10:46 上午
* @Version: 1.0
*/
public class MiniappPhoneDataBO {
private String phoneNumber;
private String purePhoneNumber;
private String countryCode;
private WaterMark watermark;
public String getPhoneNumber() {
return phoneNumber == null ? "" : phoneNumber.trim();
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPurePhoneNumber() {
return purePhoneNumber == null ? "" : purePhoneNumber.trim();
}
public void setPurePhoneNumber(String purePhoneNumber) {
this.purePhoneNumber = purePhoneNumber;
}
public String getCountryCode() {
return countryCode == null ? "" : countryCode.trim();
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public WaterMark getWatermark() {
return watermark;
}
public void setWatermark(WaterMark watermark) {
this.watermark = watermark;
}
public static class WaterMark {
private String appid;
private Long timestamp;
public String getAppid() {
return appid == null ? "" : appid.trim();
}
public void setAppid(String appid) {
this.appid = appid;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"appid\":\"")
.append(appid).append('\"');
sb.append(",\"timestamp\":")
.append(timestamp);
sb.append('}');
return sb.toString();
}
}
}

View File

@ -0,0 +1,50 @@
package ink.wgink.login.wechat.pojo.vos.update;
import ink.wgink.annotation.CheckEmptyAnnotation;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: MiniAppUpdatePhoneVO
* @Description: 小程序更新手机
* @Author: wanggeng
* @Date: 2021/8/2 5:20 下午
* @Version: 1.0
*/
@ApiModel
public class MiniappUpdatePhoneVO {
@ApiModelProperty(name = "encryptedData", value = "加密数据")
@CheckEmptyAnnotation(name = "加密数据")
private String encryptedData;
@ApiModelProperty(name = "iv", value = "向量")
@CheckEmptyAnnotation(name = "iv")
private String iv;
public String getEncryptedData() {
return encryptedData == null ? "" : encryptedData.trim();
}
public void setEncryptedData(String encryptedData) {
this.encryptedData = encryptedData;
}
public String getIv() {
return iv == null ? "" : iv.trim();
}
public void setIv(String iv) {
this.iv = iv;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"encryptedData\":\"")
.append(encryptedData).append('\"');
sb.append(",\"iv\":\"")
.append(iv).append('\"');
sb.append('}');
return sb.toString();
}
}

View File

@ -1,37 +0,0 @@
package ink.wgink.login.wechat.pojo.vos.update;
import ink.wgink.annotation.CheckEmptyAnnotation;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: MiniAppUpdatePhoneVO
* @Description: 小程序更新手机
* @Author: wanggeng
* @Date: 2021/8/2 5:20 下午
* @Version: 1.0
*/
@ApiModel
public class UpdatePhoneVO {
@ApiModelProperty(name = "phone", value = "手机号")
@CheckEmptyAnnotation(name = "手机号", regex = "phone")
private String phone;
public String getPhone() {
return phone == null ? "" : phone.trim();
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"phone\":\"")
.append(phone).append('\"');
sb.append('}');
return sb.toString();
}
}

View File

@ -1,7 +1,7 @@
package ink.wgink.login.wechat.service.sign; package ink.wgink.login.wechat.service.sign;
import ink.wgink.login.wechat.pojo.vos.sign.MiniappLoginVO; import ink.wgink.login.wechat.pojo.vos.sign.MiniappLoginVO;
import ink.wgink.module.wechat.pojo.vos.miniapp.MiniappUpdatePhoneVO; import ink.wgink.login.wechat.pojo.vos.update.MiniappUpdatePhoneVO;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;

View File

@ -7,16 +7,20 @@ import ink.wgink.exceptions.WechatAccessTokenForUserException;
import ink.wgink.exceptions.WechatUserInfoException; import ink.wgink.exceptions.WechatUserInfoException;
import ink.wgink.interfaces.consts.ISystemConstant; import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.login.base.service.BaseAppSignService; import ink.wgink.login.base.service.BaseAppSignService;
import ink.wgink.login.wechat.pojo.bos.miniapp.MiniappPhoneDataBO;
import ink.wgink.login.wechat.pojo.vos.sign.MiniappLoginVO; import ink.wgink.login.wechat.pojo.vos.sign.MiniappLoginVO;
import ink.wgink.login.wechat.pojo.vos.update.MiniappUpdatePhoneVO;
import ink.wgink.login.wechat.service.sign.IMiniappSignService; import ink.wgink.login.wechat.service.sign.IMiniappSignService;
import ink.wgink.module.wechat.pojo.bos.miniapp.MiniAppUserInfoBO; import ink.wgink.module.wechat.manager.MiniappManager;
import ink.wgink.module.wechat.pojo.bos.miniapp.MiniappUserInfoBO;
import ink.wgink.module.wechat.pojo.pos.miniapp.MiniappUserPO; import ink.wgink.module.wechat.pojo.pos.miniapp.MiniappUserPO;
import ink.wgink.module.wechat.pojo.vos.miniapp.MiniappUpdatePhoneVO;
import ink.wgink.module.wechat.service.miniapp.IMiniappUserService; import ink.wgink.module.wechat.service.miniapp.IMiniappUserService;
import ink.wgink.properties.wechat.miniapp.MiniAppProperties; import ink.wgink.properties.wechat.miniapp.MiniappProperties;
import ink.wgink.service.user.pojo.pos.UserPO; import ink.wgink.service.user.pojo.pos.UserPO;
import ink.wgink.service.user.pojo.vos.UpdateUsernameVO; import ink.wgink.service.user.pojo.vos.UpdateUsernameVO;
import ink.wgink.service.user.service.IUserService; import ink.wgink.service.user.service.IUserService;
import ink.wgink.util.AesUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -41,7 +45,7 @@ import java.util.Map;
public class MiniappSignServiceImpl extends BaseAppSignService implements IMiniappSignService { public class MiniappSignServiceImpl extends BaseAppSignService implements IMiniappSignService {
@Autowired @Autowired
private MiniAppProperties miniappProperties; private MiniappProperties miniappProperties;
@Autowired @Autowired
private IMiniappUserService miniappUserService; private IMiniappUserService miniappUserService;
@Autowired @Autowired
@ -63,7 +67,7 @@ public class MiniappSignServiceImpl extends BaseAppSignService implements IMinia
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
String resultJson = restTemplate.getForObject(url.toString(), String.class, params); String resultJson = restTemplate.getForObject(url.toString(), String.class, params);
MiniAppUserInfoBO miniAppUserInfoBO = JSONObject.parseObject(resultJson, MiniAppUserInfoBO.class); MiniappUserInfoBO miniAppUserInfoBO = JSONObject.parseObject(resultJson, MiniappUserInfoBO.class);
if (miniAppUserInfoBO == null) { if (miniAppUserInfoBO == null) {
throw new WechatUserInfoException("获取微信用户信息失败"); throw new WechatUserInfoException("获取微信用户信息失败");
} }
@ -76,17 +80,22 @@ public class MiniappSignServiceImpl extends BaseAppSignService implements IMinia
if (userPO == null) { if (userPO == null) {
throw new SearchException("用户不存在"); throw new SearchException("用户不存在");
} }
String token = getMiniappToken(userPO);
// 加入管理队列
MiniappManager.getInstance().addUser(token.substring(0, token.length() - 2), miniAppUserInfoBO);
return getMiniappToken(userPO); return getMiniappToken(userPO);
} }
@Override @Override
public String updatePhone(String token, MiniappUpdatePhoneVO miniappUpdatePhoneVO) throws UnsupportedEncodingException { public String updatePhone(String token, MiniappUpdatePhoneVO miniappUpdatePhoneVO) throws UnsupportedEncodingException {
MiniappPhoneDataBO miniappPhoneData = getMiniappPhoneData(token, miniappUpdatePhoneVO);
String userId = getAppTokenUser(token).getId(); String userId = getAppTokenUser(token).getId();
UserPO userPO = userService.getPO(userId); UserPO userPO = userService.getPO(userId);
if (userPO == null) { if (userPO == null) {
throw new SearchException("非系统用户,请刷新小程序"); throw new SearchException("非系统用户,请刷新小程序");
} }
UserPO userPOByUsername = userService.getPOByUsername(miniappUpdatePhoneVO.getPhone()); String phone = miniappPhoneData.getPhoneNumber();
UserPO userPOByUsername = userService.getPOByUsername(phone);
if (userPOByUsername != null) { if (userPOByUsername != null) {
LOG.debug("手机号存在"); LOG.debug("手机号存在");
// 不是当前用户抛异常 // 不是当前用户抛异常
@ -103,17 +112,36 @@ public class MiniappSignServiceImpl extends BaseAppSignService implements IMinia
} }
LOG.debug("手机不存在,更新用户名"); LOG.debug("手机不存在,更新用户名");
UpdateUsernameVO updateUsernameVO = new UpdateUsernameVO(); UpdateUsernameVO updateUsernameVO = new UpdateUsernameVO();
updateUsernameVO.setUsername(miniappUpdatePhoneVO.getPhone()); updateUsernameVO.setUsername(phone);
updateUsernameVO.setUpdateReason("微信用户手动更新"); updateUsernameVO.setUpdateReason("微信用户手动更新");
userService.updateUsername(userId, updateUsernameVO); userService.updateUsername(userId, updateUsernameVO);
LOG.debug("返回token"); LOG.debug("返回token");
UserPO newUserPO = new UserPO(); UserPO newUserPO = new UserPO();
BeanUtils.copyProperties(userPO, newUserPO); BeanUtils.copyProperties(userPO, newUserPO);
newUserPO.setUserUsername(miniappUpdatePhoneVO.getPhone()); newUserPO.setUserUsername(phone);
miniappUserService.updateIsInitAccount(userId, 0); miniappUserService.updateIsInitAccount(userId, 0);
return getMiniappToken(userPO); return getMiniappToken(userPO);
} }
/**
* 解密获得手机号
*
* @param token
* @param miniappUpdatePhoneVO
* @return
*/
private MiniappPhoneDataBO getMiniappPhoneData(String token, MiniappUpdatePhoneVO miniappUpdatePhoneVO) {
MiniappUserInfoBO miniappUserInfoBO = MiniappManager.getInstance().get(token);
if (miniappUserInfoBO == null) {
throw new SearchException("会话不存在,请重新打开小程序");
}
byte[] encryptedBytes = Base64.decodeBase64(miniappUpdatePhoneVO.getEncryptedData());
byte[] keyBytes = Base64.decodeBase64(miniappUserInfoBO.getSession_key());
byte[] ivBytes = Base64.decodeBase64(miniappUpdatePhoneVO.getIv());
byte[] userInfoBytes = AesUtil.aesCommonDecoderDetail(keyBytes, encryptedBytes, ivBytes, AesUtil.PKCS_7);
return JSONObject.parseObject(userInfoBytes, MiniappPhoneDataBO.class);
}
/** /**
* 获得小程序token * 获得小程序token
* *

View File

@ -0,0 +1,82 @@
package ink.wgink.module.wechat.manager;
import ink.wgink.module.wechat.pojo.bos.miniapp.MiniappUserInfoBO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
*
* @ClassName: WechatMiniAppManager
* @Description: 微信小程序管理
* @Author: WangGeng
* @Date: 2020/5/9 18:05
* @Version: 1.0
**/
public class MiniappManager {
private static final Logger LOG = LoggerFactory.getLogger(MiniappManager.class);
private static MiniappManager miniAppManager = WechatMiniAppManagerBuilder.MINI_APP_MANAGER;
// 超时分钟
private final int TIME_OUT_SECOND = 120 * 60;
private MiniappManager() {
}
private Map<String, MiniappUserInfoBO> users = new ConcurrentHashMap<>();
public static MiniappManager getInstance() {
return miniAppManager;
}
/**
* 添加用户
*
* @param token
* @param miniappUserInfoBO
*/
public void addUser(String token, MiniappUserInfoBO miniappUserInfoBO) {
miniappUserInfoBO.setUpdateTime(System.currentTimeMillis());
users.put(token, miniappUserInfoBO);
}
/**
* 获取小程序应用
*
* @param token
* @return
*/
public MiniappUserInfoBO get(String token) {
return users.get(token);
}
/**
* 删除超时用户
*/
public void removeTimeout() {
LOG.debug("delete timeout wechat mini app user start");
long startTime = System.currentTimeMillis();
List<String> tokenList = new ArrayList<>();
for (Map.Entry<String, MiniappUserInfoBO> kv : users.entrySet()) {
// 超时
if ((startTime - kv.getValue().getUpdateTime()) / 1000 > TIME_OUT_SECOND) {
tokenList.add(kv.getKey());
}
}
for (String token : tokenList) {
users.remove(token);
}
long endTime = System.currentTimeMillis();
LOG.debug("delete timeout wechat mini app user end, delete count: {}, useTime: {}ms", tokenList.size(), endTime - startTime);
}
private static class WechatMiniAppManagerBuilder {
public static final MiniappManager MINI_APP_MANAGER = new MiniappManager();
}
}

View File

@ -10,7 +10,7 @@ package ink.wgink.module.wechat.pojo.bos.miniapp;
* @Date: 2020/5/9 18:29 * @Date: 2020/5/9 18:29
* @Version: 1.0 * @Version: 1.0
**/ **/
public class MiniAppUserInfoBO { public class MiniappUserInfoBO {
private String openid; private String openid;
private String session_key; private String session_key;

View File

@ -1,37 +0,0 @@
package ink.wgink.module.wechat.pojo.vos.miniapp;
import ink.wgink.annotation.CheckEmptyAnnotation;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: MiniAppUpdatePhoneVO
* @Description: 小程序更新手机
* @Author: wanggeng
* @Date: 2021/8/2 5:20 下午
* @Version: 1.0
*/
@ApiModel
public class MiniappUpdatePhoneVO {
@ApiModelProperty(name = "phone", value = "手机号")
@CheckEmptyAnnotation(name = "手机号", regex = "phone")
private String phone;
public String getPhone() {
return phone == null ? "" : phone.trim();
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"phone\":\"")
.append(phone).append('\"');
sb.append('}');
return sb.toString();
}
}

View File

@ -1,9 +1,8 @@
package ink.wgink.module.wechat.startup; package ink.wgink.module.wechat.startup;
import ink.wgink.module.wechat.dao.miniapp.IMiniAppUserDao; import ink.wgink.module.wechat.manager.MiniappManager;
import ink.wgink.module.wechat.dao.official.account.IOfficialAccountUserDao;
import ink.wgink.module.wechat.manager.OfficialAccountAccessTokenManager; import ink.wgink.module.wechat.manager.OfficialAccountAccessTokenManager;
import ink.wgink.properties.wechat.miniapp.MiniAppProperties; import ink.wgink.properties.wechat.miniapp.MiniappProperties;
import ink.wgink.properties.wechat.official.account.OfficialAccountProperties; import ink.wgink.properties.wechat.official.account.OfficialAccountProperties;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -27,17 +26,15 @@ import org.springframework.stereotype.Component;
public class WechatStartUp implements ApplicationRunner { public class WechatStartUp implements ApplicationRunner {
private static final Logger LOG = LoggerFactory.getLogger(WechatStartUp.class); private static final Logger LOG = LoggerFactory.getLogger(WechatStartUp.class);
@Autowired @Autowired
private MiniAppProperties miniAppProperties; private MiniappProperties miniAppProperties;
@Autowired @Autowired
private OfficialAccountProperties officialAccountProperties; private OfficialAccountProperties officialAccountProperties;
@Autowired
private IMiniAppUserDao miniAppUserDao;
@Autowired
private IOfficialAccountUserDao officialAccountUserDao;
@Override @Override
public void run(ApplicationArguments args) throws Exception { public void run(ApplicationArguments args) throws Exception {
new Thread(() -> refreshOfficialAccountAccessToken()).start(); new Thread(() -> {
refreshOfficialAccountAccessToken();
}).start();
} }
@Scheduled(cron = "0 0/5 * * * ?") @Scheduled(cron = "0 0/5 * * * ?")
@ -48,4 +45,15 @@ public class WechatStartUp implements ApplicationRunner {
OfficialAccountAccessTokenManager.getInstance().refreshAccessToken(officialAccountProperties); OfficialAccountAccessTokenManager.getInstance().refreshAccessToken(officialAccountProperties);
} }
/**
* 删除小程序超时用户每10分钟执行一次
*/
@Scheduled(cron = "0 0/10 * * * ?")
public void removeTimeoutMiniAppUser() {
if (!miniAppProperties.getActive()) {
return;
}
MiniappManager.getInstance().removeTimeout();
}
} }

View File

@ -31,7 +31,7 @@
</update> </update>
<!-- 保存 --> <!-- 保存 -->
<insert id="save" parameterType="map"> <insert id="save" parameterType="map" flushCache="true">
INSERT INTO wechat_mini_app_user( INSERT INTO wechat_mini_app_user(
app_id, app_id,
open_id, open_id,
@ -48,7 +48,7 @@
</insert> </insert>
<!-- 更新信息 --> <!-- 更新信息 -->
<update id="updateInfo" parameterType="map"> <update id="updateInfo" parameterType="map" flushCache="true">
UPDATE UPDATE
wechat_mini_app_user wechat_mini_app_user
SET SET
@ -61,7 +61,7 @@
</update> </update>
<!-- 更新是否初始化账号 --> <!-- 更新是否初始化账号 -->
<update id="updateIsInitAccount" parameterType="map"> <update id="updateIsInitAccount" parameterType="map" flushCache="true">
UPDATE UPDATE
wechat_mini_app_user wechat_mini_app_user
SET SET
@ -110,7 +110,7 @@
<if test="appId != null and appId != ''"> <if test="appId != null and appId != ''">
app_id = #{appId} app_id = #{appId}
</if> </if>
<if test="openId != '' and openId != ''"> <if test="openId != null and openId != ''">
AND AND
open_id = #{openId} open_id = #{openId}
</if> </if>
@ -121,6 +121,4 @@
</where> </where>
</select> </select>
</mapper> </mapper>