新增 user 模块
调整新的项目结构,将 service 和 dao 模块,从 application 拆除,独立成 service-impl 模块 增加 sdk 模块,用于提供一个封装过的功能。例如说,认证和授权~
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
package cn.iocoder.mall.user.config;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@MapperScan("cn.iocoder.mall.user.dao") // 扫描对应的 Mapper 接口
|
||||
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理。为什么使用 proxyTargetClass 参数,参见 https://blog.csdn.net/huang_550/article/details/76492600
|
||||
public class DatabaseConfiguration {
|
||||
|
||||
// 数据源,使用 HikariCP
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.mall.user.convert;
|
||||
|
||||
import cn.iocoder.mall.user.dataobject.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.mall.user.service.api.dto.OAuth2AccessTokenBO;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
public interface OAuth2Convert {
|
||||
|
||||
OAuth2Convert INSTANCE = Mappers.getMapper(OAuth2Convert.class);
|
||||
|
||||
@Mappings({})
|
||||
OAuth2AccessTokenBO convert(OAuth2AccessTokenDO oauth2AccessTokenDO);
|
||||
|
||||
default OAuth2AccessTokenBO convertWithExpiresIn(OAuth2AccessTokenDO oauth2AccessTokenDO) {
|
||||
OAuth2AccessTokenBO bo = this.convert(oauth2AccessTokenDO);
|
||||
bo.setExpiresIn(Math.max((int) ((oauth2AccessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis()) / 1000), 0));
|
||||
return bo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.mall.user.dao;
|
||||
|
||||
import cn.iocoder.mall.user.dataobject.MobileCodeDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository // 实际不加也没问entity,就是不想 IDEA 那看到有个报错
|
||||
public interface MobileCodeMapper {
|
||||
|
||||
void insert(MobileCodeDO entity);
|
||||
|
||||
/**
|
||||
* 更新手机验证码
|
||||
*
|
||||
* @param entity 更新信息
|
||||
*/
|
||||
void update(MobileCodeDO entity);
|
||||
|
||||
/**
|
||||
* 获得手机号的最后一个手机验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @return 手机验证码
|
||||
*/
|
||||
MobileCodeDO selectLast1ByMobile(String mobile);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.iocoder.mall.user.dao;
|
||||
|
||||
import cn.iocoder.mall.user.dataobject.OAuth2AccessTokenDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OAuth2AccessTokenMapper {
|
||||
|
||||
void insert(OAuth2AccessTokenDO entity);
|
||||
|
||||
OAuth2AccessTokenDO selectByTokenId(String tokenId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package cn.iocoder.mall.user.dao;
|
||||
|
||||
import cn.iocoder.mall.user.dataobject.OAuth2RefreshTokenDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OAuth2RefreshTokenMapper {
|
||||
|
||||
void insert(OAuth2RefreshTokenDO entity);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.iocoder.mall.user.dao;
|
||||
|
||||
import cn.iocoder.mall.user.dataobject.UserDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserMapper {
|
||||
|
||||
void insert(UserDO entity);
|
||||
|
||||
UserDO selectByMobile(String mobile);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package cn.iocoder.mall.user.dao;
|
||||
|
||||
import cn.iocoder.mall.user.dataobject.UserRegisterDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRegisterMapper {
|
||||
|
||||
void insert(UserRegisterDO entity);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package cn.iocoder.mall.user.dataobject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
// TODO 优化,IP
|
||||
public class MobileCodeDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String mobile;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 今日发送的第几条
|
||||
*/
|
||||
private Integer todayIndex;
|
||||
/**
|
||||
* 是否使用
|
||||
*/
|
||||
private Boolean used;
|
||||
/**
|
||||
* 注册的用户编号
|
||||
*/
|
||||
private Long usedUid;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
private Date usedTime;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public MobileCodeDO setId(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public MobileCodeDO setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public MobileCodeDO setCode(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getTodayIndex() {
|
||||
return todayIndex;
|
||||
}
|
||||
|
||||
public MobileCodeDO setTodayIndex(Integer todayIndex) {
|
||||
this.todayIndex = todayIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
public MobileCodeDO setUsed(Boolean used) {
|
||||
this.used = used;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getUsedUid() {
|
||||
return usedUid;
|
||||
}
|
||||
|
||||
public MobileCodeDO setUsedUid(Long usedUid) {
|
||||
this.usedUid = usedUid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public MobileCodeDO setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getUsedTime() {
|
||||
return usedTime;
|
||||
}
|
||||
|
||||
public MobileCodeDO setUsedTime(Date usedTime) {
|
||||
this.usedTime = usedTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package cn.iocoder.mall.user.dataobject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class OAuth2AccessTokenDO {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String tokenId;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String refreshToken;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long uid;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
/**
|
||||
* 是否有效
|
||||
*/
|
||||
private Boolean valid;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
public String getTokenId() {
|
||||
return tokenId;
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenDO setTokenId(String tokenId) {
|
||||
this.tokenId = tokenId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenDO setRefreshToken(String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenDO setUid(Long uid) {
|
||||
this.uid = uid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getExpiresTime() {
|
||||
return expiresTime;
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenDO setExpiresTime(Date expiresTime) {
|
||||
this.expiresTime = expiresTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean getValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenDO setValid(Boolean valid) {
|
||||
this.valid = valid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenDO setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package cn.iocoder.mall.user.dataobject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*
|
||||
* idx_uid
|
||||
*/
|
||||
public class OAuth2RefreshTokenDO {
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String tokenId;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long uid;
|
||||
/**
|
||||
* 是否有效
|
||||
*/
|
||||
private Boolean valid;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
public String getTokenId() {
|
||||
return tokenId;
|
||||
}
|
||||
|
||||
public OAuth2RefreshTokenDO setTokenId(String tokenId) {
|
||||
this.tokenId = tokenId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public OAuth2RefreshTokenDO setUid(Long uid) {
|
||||
this.uid = uid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Boolean getValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public OAuth2RefreshTokenDO setValid(Boolean valid) {
|
||||
this.valid = valid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getExpiresTime() {
|
||||
return expiresTime;
|
||||
}
|
||||
|
||||
public OAuth2RefreshTokenDO setExpiresTime(Date expiresTime) {
|
||||
this.expiresTime = expiresTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public OAuth2RefreshTokenDO setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package cn.iocoder.mall.user.dataobject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户实体,存储用户基本数据。
|
||||
*
|
||||
* idx_mobile 唯一索引
|
||||
*/
|
||||
public class UserDO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String mobile;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public UserDO setId(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public UserDO setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public UserDO setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package cn.iocoder.mall.user.dataobject;
|
||||
|
||||
public class UserLoginLogDO {
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.mall.user.dataobject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户注册信息
|
||||
*/
|
||||
public class UserRegisterDO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
// TODO 芋艿 ip
|
||||
// TODO 芋艿 ua
|
||||
// TODO 芋艿 方式,手机注册、qq 等等
|
||||
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public UserRegisterDO setId(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public UserRegisterDO setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package cn.iocoder.mall.user.dataobject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户三方开放平台授权,例如:QQ / 微博 / 微信等等。
|
||||
*/
|
||||
public class UserThirdAuthDO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*
|
||||
* 外键 {@link UserDO#uid}
|
||||
*/
|
||||
private Long uid;
|
||||
|
||||
// ========== 授权相关字段
|
||||
|
||||
/**
|
||||
* 用户的唯一标识
|
||||
*/
|
||||
private String openid;
|
||||
/**
|
||||
* 开放平台
|
||||
*
|
||||
* @see cn.iocoder.mall.user.constant.ThirdPlatformConstant
|
||||
*/
|
||||
private Integer platform;
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private Date accessToken;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expireTime;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private Date refreshToken;
|
||||
/**
|
||||
* 授权范围。一般情况下,使用逗号分隔
|
||||
*/
|
||||
private String scopes;
|
||||
|
||||
// ========== 基础信息
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickname;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* TODO 芋艿,找地方统一枚举。0-未知,1-男,2-女
|
||||
*/
|
||||
private Integer gender;
|
||||
// TODO https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
|
||||
// TODO 芋艿,其他字段,国家/省份/城市/地区等
|
||||
// TODO 芋艿,头像
|
||||
// TODO 芋艿,微信独有 unionid
|
||||
/**
|
||||
* 统一存储基础信息,使用 JSON 格式化,避免未有效解析的情况。
|
||||
*/
|
||||
private String extras;
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package cn.iocoder.mall.user;
|
||||
@@ -0,0 +1,104 @@
|
||||
package cn.iocoder.mall.user.service;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.mall.user.dao.MobileCodeMapper;
|
||||
import cn.iocoder.mall.user.dataobject.MobileCodeDO;
|
||||
import cn.iocoder.mall.user.service.api.MobileCodeService;
|
||||
import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* MobileCodeService ,实现用户登陆时需要的验证码
|
||||
*/
|
||||
@Service
|
||||
@com.alibaba.dubbo.config.annotation.Service
|
||||
public class MobileCodeServiceImpl implements MobileCodeService {
|
||||
|
||||
/**
|
||||
* 每条验证码的过期时间,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.mobile-code-service.code-expire-time-millis}")
|
||||
private int codeExpireTimes;
|
||||
/**
|
||||
* 每日发送最大数量
|
||||
*/
|
||||
@Value("${modules.mobile-code-service.send-maximum-quantity-per-day}")
|
||||
private int sendMaximumQuantityPerDay;
|
||||
/**
|
||||
* 短信发送频率,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.mobile-code-service.send-frequency}")
|
||||
private int sendFrequency;
|
||||
|
||||
@Autowired
|
||||
private MobileCodeMapper mobileCodeMapper;
|
||||
@Autowired
|
||||
private UserServiceImpl userService;
|
||||
|
||||
/**
|
||||
* 校验手机号的最后一个手机验证码是否有效
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param code 验证码
|
||||
* @return 手机验证码信息
|
||||
*/
|
||||
public MobileCodeDO validLastMobileCode(String mobile, String code) {
|
||||
MobileCodeDO mobileCodePO = mobileCodeMapper.selectLast1ByMobile(mobile);
|
||||
if (mobileCodePO == null) { // 若验证码不存在,抛出异常
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_NOT_FOUND.getCode());
|
||||
}
|
||||
if (System.currentTimeMillis() - mobileCodePO.getCreateTime().getTime() >= codeExpireTimes) { // 验证码已过期
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_EXPIRED.getCode());
|
||||
}
|
||||
if (mobileCodePO.getUsed()) { // 验证码已使用
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_USED.getCode());
|
||||
}
|
||||
if (!mobileCodePO.getCode().equals(code)) {
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_NOT_CORRECT.getCode());
|
||||
}
|
||||
return mobileCodePO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新手机验证码已使用
|
||||
*
|
||||
* @param id 验证码编号
|
||||
* @param uid 用户编号
|
||||
*/
|
||||
public void useMobileCode(Long id, Long uid) {
|
||||
MobileCodeDO update = new MobileCodeDO().setId(id).setUsed(true).setUsedUid(uid).setUsedTime(new Date());
|
||||
mobileCodeMapper.update(update);
|
||||
}
|
||||
|
||||
public void send(String mobile) {
|
||||
// TODO 芋艿,校验手机格式
|
||||
// 校验手机号码是否已经注册
|
||||
if (userService.getUser(mobile) != null) {
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.USER_MOBILE_ALREADY_REGISTERED.getCode());
|
||||
}
|
||||
// 校验是否可以发送验证码
|
||||
MobileCodeDO lastMobileCodePO = mobileCodeMapper.selectLast1ByMobile(mobile);
|
||||
if (lastMobileCodePO != null) {
|
||||
if (lastMobileCodePO.getTodayIndex() >= sendMaximumQuantityPerDay) { // 超过当天发送的上限。
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY.getCode());
|
||||
}
|
||||
if (System.currentTimeMillis() - lastMobileCodePO.getCreateTime().getTime() < sendFrequency) { // 发送过于频繁
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.MOBILE_CODE_SEND_TOO_FAST.getCode());
|
||||
}
|
||||
// TODO 提升,每个 IP 每天可发送数量
|
||||
// TODO 提升,每个 IP 每小时可发送数量
|
||||
}
|
||||
// 创建验证码记录
|
||||
MobileCodeDO newMobileCodePO = new MobileCodeDO().setMobile(mobile)
|
||||
.setCode("9999") // TODO 芋艿,随机 4 位验证码 or 6 位验证码
|
||||
.setTodayIndex(lastMobileCodePO != null ? lastMobileCodePO.getTodayIndex() : 1)
|
||||
.setUsed(false).setCreateTime(new Date());
|
||||
mobileCodeMapper.insert(newMobileCodePO);
|
||||
// TODO 发送验证码短信
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package cn.iocoder.mall.user.service;
|
||||
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.mall.user.convert.OAuth2Convert;
|
||||
import cn.iocoder.mall.user.dao.OAuth2AccessTokenMapper;
|
||||
import cn.iocoder.mall.user.dao.OAuth2RefreshTokenMapper;
|
||||
import cn.iocoder.mall.user.dataobject.MobileCodeDO;
|
||||
import cn.iocoder.mall.user.dataobject.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.mall.user.dataobject.OAuth2RefreshTokenDO;
|
||||
import cn.iocoder.mall.user.dataobject.UserDO;
|
||||
import cn.iocoder.mall.user.service.api.OAuth2Service;
|
||||
import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum;
|
||||
import cn.iocoder.mall.user.service.api.dto.OAuth2AccessTokenBO;
|
||||
import cn.iocoder.mall.user.service.api.dto.OAuth2AuthenticationDTO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* OAuth2Service ,实现用户授权相关的逻辑
|
||||
*/
|
||||
@Service
|
||||
@com.alibaba.dubbo.config.annotation.Service
|
||||
public class OAuth2ServiceImpl implements OAuth2Service {
|
||||
|
||||
/**
|
||||
* 访问令牌过期时间,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.oauth2-code-service.access-token-expire-time-millis}")
|
||||
private int accessTokenExpireTimeMillis;
|
||||
/**
|
||||
* 刷新令牌过期时间,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.oauth2-code-service.refresh-token-expire-time-millis}")
|
||||
private int refreshTokenExpireTimeMillis;
|
||||
|
||||
@Autowired
|
||||
private UserServiceImpl userService;
|
||||
@Autowired
|
||||
private MobileCodeServiceImpl mobileCodeService;
|
||||
@Autowired
|
||||
private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
|
||||
@Autowired
|
||||
private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public OAuth2AccessTokenBO getAccessToken(String mobile, String code) {
|
||||
// 校验手机号的最后一个手机验证码是否有效
|
||||
MobileCodeDO mobileCodeDO = mobileCodeService.validLastMobileCode(mobile, code);
|
||||
// 获取用户
|
||||
UserDO userDO = userService.getUser(mobile);
|
||||
if (userDO == null) { // 用户不存在
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.USER_MOBILE_NOT_REGISTERED.getCode());
|
||||
}
|
||||
// 创建刷新令牌
|
||||
OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(userDO.getId());
|
||||
// 创建访问令牌
|
||||
OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(userDO.getId(), oauth2RefreshTokenDO.getTokenId());
|
||||
// 标记已使用
|
||||
mobileCodeService.useMobileCode(mobileCodeDO.getId(), userDO.getId());
|
||||
// 转换返回
|
||||
return OAuth2Convert.INSTANCE.convertWithExpiresIn(oauth2AccessTokenDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AuthenticationDTO checkToken(String accessToken) throws ServiceException {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByTokenId(accessToken);
|
||||
if (accessTokenDO == null) { // 不存在
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.OAUTH_INVALID_TOKEN_NOT_FOUND.getCode());
|
||||
}
|
||||
if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.OAUTH_INVALID_TOKEN_EXPIRED.getCode());
|
||||
}
|
||||
if (!accessTokenDO.getValid()) { // 无效
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.OAUTH_INVALID_TOKEN_INVALID.getCode());
|
||||
}
|
||||
// 转换返回
|
||||
return new OAuth2AuthenticationDTO().setUid(accessTokenDO.getUid());
|
||||
}
|
||||
|
||||
private OAuth2AccessTokenDO createOAuth2AccessToken(Long uid, String refreshToken) {
|
||||
OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setTokenId(generateAccessToken())
|
||||
.setRefreshToken(refreshToken)
|
||||
.setUid(uid)
|
||||
.setExpiresTime(new Date(System.currentTimeMillis() + accessTokenExpireTimeMillis))
|
||||
.setValid(true);
|
||||
oauth2AccessTokenMapper.insert(accessToken);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long uid) {
|
||||
OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setTokenId(generateRefreshToken())
|
||||
.setUid(uid)
|
||||
.setExpiresTime(new Date(System.currentTimeMillis() + refreshTokenExpireTimeMillis))
|
||||
.setValid(true);
|
||||
oauth2RefreshTokenMapper.insert(refreshToken);
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
private String generateAccessToken() {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
|
||||
private String generateRefreshToken() {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package cn.iocoder.mall.user.service;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.mall.user.dao.UserMapper;
|
||||
import cn.iocoder.mall.user.dao.UserRegisterMapper;
|
||||
import cn.iocoder.mall.user.dataobject.UserDO;
|
||||
import cn.iocoder.mall.user.dataobject.UserRegisterDO;
|
||||
import cn.iocoder.mall.user.service.api.UserService;
|
||||
import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum;
|
||||
import cn.iocoder.mall.user.service.api.dto.UserDTO;
|
||||
import com.alibaba.dubbo.config.annotation.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* UserService ,实现和用户信息相关的逻辑
|
||||
*/
|
||||
@org.springframework.stereotype.Service
|
||||
@com.alibaba.dubbo.config.annotation.Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
@Autowired
|
||||
private UserRegisterMapper userRegisterMapper;
|
||||
@Autowired
|
||||
private MobileCodeServiceImpl mobileCodeService;
|
||||
|
||||
public UserDO getUser(String mobile) {
|
||||
return userMapper.selectByMobile(mobile);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public UserDTO createUser(String mobile, String code) {
|
||||
// TODO 芋艿,校验手机格式
|
||||
// 校验手机号的最后一个手机验证码是否有效
|
||||
mobileCodeService.validLastMobileCode(mobile, code);
|
||||
// 校验用户是否已经存在
|
||||
if (getUser(mobile) != null) {
|
||||
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.USER_MOBILE_ALREADY_REGISTERED.getCode());
|
||||
}
|
||||
// 创建用户
|
||||
UserDO userDO = new UserDO().setMobile(mobile).setCreateTime(new Date());
|
||||
userMapper.insert(userDO);
|
||||
// 插入注册信息
|
||||
createUserRegister(userDO);
|
||||
// 转换返回
|
||||
return new UserDTO().setUid(userDO.getId());
|
||||
}
|
||||
|
||||
private void createUserRegister(UserDO userDO) {
|
||||
UserRegisterDO userRegisterDO = new UserRegisterDO().setId(userDO.getId())
|
||||
.setCreateTime(new Date());
|
||||
userRegisterMapper.insert(userRegisterDO);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user