注册绑定手机号接口

This commit is contained in:
cl
2021-06-01 16:27:40 +08:00
parent 909233ae26
commit 02d385dbe7
12 changed files with 416 additions and 18 deletions

View File

@@ -12,6 +12,7 @@ package com.yami.shop.admin.security;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yami.shop.bean.model.User;
import com.yami.shop.common.util.CacheManagerUtil;
import com.yami.shop.sys.constant.Constant;
import com.yami.shop.security.enums.App;
@@ -122,4 +123,14 @@ public class YamiSysUserDetailsServiceImpl implements YamiUserDetailsService {
public YamiUser loadUserByUserMail(String userMail, String loginPassword) {
return null;
}
@Override
public User loadUserByMobileOrUserName(String mobileOrUserName, Integer loginType) {
return null;
}
@Override
public YamiUser getYamiUser(Integer appId, User user, String bizUserId) {
return null;
}
}

View File

@@ -1,21 +1,31 @@
package com.yami.shop.api.controller;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.shop.api.security.AuthenticationToken;
import com.yami.shop.bean.enums.SmsType;
import com.yami.shop.bean.model.User;
import com.yami.shop.bean.param.UserRegisterParam;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.IPHelper;
import com.yami.shop.common.util.PrincipalUtil;
import com.yami.shop.mp.config.WxMaConfiguration;
import com.yami.shop.security.enums.App;
import com.yami.shop.security.handler.LoginAuthSuccessHandler;
import com.yami.shop.security.model.AppConnect;
import com.yami.shop.security.service.AppConnectService;
import com.yami.shop.security.service.YamiUser;
import com.yami.shop.security.service.YamiUserDetailsService;
import com.yami.shop.security.util.SecurityUtils;
import com.yami.shop.service.SmsLogService;
import com.yami.shop.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.web.bind.annotation.*;
@@ -24,6 +34,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.Date;
import java.util.Objects;
/**
* 用户信息
@@ -38,8 +49,22 @@ public class UserRegisterController {
private final UserService userService;
private final SmsLogService smsLogService;
private final AppConnectService appConnectService;
private final LoginAuthSuccessHandler loginAuthSuccessHandler;
private final WxMaConfiguration wxConfig;
private final YamiUserDetailsService yamiUserDetailsService;
private final PasswordEncoder passwordEncoder;
public static final String CHECK_REGISTER_SMS_FLAG = "checkRegisterSmsFlag";
public static final String CHECK_UPDATE_PWD_SMS_FLAG = "updatePwdSmsFlag";
@PostMapping("/register")
@ApiOperation(value = "注册", notes = "用户注册或绑定手机号接口")
public ResponseEntity<Boolean> register(@Valid @RequestBody UserRegisterParam userRegisterParam) {
@@ -54,19 +79,152 @@ public class UserRegisterController {
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, userRegisterParam.getUserMail()));
if (user == null) {
// 无法获取用户信息
throw new YamiShopBindException("yami.user.no.exist");
throw new YamiShopBindException("无法获取用户信息");
}
if (StrUtil.isBlank(userRegisterParam.getPassword())) {
// 新密码不能为空
throw new YamiShopBindException("yami.user.password.no.exist");
throw new YamiShopBindException("新密码不能为空");
}
if (StrUtil.equals(passwordEncoder.encode(userRegisterParam.getPassword()), user.getLoginPassword())) {
// 新密码不能与原密码相同
throw new YamiShopBindException("yami.user.password.check");
throw new YamiShopBindException("新密码不能与原密码相同");
}
user.setModifyTime(new Date());
user.setLoginPassword(passwordEncoder.encode(userRegisterParam.getPassword()));
userService.updateById(user);
return ResponseEntity.ok().build();
}
@PutMapping("/registerOrBindUser")
@ApiOperation(value="注册或绑定手机号", notes="用户注册或绑定手机号接口")
public ResponseEntity<Void> register(HttpServletRequest request, HttpServletResponse response, @Valid @RequestBody UserRegisterParam userRegisterParam) {
String mobile = userRegisterParam.getMobile();
AppConnect appConnect = null;
User user = null;
String bizUserId = null;
boolean isWxAppId = Objects.equals(userRegisterParam.getAppType(), App.MINI.value()) || Objects.equals(userRegisterParam.getAppType(), App.MP.value());
if(isWxAppId) {
bizUserId = SecurityUtils.getUser().getBizUserId();
}
// 正在进行注册,通过验证码校验
if (Objects.equals(userRegisterParam.getRegisterOrBind(), 1)) {
// 看看有没有校验验证码成功的标识
userService.validate(userRegisterParam, CHECK_REGISTER_SMS_FLAG + userRegisterParam.getCheckRegisterSmsFlag());
// 正在进行申请注册
if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUserMobile,userRegisterParam.getMobile())) > 0) {
// 手机号已存在,无法注册
throw new YamiShopBindException("yami.user.phone.exist");
}
}
// 小程序注册/绑定手机号
else {
YamiUser yamiUser = SecurityUtils.getUser();
appConnect = appConnectService.getByBizUserId(yamiUser.getBizUserId(), App.instance(yamiUser.getAppType()));
// 通过微信手机号校验
if (Objects.equals(2, userRegisterParam.getValidateType())) {
try {
WxMaPhoneNumberInfo wxMaPhoneNumberInfo = wxConfig.wxMaService().getUserService().getPhoneNoInfo(yamiUser.getSessionKey(), userRegisterParam.getEncryptedData(), userRegisterParam.getIvStr());
mobile = wxMaPhoneNumberInfo.getPhoneNumber();
} catch (Exception e) {
// 授权失败,请重新授权
throw new YamiShopBindException(" 授权失败,请重新授权");
}
if (StrUtil.isBlank(mobile)) {
// 无法获取用户手机号信息
throw new YamiShopBindException("无法获取用户手机号信息");
}
user = yamiUserDetailsService.loadUserByMobileOrUserName(mobile, 0);
}
// 通过账号密码校验
else if (Objects.equals(3, userRegisterParam.getValidateType())) {
user = yamiUserDetailsService.loadUserByMobileOrUserName(mobile, 0);
if (user == null) {
// 账号或密码不正确
throw new YamiShopBindException("yami.user.account.error");
}
String encodedPassword = user.getLoginPassword();
String rawPassword = userRegisterParam.getPassword();
// 密码不正确
if (StrUtil.isBlank(encodedPassword) || !passwordEncoder.matches(rawPassword,encodedPassword)){
// 账号或密码不正确
throw new YamiShopBindException("yami.user.account.error");
}
}
// 通过验证码校验
else {
if (!smsLogService.checkValidCode(userRegisterParam.getMobile(), userRegisterParam.getValidCode(), SmsType.VALID)){
// 验证码有误或已过期
throw new YamiShopBindException("yami.user.code.error");
}
}
}
Date now = new Date();
// 尝试用手机号获取用户信息
if (user == null && StrUtil.isNotBlank(mobile)) {
user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile,mobile));
}
// 新建用户
if (user == null) {
user = new User();
if (StrUtil.isBlank(userRegisterParam.getUserName())) {
userRegisterParam.setUserName(mobile);
}
// 如果有用户名,就判断用户名格式是否正确
if (!PrincipalUtil.isUserName(userRegisterParam.getUserName())) {
throw new YamiShopBindException("用户名应由4-16位数字字母下划线组成");
}
user.setModifyTime(now);
user.setUserRegtime(now);
user.setUserRegip(IPHelper.getIpAddr());
user.setStatus(1);
user.setPic(userRegisterParam.getImg());
user.setUserMobile(mobile);
if (StrUtil.isNotBlank(userRegisterParam.getPassword())) {
user.setLoginPassword(passwordEncoder.encode(userRegisterParam.getPassword()));
}
// 用户名就是默认的昵称
user.setNickName(StrUtil.isBlank(userRegisterParam.getNickName())? userRegisterParam.getUserName(): userRegisterParam.getNickName());
// } else {
// String userId = user.getUserId();
// // 绑定账号
// if (Objects.equals(userRegisterParam.getRegisterOrBind(),2)) {
// int count = appConnectService.count(new LambdaQueryWrapper<AppConnect>().eq(AppConnect::getUserId, userId).eq(AppConnect::getAppId, userRegisterParam.getAppType()));
// if (count > 0) {
// throw new YamiShopBindException("该账号已被绑定,请换个账号试试");
// }
// }
}
if(Objects.nonNull(bizUserId)){
appConnect = new AppConnect();
appConnect.setBizUserId(bizUserId);
}
appConnectService.registerOrBindUser(user, appConnect, userRegisterParam.getAppType());
//进行授权登录
UserDetails userDetails = yamiUserDetailsService.getYamiUser(userRegisterParam.getAppType(),user, bizUserId);
AuthenticationToken authenticationToken = new AuthenticationToken();
authenticationToken.setPrincipal(user.getUserMobile());
authenticationToken.setCredentials(user.getLoginPassword());
authenticationToken.setPrincipal(userDetails.getUsername());
authenticationToken.setDetails(userDetails);
authenticationToken.setAuthenticated(true);
loginAuthSuccessHandler.onAuthenticationSuccess(request,response,authenticationToken);
return ResponseEntity.ok().build();
}
}

View File

@@ -13,10 +13,12 @@ package com.yami.shop.api.security;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.emoji.EmojiUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.shop.bean.model.User;
import com.yami.shop.common.annotation.RedisLock;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.CacheManagerUtil;
import com.yami.shop.common.util.PrincipalUtil;
import com.yami.shop.dao.UserMapper;
import com.yami.shop.security.dao.AppConnectMapper;
import com.yami.shop.security.enums.App;
@@ -146,4 +148,28 @@ public class YamiUserServiceImpl implements YamiUserDetailsService {
yamiUser.setPic(user.getPic());
return yamiUser;
}
@Override
public User loadUserByMobileOrUserName(String mobileOrUserName, Integer loginType) {
User user = null;
// 手机验证码登陆,或传过来的账号很像手机号
if (Objects.equals(loginType, 1) || PrincipalUtil.isMobile(mobileOrUserName)) {
user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, mobileOrUserName));
}
return user;
}
@Override
public YamiUser getYamiUser(Integer appId, User user, String bizUserId) {
String name = StrUtil.isBlank(user.getRealName()) ? user.getNickName() : user.getRealName();
YamiUser yamiUser = new YamiUser();
yamiUser.setEnabled(user.getStatus() == 1);
yamiUser.setUserId(user.getUserId());
yamiUser.setBizUserId(bizUserId);
yamiUser.setAppType(appId);
yamiUser.setName(name);
yamiUser.setPic(user.getPic());
yamiUser.setPassword(user.getLoginPassword());
return yamiUser;
}
}

View File

@@ -32,4 +32,28 @@ public class UserRegisterParam {
@ApiModelProperty(value = "应用类型 1小程序 2微信公众号 3 PC 4 h5")
private Integer appType;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "验证码")
private String validCode;
@ApiModelProperty(value = "微信小程序的encryptedData")
private String encryptedData;
@ApiModelProperty(value = "微信小程序的ivStr")
private String ivStr;
@ApiModelProperty(value = "头像")
private String img;
@ApiModelProperty(value = "校验登陆注册验证码成功的标识")
private String checkRegisterSmsFlag;
@ApiModelProperty(value = "验证类型 1验证码验证 2 小程序encryptedData验证 3 密码验证 ")
private Integer validateType;
@ApiModelProperty(value = "验证类型 1注册 2绑定 ")
private Integer registerOrBind;
}

View File

@@ -0,0 +1,36 @@
package com.yami.shop.common.util;
import cn.hutool.core.util.StrUtil;
import java.util.regex.Pattern;
/**
* 正则表达式工具
* @author LGH
*/
public class PrincipalUtil {
/**
* 以1开头后面跟10位数
*/
public static final String MOBILE_REGEXP = "1[0-9]{10}";
/**
* 数字字母下划线 4-16位
*/
public static final String USER_NAME_REGEXP = "([a-zA-Z0-9_]{4,16})";
public static boolean isMobile(String value) {
if(StrUtil.isBlank(value)) {
return false;
}
return Pattern.matches(MOBILE_REGEXP, value);
}
public static boolean isUserName(String value) {
if(StrUtil.isBlank(value)) {
return false;
}
return Pattern.matches(USER_NAME_REGEXP, value);
}
}

View File

@@ -14,7 +14,7 @@
<dependencies>
<dependency>
<groupId>com.yami.shop</groupId>
<artifactId>yami-shop-common</artifactId>
<artifactId>yami-shop-service</artifactId>
<version>${yami.shop.version}</version>
</dependency>
<!--security、oauth2-->

View File

@@ -12,6 +12,7 @@ package com.yami.shop.security.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.model.User;
import com.yami.shop.security.enums.App;
import com.yami.shop.security.model.AppConnect;
@@ -22,4 +23,7 @@ import com.yami.shop.security.model.AppConnect;
public interface AppConnectService extends IService<AppConnect> {
AppConnect getByBizUserId(String bizUserId, App app);
User registerOrBindUser(User user, AppConnect appConnect, Integer appId);
}

View File

@@ -11,10 +11,12 @@
package com.yami.shop.security.service;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
@@ -22,39 +24,91 @@ import java.util.Collections;
/**
* 用户详细信息
*/
@Getter
public class YamiUser extends User {
@Data
public class YamiUser implements UserDetails {
/**
* 用户ID
*/
@Setter
private String userId;
@Setter
private String bizUserId;
@Setter
private String pic;
@Setter
private String name;
@Setter
private boolean debugger;
@Setter
private String password;
public YamiUser(String userId, String password, boolean enabled) {
super(userId, password, enabled,true, true, true , Collections.emptyList());
this.userId = userId;
this.password = password;
private Integer appType;
private boolean enabled;
/**
* 自提点Id
*/
private Long stationId;
/**
* 店铺Id
*/
private Long shopId;
/**
* 小程序session_key
*/
private String sessionKey;
public YamiUser() {
}
public YamiUser(String userId, String bizUserId, Integer appId, boolean enabled) {
super(appId + StrUtil.COLON + bizUserId, "", enabled,true, true, true , Collections.emptyList());
public YamiUser(String userId, String bizUserId, Integer appType, boolean enabled) {
this.userId = userId;
this.bizUserId = bizUserId;
this.appType = appType;
this.enabled = enabled;
}
public YamiUser(String userId, String password, boolean enabled) {
this.userId = userId;
this.password = password;
this.enabled = enabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return StrUtil.isBlank(userId)? bizUserId: userId;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}

View File

@@ -10,6 +10,7 @@
package com.yami.shop.security.service;
import com.yami.shop.bean.model.User;
import com.yami.shop.security.enums.App;
import com.yami.shop.security.exception.UsernameNotFoundExceptionBase;
import com.yami.shop.security.model.AppConnect;
@@ -45,4 +46,8 @@ public interface YamiUserDetailsService extends UserDetailsService {
* @return
*/
YamiUser loadUserByUserMail(String userMail, String loginPassword);
User loadUserByMobileOrUserName(String mobileOrUserName, Integer loginType);
YamiUser getYamiUser(Integer appId, User user, String bizUserId);
}

View File

@@ -10,16 +10,26 @@
package com.yami.shop.security.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.model.User;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.dao.UserMapper;
import com.yami.shop.security.dao.AppConnectMapper;
import com.yami.shop.security.enums.App;
import com.yami.shop.security.model.AppConnect;
import com.yami.shop.security.service.AppConnectService;
import com.yami.shop.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.Objects;
/**
@@ -32,6 +42,12 @@ public class AppConnectServiceImpl extends ServiceImpl<AppConnectMapper, AppConn
@Autowired
private AppConnectMapper appConnectMapper;
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
/**
* YamiUserServiceImpl#insertUserIfNecessary 将会清楚该缓存信息
* @param bizUserId
@@ -44,4 +60,46 @@ public class AppConnectServiceImpl extends ServiceImpl<AppConnectMapper, AppConn
return appConnectMapper.getByBizUserId(bizUserId, app.value());
}
@Override
@Transactional(rollbackFor = Exception.class)
public User registerOrBindUser(User user, AppConnect appConnect, Integer appId) {
if (StrUtil.isBlank(user.getUserId())) {
if (userMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getUserMobile, user.getUserMobile())) > 0) {
// 该电话号码已存在
throw new YamiShopBindException("该电话号码已存在");
}
String userId = IdUtil.simpleUUID();
user.setUserId(userId);
userMapper.insert(user);
} else {
if (appConnect != null&& StrUtil.isBlank(user.getPic())) {
User userParam = new User();
userParam.setUserId(user.getUserId());
userParam.setModifyTime(new Date());
userParam.setPic(appConnect.getImageUrl());
userService.updateById(userParam);
}
}
if (appConnect == null) {
// 避免重复插入数据
if (appConnectMapper.getByBizUserId(user.getUserId(), appId) != null) {
return user;
}
appConnect = new AppConnect();
appConnect.setUserId(user.getUserId());
appConnect.setNickName(user.getNickName());
appConnect.setImageUrl(user.getPic());
// 0表示是系统的用户不是第三方的
appConnect.setAppId(appId);
appConnectMapper.insert(appConnect);
} else if (StrUtil.isBlank(appConnect.getUserId()) || Objects.isNull(appId)) {
appConnect.setAppId(appId);
appConnect.setUserId(user.getUserId());
appConnect.setUserId(user.getUserId());
appConnectMapper.updateById(appConnect);
}
return user;
}
}

View File

@@ -24,4 +24,6 @@ public interface UserService extends IService<User> {
User getUserByUserId(String userId);
Boolean insertUser(UserRegisterParam userRegisterParam);
void validate(UserRegisterParam userRegisterParam, String checkRegisterSmsFlag);
}

View File

@@ -18,6 +18,7 @@ import com.yami.shop.bean.model.User;
import com.yami.shop.bean.param.UserRegisterParam;
import com.yami.shop.bean.vo.UserVO;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.dao.UserMapper;
import com.yami.shop.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +27,7 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
/**
@@ -61,4 +63,22 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
user.setLoginPassword(uParam.getPassword());
return userMapper.insert(user) == 1;
}
/**
* 看看有没有校验验证码成功的标识
* @param userRegisterParam
* @param checkRegisterSmsFlag
*/
@Override
public void validate(UserRegisterParam userRegisterParam, String checkRegisterSmsFlag) {
if (StrUtil.isBlank(userRegisterParam.getCheckRegisterSmsFlag())) {
// 验证码已过期,请重新发送验证码校验
throw new YamiShopBindException("验证码已过期,请重新发送验证码校验");
} else {
String checkRegisterSmsFlagMobile = RedisUtil.get(checkRegisterSmsFlag);
if (!Objects.equals(checkRegisterSmsFlagMobile, userRegisterParam.getMobile())) {
// 验证码已过期,请重新发送验证码校验
throw new YamiShopBindException("验证码已过期,请重新发送验证码校验");
}
}
}
}