重写用户快速登录逻辑

This commit is contained in:
YunaiV
2020-07-03 19:12:56 +08:00
parent ee7cf3e871
commit 3d6bd5e4ee
57 changed files with 1107 additions and 228 deletions

View File

@@ -1,25 +0,0 @@
package cn.iocoder.mall.system.biz.dao.oauth2;
import cn.iocoder.mall.system.biz.dataobject.oauth2.OAuth2MobileCodeDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface OAuth2MobileCodeMapper extends BaseMapper<OAuth2MobileCodeDO> {
/**
* 获得手机号的最后一个手机验证码
*
* @param mobile 手机号
* @return 手机验证码
*/
default OAuth2MobileCodeDO selectLastByMobile(String mobile) {
QueryWrapper<OAuth2MobileCodeDO> query = new QueryWrapper<OAuth2MobileCodeDO>()
.eq("mobile", mobile)
.orderByDesc("id")
.last("limit 1");
return selectOne(query);
}
}

View File

@@ -1,13 +1,10 @@
package cn.iocoder.mall.system.biz.dao.user;
import cn.iocoder.mall.mybatis.query.QueryWrapperX;
import cn.iocoder.mall.system.biz.dataobject.authorization.RoleDO;
import cn.iocoder.mall.system.biz.dataobject.user.UserDO;
import cn.iocoder.mall.system.biz.dto.user.UserPageDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
@@ -15,12 +12,6 @@ import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<UserDO> {
default UserDO selectByAccountId(Integer accountId) {
return selectOne(new QueryWrapper<UserDO>()
.eq("account_id", accountId)
);
}
/**
* 根据条件分页查询用户列表
* @param userPageDTO

View File

@@ -1,53 +0,0 @@
package cn.iocoder.mall.system.biz.dataobject.oauth2;
import cn.iocoder.mall.mybatis.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* OAuth2 手机验证码
*/
@TableName("oauth2_mobile_code")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class OAuth2MobileCodeDO extends BaseDO {
/**
* 编号
*/
private Integer id;
/**
* 手机号
*/
private String mobile;
/**
* 验证码
*/
private String code;
/**
* 创建 IP
*/
private String createIp;
/**
* 今日发送的第几条
*/
private Integer todayIndex;
/**
* 是否使用
*/
private Boolean used;
/**
* 使用时间
*/
private Date usedTime;
/**
* 使用 IP
*/
private Date usedIp;
}

View File

@@ -4,7 +4,7 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.util.MallUtil;
import cn.iocoder.common.framework.util.MallUtils;
import cn.iocoder.mall.system.biz.log.operation.annotation.OperationLogging;
import cn.iocoder.mall.system.biz.log.operation.enums.LogStatus;
import cn.iocoder.mall.system.biz.log.operation.event.OperationLogEvent;
@@ -98,7 +98,7 @@ public class OperationLogAspect {
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
return new OperationLogDTO()
.setTraceId(MallUtil.getTraceId())
.setTraceId(MallUtils.getTraceId())
.setUri(URLUtil.getPath(request.getRequestURI()))
.setUserAgent(HttpUtil.getUserAgent(request))
.setIp(HttpUtil.getIp(request))

View File

@@ -1,7 +1,7 @@
package cn.iocoder.mall.system.biz.service.authorization;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.StringUtil;
import cn.iocoder.common.framework.util.StringUtils;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.mybatis.enums.DeletedStatusEnum;
import cn.iocoder.mall.system.biz.bo.authorization.RoleBO;
@@ -127,7 +127,7 @@ public class RoleServiceImpl implements RoleService {
throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.ROLE_NAME_DUPLICATE, name);
}
// 2. 是否存在相同编码的角色
if (!StringUtil.hasText(code)) {
if (!StringUtils.hasText(code)) {
return;
}
// 该 code 编码被其它角色所使用

View File

@@ -1,16 +0,0 @@
package cn.iocoder.mall.system.biz.service.oauth2;
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
/**
* OAuth2 手机验证码 Service 接口
*
* 我们将手机验证码登陆的方式,作为一种拓展的 OAuth2 的认证方式。因此,我们放在了 `oauth2` 包下
*/
public interface OAuth2MobileCodeService {
void send(OAuth2MobileCodeSendDTO sendDTO);
void use(String mobile, String code);
}

View File

@@ -1,89 +0,0 @@
package cn.iocoder.mall.system.biz.service.oauth2;
import cn.iocoder.common.framework.constant.SysErrorCodeEnum;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.ValidationUtil;
import cn.iocoder.mall.system.biz.dao.oauth2.OAuth2MobileCodeMapper;
import cn.iocoder.mall.system.biz.dataobject.oauth2.OAuth2MobileCodeDO;
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Date;
import static cn.iocoder.mall.system.biz.enums.SystemErrorCodeEnum.*;
@Service
public class OAuth2MobileCodeServiceImpl implements OAuth2MobileCodeService {
/**
* 每条验证码的过期时间,单位:毫秒
*/
@Value("${modules.oauth2-mobile-code-service.code-expire-time-millis}")
private int codeExpireTimes;
/**
* 每日发送最大数量
*/
@Value("${modules.oauth2-mobile-code-service.send-maximum-quantity-per-day}")
private int sendMaximumQuantityPerDay;
/**
* 短信发送频率,单位:毫秒
*/
@Value("${modules.oauth2-mobile-code-service.send-frequency}")
private int sendFrequency;
@Autowired
private OAuth2MobileCodeMapper oauth2MobileCodeMapper;
@Override
public void send(OAuth2MobileCodeSendDTO sendDTO) {
if (!ValidationUtil.isMobile(sendDTO.getMobile())) {
throw ServiceExceptionUtil.exception(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), "手机格式不正确"); // TODO 有点搓
}
// 校验是否可以发送验证码
OAuth2MobileCodeDO lastMobileCodePO = oauth2MobileCodeMapper.selectLastByMobile(sendDTO.getMobile());
if (lastMobileCodePO != null) {
if (lastMobileCodePO.getTodayIndex() >= sendMaximumQuantityPerDay) { // 超过当天发送的上限。
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
}
if (System.currentTimeMillis() - lastMobileCodePO.getCreateTime().getTime() < sendFrequency) { // 发送过于频繁
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_SEND_TOO_FAST);
}
// TODO 提升,每个 IP 每天可发送数量
// TODO 提升,每个 IP 每小时可发送数量
}
// 创建验证码记录
OAuth2MobileCodeDO newMobileCodePO = new OAuth2MobileCodeDO().setMobile(sendDTO.getMobile())
.setCode("9999") // TODO 芋艿,随机 4 位验证码 or 6 位验证码
.setTodayIndex(lastMobileCodePO != null ? lastMobileCodePO.getTodayIndex() : 1)
.setCreateIp(sendDTO.getIp())
.setUsed(false);
newMobileCodePO.setCreateTime(new Date());
oauth2MobileCodeMapper.insert(newMobileCodePO);
// TODO 发送验证码短信
}
@Override
public void use(String mobile, String code) {
// 校验验证码
OAuth2MobileCodeDO mobileCodeDO = oauth2MobileCodeMapper.selectLastByMobile(mobile);
if (mobileCodeDO == null) { // 若验证码不存在,抛出异常
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_NOT_FOUND);
}
if (System.currentTimeMillis() - mobileCodeDO.getCreateTime().getTime() >= codeExpireTimes) { // 验证码已过期
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_EXPIRED);
}
if (mobileCodeDO.getUsed()) { // 验证码已使用
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_USED);
}
if (!mobileCodeDO.getCode().equals(code)) {
throw ServiceExceptionUtil.exception(OAUTH2_MOBILE_CODE_NOT_CORRECT);
}
// 使用验证码
OAuth2MobileCodeDO update = new OAuth2MobileCodeDO().setId(mobileCodeDO.getId())
.setUsed(true).setUsedTime(new Date()); // TODO usedIp
oauth2MobileCodeMapper.updateById(update);
}
}

View File

@@ -1,29 +1,14 @@
package cn.iocoder.mall.system.rest.controller.oauth2;
import cn.iocoder.common.framework.constant.MallConstants;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.core.annotation.RequiresNone;
import cn.iocoder.mall.system.biz.bo.user.UserAuthenticateBO;
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeAuthenticateDTO;
import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeSendDTO;
import cn.iocoder.mall.system.biz.service.oauth2.OAuth2MobileCodeService;
import cn.iocoder.mall.system.biz.service.oauth2.OAuth2Service;
import cn.iocoder.mall.system.biz.service.user.UserService;
import cn.iocoder.mall.system.rest.convert.oauth2.UsersOAuth2Convert;
import cn.iocoder.mall.system.rest.request.oauth2.UsersOAuth2MobileCodeAuthenticateRequest;
import cn.iocoder.mall.system.rest.response.user.UsersOAuth2AuthenticateResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(MallConstants.ROOT_PATH_USER + "/oauth2")
@Api(tags = "用户 - OAuth2 API")
@@ -36,33 +21,6 @@ public class UsersOAuth2Controller {
@Autowired
private OAuth2MobileCodeService oauth2MobileCodeService;
@PostMapping("/mobile-code-authenticate")
@ApiOperation("手机验证码认证")
@RequiresNone
public CommonResult<UsersOAuth2AuthenticateResponse> mobileCodeAuthenticate(UsersOAuth2MobileCodeAuthenticateRequest request,
HttpServletRequest httpRequest) {
// 执行认证
OAuth2MobileCodeAuthenticateDTO authenticateDTO = UsersOAuth2Convert.INSTANCE.convert(request)
.setIp(HttpUtil.getIp(httpRequest));
UserAuthenticateBO userAuthenticateBO = userService.authenticate(authenticateDTO);
// 转换返回
return CommonResult.success(
UsersOAuth2Convert.INSTANCE.convert(userAuthenticateBO)
);
}
@PostMapping("/send-mobile-code")
@ApiOperation("发送手机验证码")
@ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691234")
@RequiresNone
public CommonResult<Boolean> sendMobileCode(@RequestParam("mobile") String mobile,
HttpServletRequest request) {
// 执行发送验证码
OAuth2MobileCodeSendDTO sendDTO = new OAuth2MobileCodeSendDTO()
.setMobile(mobile).setIp(HttpUtil.getIp(request));
oauth2MobileCodeService.send(sendDTO);
// 返回成功
return CommonResult.success(true);
}
}