Merge remote-tracking branch 'origin/feature-20240104' into feature-20240104

master
elliott 2 years ago
commit 22ddcb532a

@ -0,0 +1,31 @@
package com.flossom.system.api;
import com.flossom.common.core.constant.SecurityConstants;
import com.flossom.common.core.constant.ServiceNameConstants;
import com.flossom.common.core.domain.R;
import com.flossom.system.api.domain.LoginBody;
import com.flossom.system.api.domain.RegisterBody;
import com.flossom.system.api.domain.SysUser;
import com.flossom.system.api.factory.RemoteUserFallbackFactory;
import com.flossom.system.api.model.LoginUser;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
*
*
* @author flossom
*/
@FeignClient(contextId = "remoteAuthService", value = ServiceNameConstants.AUTH_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteAuthService
{
@PostMapping("login")
public R<Map<String, String>> login(@RequestBody LoginBody form);
@PostMapping("register")
public R<?> register(@RequestBody RegisterBody registerBody);
}

@ -0,0 +1,39 @@
package com.flossom.system.api.domain;
/**
*
*
* @author flossom
*/
public class LoginBody
{
/**
*
*/
private String username;
/**
*
*/
private String password;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}

@ -0,0 +1,11 @@
package com.flossom.system.api.domain;
/**
*
*
* @author flossom
*/
public class RegisterBody extends LoginBody
{
}

@ -0,0 +1,42 @@
package com.flossom.system.api.factory;
import com.flossom.common.core.domain.R;
import com.flossom.system.api.RemoteAuthService;
import com.flossom.system.api.RemoteUserService;
import com.flossom.system.api.domain.LoginBody;
import com.flossom.system.api.domain.RegisterBody;
import com.flossom.system.api.domain.SysUser;
import com.flossom.system.api.model.LoginUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
*
*
* @author flossom
*/
@Component
public class RemoteAuthFallbackFactory implements FallbackFactory<RemoteAuthService> {
private static final Logger log = LoggerFactory.getLogger(RemoteAuthFallbackFactory.class);
@Override
public RemoteAuthService create(Throwable throwable) {
log.error("用户服务调用失败:{}", throwable.getMessage());
return new RemoteAuthService() {
@Override
public R<Map<String, String>> login(LoginBody form) {
return R.fail("用户登录失败:" + throwable.getMessage());
}
@Override
public R<?> register(RegisterBody registerBody) {
return R.fail("用户注册失败:" + throwable.getMessage());
}
};
}
}

@ -56,4 +56,9 @@ public class CacheConstants
* IP cache key
*/
public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList";
/**
* session_key
*/
public static final String wx_session_key_cache = "wx_session_key:";
}

@ -77,4 +77,11 @@ public class UserConstants
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MAX_LENGTH = 20;
/**
*
*/
public static final String WX_SYSTEM_USER_USERNAME = "wxhz";
public static final String WX_SYSTEM_USER_PASSWORD = "hzAnonymous9090.@";
}

@ -2,20 +2,20 @@ package com.flossom.hzMapper.mapper;
import com.flossom.hzMapper.domain.WxUserMember;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Mapper
*
*
* @author flossom
* @date 2023-12-08
*/
public interface WxUserMemberMapper
{
public interface WxUserMemberMapper {
/**
*
*
*
* @param id
* @return
*/
@ -23,7 +23,7 @@ public interface WxUserMemberMapper
/**
*
*
*
* @param wxUserMember
* @return
*/
@ -31,7 +31,7 @@ public interface WxUserMemberMapper
/**
*
*
*
* @param wxUserMember
* @return
*/
@ -39,7 +39,7 @@ public interface WxUserMemberMapper
/**
*
*
*
* @param wxUserMember
* @return
*/
@ -47,7 +47,7 @@ public interface WxUserMemberMapper
/**
*
*
*
* @param id
* @return
*/
@ -55,9 +55,21 @@ public interface WxUserMemberMapper
/**
*
*
*
* @param ids
* @return
*/
public int deleteWxUserMemberByIds(Long[] ids);
/**
*
*
* @param wxUserMember
* @return
*/
public void insertTouristUser(WxUserMember wxUserMember);
WxUserMember selectWxUserMemberByOpenId(@Param("openid") String openid);
void upgradeMember(@Param("phoneNumber") String phoneNumber, @Param("id") String id);
}

@ -75,7 +75,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectWxUserMemberVo"/>
where id = #{id}
</select>
<select id="selectWxUserMemberByOpenId" parameterType="String" resultType="com.flossom.hzMapper.domain.WxUserMember">
<include refid="selectWxUserMemberVo"/>
WHERE openid = #{openid}
</select>
<insert id="insertWxUserMember" parameterType="WxUserMember" useGeneratedKeys="true" keyProperty="id">
insert into wx_user_member
<trim prefix="(" suffix=")" suffixOverrides=",">
@ -178,6 +182,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{id}
</update>
<update id="upgradeMember">
UPDATE `wx_user_member`
SET `user_type` = 2,
`level` = 1,
`mobile` = #{phoneNumber},
`login_time` = sysdate(),
`update_time` = sysdate()
WHERE
`id` = #{id};
</update>
<delete id="deleteWxUserMemberById" parameterType="Long">
delete from wx_user_member where id = #{id}
</delete>
@ -188,4 +203,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{id}
</foreach>
</delete>
<insert id="insertTouristUser" parameterType="WxUserMember" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `wx_user_member` (login_time, create_time) VALUES(sysdate(), sysdate());
</insert>
</mapper>

@ -78,6 +78,12 @@
<artifactId>flossom-common-swagger</artifactId>
</dependency>
<!-- flossom Api System -->
<dependency>
<groupId>com.flossom</groupId>
<artifactId>flossom-api-system</artifactId>
</dependency>
<dependency>
<groupId>com.flossom</groupId>
<artifactId>flossom-common-hzMapper</artifactId>

@ -1,13 +1,8 @@
package com.flossom.miniProgram.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.flossom.common.core.web.controller.BaseController;
import com.flossom.common.core.web.domain.AjaxResult;
import com.flossom.miniProgram.domain.vo.WxCode2SessionRet;
import com.flossom.miniProgram.service.IWxUserMemberService;
import com.flossom.miniProgram.utils.MiniProgramUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -22,49 +17,32 @@ public class UserMemberController extends BaseController {
private IWxUserMemberService wxUserMemberService;
/**
* userNo \
* 使 userNo id
*
*
* @param code
* @param rawData
* @param signature 使 sha1( rawData + sessionkey )
* @return
* @throws Exception
*/
@PostMapping("/obtainUserMemberInfo")
public AjaxResult obtainUserMemberInfo(@RequestParam(value = "userNo", required = false) Long userNo) {
return AjaxResult.success(wxUserMemberService.obtainUserMemberInfo(userNo));
}
@PostMapping("/login")
public AjaxResult getJwt(@RequestParam(value = "code") String code,
@RequestParam(value = "rawData", required = false) String rawData,
@RequestParam(value = "signature", required = false) String signature) throws Exception {
// 1. 使用临时凭证code获取 appi + appsecret + code
WxCode2SessionRet wxCode2SessionRet = JSON.parseObject(MiniProgramUtils.getSessionKeyAndOpenId(code), WxCode2SessionRet.class);
if (0 != wxCode2SessionRet.getErrcode()) {
return AjaxResult.error(wxCode2SessionRet.getErrmsg());
}
// 2.校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey)
String signature2 = DigestUtils.sha1Hex(rawData + wxCode2SessionRet.getSession_key());
if (!signature.equals(signature2)) {
return AjaxResult.error("签名校验失败");
}
// 5.根据返回的User实体类判断用户是否是新用户是的话将用户信息存到数据库
// 用户非敏感信息rawData
// 签名signature
JSONObject rawDataJson = JSON.parseObject(rawData);
// LambdaQueryWrapper<User> lqw = Wrappers.lambdaQuery();
// lqw.eq(User::getOpenId, openid);
// User user = userService.getOne(lqw);
// if (user == null) {
// // 用户信息入库
// String nickName = rawDataJson.getString("nickName");
// String avatarUrl = rawDataJson.getString("avatarUrl");
// user = new User();
// user.setOpenId(openid);
// user.setAvatar(avatarUrl);
// user.setNickName(nickName);
// userService.save(user);
// }
return AjaxResult.success();
public AjaxResult login(
@RequestParam(value = "code") String code,
@RequestParam(value = "rawData") String rawData,
@RequestParam(value = "signature") String signature) throws Exception {
return AjaxResult.success(wxUserMemberService.login(code, rawData, signature));
}
/**
*
* 1 wx.login
* 2getPhoneNumber code wx.login code
*
* @param code
* @return
*/
@PostMapping("/upgradeMember")
public AjaxResult upgradeMember(@RequestParam(value = "code") String code) throws Exception {
return wxUserMemberService.upgradeMember(code);
}
}

@ -0,0 +1,40 @@
package com.flossom.miniProgram.domain.vo;
import com.flossom.hzMapper.domain.WxUserMember;
public class LoginUserVo extends WxUserMember {
/**
*
*/
private String accessToken;
/**
*
*/
private String expiresIn;
public LoginUserVo() {
}
public LoginUserVo(String accessToken, String expiresIn) {
this.accessToken = accessToken;
this.expiresIn = expiresIn;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
}

@ -0,0 +1,41 @@
package com.flossom.miniProgram.domain.vo;
public class PhoneInfo {
private String phoneNumber;
private String purePhoneNumber;
private String countryCode;
public PhoneInfo() {
}
public PhoneInfo(String phoneNumber, String purePhoneNumber, String countryCode) {
this.phoneNumber = phoneNumber;
this.purePhoneNumber = purePhoneNumber;
this.countryCode = countryCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPurePhoneNumber() {
return purePhoneNumber;
}
public void setPurePhoneNumber(String purePhoneNumber) {
this.purePhoneNumber = purePhoneNumber;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
}

@ -0,0 +1,44 @@
package com.flossom.miniProgram.domain.vo;
/**
*
*/
public class WxCode2PhoneRet {
private PhoneInfo phoneInfo;
private Integer errcode;
private String errmsg;
public WxCode2PhoneRet() {
}
public WxCode2PhoneRet(PhoneInfo phoneInfo, Integer errcode, String errmsg) {
this.phoneInfo = phoneInfo;
this.errcode = errcode;
this.errmsg = errmsg;
}
public PhoneInfo getPhoneInfo() {
return phoneInfo;
}
public void setPhoneInfo(PhoneInfo phoneInfo) {
this.phoneInfo = phoneInfo;
}
public Integer getErrcode() {
return errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}

@ -1,9 +1,14 @@
package com.flossom.miniProgram.service;
import com.flossom.common.core.web.domain.AjaxResult;
import com.flossom.hzMapper.domain.WxUserMember;
import com.flossom.miniProgram.domain.vo.LoginUserVo;
public interface IWxUserMemberService {
WxUserMember obtainUserMemberInfo(Long userNo);
LoginUserVo obtainUserMemberInfo(Long userNo);
LoginUserVo login(String code, String rawData, String signature) throws Exception;
AjaxResult upgradeMember(String code) throws Exception;
}

@ -1,27 +1,176 @@
package com.flossom.miniProgram.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.flossom.common.core.constant.CacheConstants;
import com.flossom.common.core.constant.UserConstants;
import com.flossom.common.core.domain.R;
import com.flossom.common.core.exception.ServiceException;
import com.flossom.common.core.web.domain.AjaxResult;
import com.flossom.common.redis.service.RedisService;
import com.flossom.common.security.utils.SecurityUtils;
import com.flossom.hzMapper.domain.WxUserMember;
import com.flossom.hzMapper.mapper.WxUserMemberMapper;
import com.flossom.miniProgram.domain.vo.LoginUserVo;
import com.flossom.miniProgram.domain.vo.WxCode2PhoneRet;
import com.flossom.miniProgram.domain.vo.WxCode2SessionRet;
import com.flossom.miniProgram.service.IWxUserMemberService;
import com.flossom.miniProgram.utils.MiniProgramUtils;
import com.flossom.system.api.RemoteAuthService;
import com.flossom.system.api.RemoteUserService;
import com.flossom.system.api.domain.LoginBody;
import com.flossom.system.api.domain.RegisterBody;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class WxUserMemberServiceImpl implements IWxUserMemberService {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private WxUserMemberMapper wxUserMemberMapper;
@Autowired
private RemoteAuthService remoteAuthService;
@Autowired
private RemoteUserService remoteUserService;
@Autowired
private RedisService redisService;
@Override
public WxUserMember obtainUserMemberInfo(Long userNo) {
public LoginUserVo obtainUserMemberInfo(Long userNo) {
WxUserMember wxUserMember = new WxUserMember();
if (userNo != null) {
// 根据 userNo 查询用户信息
wxUserMember = wxUserMemberMapper.selectWxUserMemberById(userNo);
}
if (userNo == null || wxUserMember == null) {
// 未注册用户,就注册游客账户
// 微信用户表注册
wxUserMemberMapper.insertTouristUser(wxUserMember);
// 系统表注册
RegisterBody registerBody = new RegisterBody();
registerBody.setUsername(UserConstants.WX_SYSTEM_USER_USERNAME + wxUserMember.getId());
registerBody.setPassword(UserConstants.WX_SYSTEM_USER_PASSWORD);
R<?> registerResult = remoteAuthService.register(registerBody);
if (R.FAIL == registerResult.getCode()) {
throw new ServiceException(registerResult.getMsg());
}
}
// 获取token
LoginBody loginBody = new LoginBody();
loginBody.setUsername(UserConstants.WX_SYSTEM_USER_USERNAME + wxUserMember.getId());
loginBody.setPassword(UserConstants.WX_SYSTEM_USER_PASSWORD);
R<Map<String, String>> loginResult = remoteAuthService.login(loginBody);
if (loginResult.getCode() == R.FAIL) {
throw new ServiceException(loginResult.getMsg());
}
LoginUserVo loginUserVo = new LoginUserVo();
BeanUtils.copyProperties(wxUserMember, loginUserVo);
loginUserVo.setAccessToken(loginResult.getData().get("access_token"));
loginUserVo.setExpiresIn(loginResult.getData().get("expires_in"));
return loginUserVo;
}
@Override
public LoginUserVo login(String code, String rawData, String signature) throws Exception {
// 1、使用临时凭证 code 获取 appi + appsecret + code
WxCode2SessionRet wxCode2SessionRet = JSON.parseObject(MiniProgramUtils.getSessionKeyAndOpenId(code), WxCode2SessionRet.class);
if (200 != wxCode2SessionRet.getErrcode()) {
logger.error(wxCode2SessionRet.getErrmsg());
return null;
}
// 2、校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey)
String signature2 = DigestUtils.sha1Hex(rawData + wxCode2SessionRet.getSession_key());
if (!signature.equals(signature2)) {
logger.error("签名校验失败code = {}", code);
return null;
}
// 3、根据 openid 查询用户信息
WxUserMember wxUserMember = wxUserMemberMapper.selectWxUserMemberByOpenId(wxCode2SessionRet.getOpenid());
if (wxUserMember == null) {
// 未注册用户,就注册游客账户
// 2.1、封装微信用户信息
/**
* rawData:
* {
* "nickName": "Band",
* "avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"
* "gender": 1,//不再返回
* "language": "zh_CN", //强制返回 “zh_CN”
* "city": "Guangzhou", //不再返回
* "province": "Guangdong", //不再返回
* "country": "CN", //不再返回
* }
*/
JSONObject rawDataJson = JSON.parseObject(rawData);
wxUserMember.setNickname(rawDataJson.getString("nickName"));
wxUserMember.setHeadimg(rawDataJson.getString("avatarUrl"));
wxUserMember.setOpenid(wxCode2SessionRet.getOpenid());
wxUserMember.setUnionid(wxCode2SessionRet.getUnionid());
/**
* TODO: session_keyredis
*/
redisService.setCacheObject(CacheConstants.wx_session_key_cache + wxUserMember.getId(), wxCode2SessionRet.getSession_key());
// 3.2、微信用户表注册
wxUserMemberMapper.insertWxUserMember(wxUserMember);
// 3.3、系统表注册
RegisterBody registerBody = new RegisterBody();
registerBody.setUsername(UserConstants.WX_SYSTEM_USER_USERNAME + wxUserMember.getId());
registerBody.setPassword(UserConstants.WX_SYSTEM_USER_PASSWORD);
R<?> registerResult = remoteAuthService.register(registerBody);
if (R.FAIL == registerResult.getCode()) {
throw new ServiceException(registerResult.getMsg());
}
}
// 4、获取 token
LoginBody loginBody = new LoginBody();
loginBody.setUsername(UserConstants.WX_SYSTEM_USER_USERNAME + wxUserMember.getId());
loginBody.setPassword(UserConstants.WX_SYSTEM_USER_PASSWORD);
R<Map<String, String>> loginResult = remoteAuthService.login(loginBody);
if (loginResult.getCode() == R.FAIL) {
throw new ServiceException(loginResult.getMsg());
}
return wxUserMember;
// 5、返回登录信息
LoginUserVo loginUserVo = new LoginUserVo();
BeanUtils.copyProperties(wxUserMember, loginUserVo);
loginUserVo.setAccessToken(loginResult.getData().get("access_token"));
loginUserVo.setExpiresIn(loginResult.getData().get("expires_in"));
return loginUserVo;
}
@Override
public AjaxResult upgradeMember(String code) throws Exception {
WxCode2PhoneRet wxCode2PhoneRet = JSON.parseObject(MiniProgramUtils.getPhone(code), WxCode2PhoneRet.class);
if (wxCode2PhoneRet.getErrcode() != 200) {
logger.error("用户手机号码获取失败");
return AjaxResult.error("手机号获取失败");
}
String username = SecurityUtils.getLoginUser().getUsername();
if (StringUtils.isBlank(username)) {
logger.error("获取用户信息失败");
return AjaxResult.error("获取用户信息失败");
}
String id = username.replace(UserConstants.WX_SYSTEM_USER_USERNAME, "");
wxUserMemberMapper.upgradeMember(wxCode2PhoneRet.getPhoneInfo().getPhoneNumber(), id);
return AjaxResult.success();
}
}

@ -12,32 +12,33 @@ public class MiniProgramUtils {
private static final String WX_SECRET = "";
private static final String loginUrl = "https://api.weixin.qq.com/sns/jscode2session";
private static final String getPhoneUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
public static String getSessionKeyAndOpenId(String jsCode) throws Exception {
return getSessionKeyAndOpenId(WX_APPID, WX_SECRET, jsCode, "authorization_code");
}
/**
* code openid session_key
* code openid session_key
*
* @param appid
* @param secret
* @param jsCode
* @param grant_type
*
*
* @return <p>
* {
* "openid":"xxxxxx",
* "session_key":"xxxxx",
* "unionid":"xxxxx",
* "errcode":0,
* "errmsg":"xxxxx"
* "openid":"xxxxxx",
* "session_key":"xxxxx",
* "unionid":"xxxxx",
* "errcode":0,
* "errmsg":"xxxxx"
* }
*
* <p>
* errcode
* 40029 code js_code
* 45011 api minute-quota reach limit mustslower retry next minute API
* 40226 code blocked
* -1 system error
* 40029 code js_code
* 45011 api minute-quota reach limit mustslower retry next minute API
* 40226 code blocked
* -1 system error
*/
public static String getSessionKeyAndOpenId(String appid, String secret, String jsCode, String grant_type) throws Exception {
Map<String, String> params = new HashMap<>();
@ -48,5 +49,22 @@ public class MiniProgramUtils {
return HttpClientUtils.getParameters(loginUrl, params);
}
/**
* @param code
* @return <p></p>
* errcode number
* errmsg string
* phone_info object
*
* phoneNumber string
* purePhoneNumber string
* countryCode string
*/
public static String getPhone(String code) throws Exception {
Map<String, String> params = new HashMap();
params.put("code", code);
return HttpClientUtils.postParameters(getPhoneUrl, params);
}
}

Loading…
Cancel
Save