新增 CommonResult 。

将使用手机号进行注册登陆的逻辑,进行变更~
This commit is contained in:
YunaiV 2019-02-26 00:24:12 +08:00
parent 4ae211dbc2
commit 4162eda377
27 changed files with 351 additions and 85 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@
*.iws *.iws
*.iml *.iml
*.ipr *.ipr
target/*
### NetBeans ### ### NetBeans ###
/nbproject/private/ /nbproject/private/

View File

@ -2,6 +2,7 @@ package cn.iocoder.common.framework.config;
import cn.iocoder.common.framework.constant.SysErrorCodeEnum; import cn.iocoder.common.framework.constant.SysErrorCodeEnum;
import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.util.ExceptionUtil;
import cn.iocoder.common.framework.vo.RestResult; import cn.iocoder.common.framework.vo.RestResult;
import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ControllerAdvice;
@ -9,7 +10,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
@ControllerAdvice @ControllerAdvice
@ -21,19 +21,13 @@ public class GlobalExceptionHandler {
return RestResult.error(ex.getCode(), ex.getMessage()); return RestResult.error(ex.getCode(), ex.getMessage());
} }
// 处理 Spring 动态代理调用时发生 UndeclaredThrowableException 的情况
// 不了解的胖友可以看看 https://segmentfault.com/a/1190000012262244 文章
@ResponseBody @ResponseBody
@ExceptionHandler(value = UndeclaredThrowableException.class) @ExceptionHandler(value = UndeclaredThrowableException.class)
public RestResult undeclaredThrowableExceptionHandler(HttpServletRequest req, UndeclaredThrowableException e) { public RestResult undeclaredThrowableExceptionHandler(HttpServletRequest req, UndeclaredThrowableException e) {
// 尝试获得 ServiceException 异常如果是则使用 serviceExceptionHandler 方法处理 // 尝试获得 ServiceException 异常如果是则使用 serviceExceptionHandler 方法处理
Throwable undeclaredThrowable = e.getUndeclaredThrowable(); ServiceException serviceException = ExceptionUtil.getServiceException(e);
if (undeclaredThrowable instanceof InvocationTargetException) { if (serviceException != null) {
InvocationTargetException invocationTargetException = (InvocationTargetException) undeclaredThrowable; return serviceExceptionHandler(req, serviceException);
Throwable targetException = invocationTargetException.getTargetException();
if (targetException != null & targetException instanceof ServiceException) {
return serviceExceptionHandler(req, (ServiceException) targetException);
}
} }
// 获得不到使用 异常日志 方法处理 // 获得不到使用 异常日志 方法处理
return resultExceptionHandler(req, e); return resultExceptionHandler(req, e);

View File

@ -1,5 +1,6 @@
package cn.iocoder.common.framework.config; package cn.iocoder.common.framework.config;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.RestResult; import cn.iocoder.common.framework.vo.RestResult;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -21,6 +22,9 @@ public class GlobalResponseBodyAdvice implements ResponseBodyAdvice {
if (body instanceof RestResult) { if (body instanceof RestResult) {
return body; return body;
} }
if (body instanceof CommonResult) { // TODO 芋艿后续要改下
return body;
}
return RestResult.ok(body); return RestResult.ok(body);
} }

View File

@ -0,0 +1,34 @@
package cn.iocoder.common.framework.util;
import cn.iocoder.common.framework.exception.ServiceException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
public class ExceptionUtil {
public static ServiceException getServiceException(Exception e) {
if (e instanceof UndeclaredThrowableException) {
return getServiceException((UndeclaredThrowableException) e);
}
return null;
}
// 处理 Spring 动态代理调用时发生 UndeclaredThrowableException 的情况
// 不了解的胖友可以先看看 https://segmentfault.com/a/1190000012262244 文章
// 原因是
// 1. Dubbo 动态代理 Wrapper 会将抛出的异常包装成 InvocationTargetException 异常
// 2. Spring AOP 发现是 InvocationTargetException 异常是非方法定义的异常则会包装成 UndeclaredThrowableException 异常
public static ServiceException getServiceException(UndeclaredThrowableException e) {
Throwable undeclaredThrowable = e.getUndeclaredThrowable();
if (undeclaredThrowable instanceof InvocationTargetException) {
InvocationTargetException invocationTargetException = (InvocationTargetException) undeclaredThrowable;
Throwable targetException = invocationTargetException.getTargetException();
if (targetException != null & targetException instanceof ServiceException) {
return (ServiceException) targetException;
}
}
return null;
}
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.common.framework.util; package cn.iocoder.common.framework.util;
import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.vo.CommonResult;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -38,6 +39,16 @@ public class ServiceExceptionUtil {
ServiceExceptionUtil.messages.put(code, message); ServiceExceptionUtil.messages.put(code, message);
} }
// TODO 芋艿可能不是目前最优解目前暂时这样
public static <T> CommonResult<T> error(Integer code) {
return CommonResult.error(code, messages.get(code));
}
public static CommonResult error(Integer code, Object... params) {
String message = doFormat(code, messages.get(code), params);
return CommonResult.error(code, message);
}
/** /**
* 创建指定编号的 ServiceException 的异常 * 创建指定编号的 ServiceException 的异常
* *

View File

@ -0,0 +1,83 @@
package cn.iocoder.common.framework.vo;
import org.springframework.util.Assert;
public class CommonResult<T> {
public static Integer CODE_SUCCESS = 0;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 返回数据
*/
private T data;
/**
* 将传入的 result 对象转换成另外一个泛型结果的对象
*
* 因为 A 方法返回的 CommonResult 对象不满足调用其的 B 方法的返回所以需要进行转换
*
* @param result 传入的 result 对象
* @param <T> 返回的泛型
* @return 新的 CommonResult 对象
*/
public static <T> CommonResult<T> error(CommonResult<?> result) {
return error(result.getCode(), result.getMessage());
}
public static <T> CommonResult<T> error(Integer code, String message) {
Assert.isTrue(!CODE_SUCCESS.equals(code), "code 必须是错误的!");
CommonResult<T> result = new CommonResult<>();
result.code = code;
result.message = message;
return result;
}
public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<>();
result.code = CODE_SUCCESS;
result.data = data;
result.message = "";
return result;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public boolean isSuccess() {
return CODE_SUCCESS.equals(code);
}
public boolean isError() {
return !isSuccess();
}
}

View File

@ -53,4 +53,5 @@ public class RestResult {
public void setData(Object data) { public void setData(Object data) {
this.data = data; this.data = data;
} }
} }

View File

@ -92,6 +92,12 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,12 +1,14 @@
package cn.iocoder.mall.user.controller; package cn.iocoder.mall.user.controller;
import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.util.ExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.user.sdk.annotation.PermitAll; import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import cn.iocoder.mall.user.service.api.MobileCodeService; import cn.iocoder.mall.user.service.api.MobileCodeService;
import cn.iocoder.mall.user.service.api.OAuth2Service; import cn.iocoder.mall.user.service.api.OAuth2Service;
import cn.iocoder.mall.user.service.api.UserService; import cn.iocoder.mall.user.service.api.UserService;
import cn.iocoder.mall.user.service.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum; import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum;
import cn.iocoder.mall.user.service.api.dto.OAuth2AccessTokenBO;
import com.alibaba.dubbo.config.annotation.Reference; import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -47,7 +49,11 @@ public class PassportController {
try { try {
accessTokenDTO = oauth2Service.getAccessToken(mobile, code); accessTokenDTO = oauth2Service.getAccessToken(mobile, code);
return accessTokenDTO; return accessTokenDTO;
} catch (ServiceException serviceException) { } catch (Exception ex) {
ServiceException serviceException = ExceptionUtil.getServiceException(ex);
if (serviceException == null) {
throw ex;
}
if (!serviceException.getCode().equals(UserErrorCodeEnum.USER_MOBILE_NOT_REGISTERED.getCode())) { // 如果是未注册异常忽略下面发起自动注册逻辑 if (!serviceException.getCode().equals(UserErrorCodeEnum.USER_MOBILE_NOT_REGISTERED.getCode())) { // 如果是未注册异常忽略下面发起自动注册逻辑
throw serviceException; throw serviceException;
} }
@ -55,7 +61,11 @@ public class PassportController {
// 上面尝试授权失败说明用户未注册发起自动注册 // 上面尝试授权失败说明用户未注册发起自动注册
try { try {
userService.createUser(mobile, code); userService.createUser(mobile, code);
} catch (ServiceException serviceException) { } catch (Exception ex) {
ServiceException serviceException = ExceptionUtil.getServiceException(ex);
if (serviceException == null) {
throw ex;
}
if (!serviceException.getCode().equals(UserErrorCodeEnum.USER_MOBILE_ALREADY_REGISTERED.getCode())) { // 如果是已注册异常忽略下面再次发起授权 if (!serviceException.getCode().equals(UserErrorCodeEnum.USER_MOBILE_ALREADY_REGISTERED.getCode())) { // 如果是已注册异常忽略下面再次发起授权
throw serviceException; throw serviceException;
} }
@ -65,11 +75,26 @@ public class PassportController {
return accessTokenDTO; return accessTokenDTO;
} }
/**
* 手机号 + 验证码登陆
*
* @param mobile 手机号
* @param code 验证码
* @return 授权信息
*/
@PermitAll
@PostMapping("/mobile/login2")
public CommonResult<OAuth2AccessTokenBO> mobileRegister2(@RequestParam("mobile") String mobile,
@RequestParam("code") String code) {
return oauth2Service.getAccessToken2(mobile, code);
}
/** /**
* 发送手机验证码 * 发送手机验证码
* *
* @param mobile 手机号 * @param mobile 手机号
*/ */
@PermitAll
@PostMapping("mobile/send") @PostMapping("mobile/send")
public void mobileSend(@RequestParam("mobile") String mobile) { public void mobileSend(@RequestParam("mobile") String mobile) {
mobileCodeService.send(mobile); mobileCodeService.send(mobile);

View File

@ -1,16 +1,18 @@
package cn.iocoder.mall.user.controller; package cn.iocoder.mall.user.controller;
import cn.iocoder.mall.user.sdk.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//@RestController @RestController
//@RequestMapping("/user") @RequestMapping("/user")
public class UserController { public class UserController {
@GetMapping("/info") @GetMapping("/info")
public Long info() { public Long info() {
// TODO 芋艿正在实现中 // TODO 芋艿正在实现中
// return SecurityContextHolder.getContext().getUid(); return SecurityContextHolder.getContext().getUid();
return null;
} }
} }

View File

@ -5,7 +5,7 @@ import cn.iocoder.mall.user.sdk.annotation.PermitAll;
import cn.iocoder.mall.user.sdk.context.SecurityContext; import cn.iocoder.mall.user.sdk.context.SecurityContext;
import cn.iocoder.mall.user.sdk.context.SecurityContextHolder; import cn.iocoder.mall.user.sdk.context.SecurityContextHolder;
import cn.iocoder.mall.user.service.api.OAuth2Service; import cn.iocoder.mall.user.service.api.OAuth2Service;
import cn.iocoder.mall.user.service.api.dto.OAuth2AuthenticationDTO; import cn.iocoder.mall.user.service.api.bo.OAuth2AuthenticationBO;
import com.alibaba.dubbo.config.annotation.Reference; import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -29,7 +29,7 @@ public class SecurityInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 校验访问令牌是否正确若正确返回授权信息 // 校验访问令牌是否正确若正确返回授权信息
String accessToken = obtainAccess(request); String accessToken = obtainAccess(request);
OAuth2AuthenticationDTO authentication = null; OAuth2AuthenticationBO authentication = null;
if (accessToken != null) { if (accessToken != null) {
authentication = oauth2Service.checkToken(accessToken); authentication = oauth2Service.checkToken(accessToken);
// 添加到 SecurityContext // 添加到 SecurityContext

View File

@ -2,8 +2,9 @@ package cn.iocoder.mall.user.service.api;
import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.mall.user.service.api.dto.OAuth2AccessTokenBO; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.user.service.api.dto.OAuth2AuthenticationDTO; import cn.iocoder.mall.user.service.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.user.service.api.bo.OAuth2AuthenticationBO;
public interface OAuth2Service { public interface OAuth2Service {
@ -19,13 +20,15 @@ public interface OAuth2Service {
OAuth2AccessTokenBO getAccessToken(String mobile, String code) OAuth2AccessTokenBO getAccessToken(String mobile, String code)
throws ServiceException; throws ServiceException;
CommonResult<OAuth2AccessTokenBO> getAccessToken2(String mobile, String code);
/** /**
* 校验访问令牌获取身份信息( 不包括 accessToken 等等 ) * 校验访问令牌获取身份信息( 不包括 accessToken 等等 )
* *
* @param accessToken 访问令牌 * @param accessToken 访问令牌
* @return 授权信息 * @return 授权信息
*/ */
OAuth2AuthenticationDTO checkToken(String accessToken) OAuth2AuthenticationBO checkToken(String accessToken)
throws ServiceException; throws ServiceException;
// @see 刷新 token // @see 刷新 token

View File

@ -1,17 +1,19 @@
package cn.iocoder.mall.user.service.api; package cn.iocoder.mall.user.service.api;
import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.mall.user.service.api.dto.UserDTO; import cn.iocoder.mall.user.service.api.bo.UserBO;
public interface UserService { public interface UserService {
/** /**
* 创建用户一般在用户注册时调用该方法 * 创建用户一般在用户注册时调用该方法
* *
* TODO 芋艿此处要传递一些用户注册时的相关信息例如说 ipua客户端来源等等用于数据分析风控等等
*
* @param mobile 手机号 * @param mobile 手机号
* @param code 手机验证码 * @param code 手机验证码
* @return 用户 * @return 用户
*/ */
UserDTO createUser(String mobile, String code) throws ServiceException; UserBO createUser(String mobile, String code) throws ServiceException;
} }

View File

@ -1,4 +1,4 @@
package cn.iocoder.mall.user.service.api.dto; package cn.iocoder.mall.user.service.api.bo;
import java.io.Serializable; import java.io.Serializable;

View File

@ -1,8 +1,8 @@
package cn.iocoder.mall.user.service.api.dto; package cn.iocoder.mall.user.service.api.bo;
import java.io.Serializable; import java.io.Serializable;
public class OAuth2AuthenticationDTO implements Serializable { public class OAuth2AuthenticationBO implements Serializable {
/** /**
* 用户编号 * 用户编号
@ -13,7 +13,7 @@ public class OAuth2AuthenticationDTO implements Serializable {
return uid; return uid;
} }
public OAuth2AuthenticationDTO setUid(Long uid) { public OAuth2AuthenticationBO setUid(Long uid) {
this.uid = uid; this.uid = uid;
return this; return this;
} }

View File

@ -1,6 +1,6 @@
package cn.iocoder.mall.user.service.api.dto; package cn.iocoder.mall.user.service.api.bo;
public class UserDTO { public class UserBO {
/** /**
* 用户编号 * 用户编号
@ -15,7 +15,7 @@ public class UserDTO {
return uid; return uid;
} }
public UserDTO setUid(Long uid) { public UserBO setUid(Long uid) {
this.uid = uid; this.uid = uid;
return this; return this;
} }
@ -24,7 +24,7 @@ public class UserDTO {
return mobile; return mobile;
} }
public UserDTO setMobile(String mobile) { public UserBO setMobile(String mobile) {
this.mobile = mobile; this.mobile = mobile;
return this; return this;
} }

View File

@ -1,15 +0,0 @@
package cn.iocoder.mall.user.service.api.dto;
public class MobileCodeDTO {
private String code;
public String getCode() {
return code;
}
public MobileCodeDTO setCode(String code) {
this.code = code;
return this;
}
}

View File

@ -9,6 +9,10 @@
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties>
<org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
</properties>
<artifactId>user-service-impl</artifactId> <artifactId>user-service-impl</artifactId>
<dependencies> <dependencies>
<dependency> <dependency>
@ -41,11 +45,31 @@
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <artifactId>mapstruct</artifactId>
<version>1.3.0.Final</version> <version>${org.mapstruct.version}</version>
<scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<!-- 提供给 mapstruct 使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source> <!-- or higher, depending on your project -->
<target>1.8</target> <!-- or higher, depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -1,21 +1,29 @@
package cn.iocoder.mall.user.convert; package cn.iocoder.mall.user.convert;
import cn.iocoder.mall.user.dataobject.OAuth2AccessTokenDO; import cn.iocoder.mall.user.dataobject.OAuth2AccessTokenDO;
import cn.iocoder.mall.user.service.api.dto.OAuth2AccessTokenBO; import cn.iocoder.mall.user.service.api.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.user.service.api.bo.OAuth2AuthenticationBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
@Mapper
public interface OAuth2Convert { public interface OAuth2Convert {
OAuth2Convert INSTANCE = Mappers.getMapper(OAuth2Convert.class); OAuth2Convert INSTANCE = Mappers.getMapper(OAuth2Convert.class);
@Mappings({
@Mapping(source = "id", target = "accessToken")
})
OAuth2AccessTokenBO convertToAccessToken(OAuth2AccessTokenDO oauth2AccessTokenDO);
default OAuth2AccessTokenBO convertToAccessTokenWithExpiresIn(OAuth2AccessTokenDO oauth2AccessTokenDO) {
return this.convertToAccessToken(oauth2AccessTokenDO)
.setExpiresIn(Math.max((int) ((oauth2AccessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis()) / 1000), 0));
}
@Mappings({}) @Mappings({})
OAuth2AccessTokenBO convert(OAuth2AccessTokenDO oauth2AccessTokenDO); OAuth2AuthenticationBO convertToAuthentication(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;
}
} }

View File

@ -0,0 +1,17 @@
package cn.iocoder.mall.user.convert;
import cn.iocoder.mall.user.dataobject.UserDO;
import cn.iocoder.mall.user.service.api.bo.UserBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
@Mappings({})
UserBO convert(UserDO userDO);
}

View File

@ -7,7 +7,7 @@ public class OAuth2AccessTokenDO {
/** /**
* 访问令牌 * 访问令牌
*/ */
private String tokenId; private String id;
/** /**
* 刷新令牌 * 刷新令牌
*/ */
@ -29,12 +29,12 @@ public class OAuth2AccessTokenDO {
*/ */
private Date createTime; private Date createTime;
public String getTokenId() { public String getId() {
return tokenId; return id;
} }
public OAuth2AccessTokenDO setTokenId(String tokenId) { public OAuth2AccessTokenDO setId(String id) {
this.tokenId = tokenId; this.id = id;
return this; return this;
} }

View File

@ -12,7 +12,7 @@ public class OAuth2RefreshTokenDO {
/** /**
* 刷新令牌 * 刷新令牌
*/ */
private String tokenId; private String id;
/** /**
* 用户编号 * 用户编号
*/ */
@ -30,12 +30,12 @@ public class OAuth2RefreshTokenDO {
*/ */
private Date createTime; private Date createTime;
public String getTokenId() { public String getId() {
return tokenId; return id;
} }
public OAuth2RefreshTokenDO setTokenId(String tokenId) { public OAuth2RefreshTokenDO setId(String id) {
this.tokenId = tokenId; this.id = id;
return this; return this;
} }

View File

@ -1,6 +1,7 @@
package cn.iocoder.mall.user.service; package cn.iocoder.mall.user.service;
import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.user.dao.MobileCodeMapper; import cn.iocoder.mall.user.dao.MobileCodeMapper;
import cn.iocoder.mall.user.dataobject.MobileCodeDO; import cn.iocoder.mall.user.dataobject.MobileCodeDO;
import cn.iocoder.mall.user.service.api.MobileCodeService; import cn.iocoder.mall.user.service.api.MobileCodeService;
@ -63,6 +64,30 @@ public class MobileCodeServiceImpl implements MobileCodeService {
return mobileCodePO; return mobileCodePO;
} }
/**
* 校验手机号的最后一个手机验证码是否有效
*
* @param mobile 手机号
* @param code 验证码
* @return 手机验证码信息
*/
public CommonResult<MobileCodeDO> validLastMobileCode2(String mobile, String code) {
MobileCodeDO mobileCodePO = mobileCodeMapper.selectLast1ByMobile(mobile);
if (mobileCodePO == null) { // 若验证码不存在抛出异常
return ServiceExceptionUtil.error(UserErrorCodeEnum.MOBILE_CODE_NOT_FOUND.getCode());
}
if (System.currentTimeMillis() - mobileCodePO.getCreateTime().getTime() >= codeExpireTimes) { // 验证码已过期
return ServiceExceptionUtil.error(UserErrorCodeEnum.MOBILE_CODE_EXPIRED.getCode());
}
if (mobileCodePO.getUsed()) { // 验证码已使用
return ServiceExceptionUtil.error(UserErrorCodeEnum.MOBILE_CODE_USED.getCode());
}
if (!mobileCodePO.getCode().equals(code)) {
return ServiceExceptionUtil.error(UserErrorCodeEnum.MOBILE_CODE_NOT_CORRECT.getCode());
}
return CommonResult.success(mobileCodePO);
}
/** /**
* 更新手机验证码已使用 * 更新手机验证码已使用
* *

View File

@ -2,6 +2,7 @@ package cn.iocoder.mall.user.service;
import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.user.convert.OAuth2Convert; import cn.iocoder.mall.user.convert.OAuth2Convert;
import cn.iocoder.mall.user.dao.OAuth2AccessTokenMapper; import cn.iocoder.mall.user.dao.OAuth2AccessTokenMapper;
import cn.iocoder.mall.user.dao.OAuth2RefreshTokenMapper; import cn.iocoder.mall.user.dao.OAuth2RefreshTokenMapper;
@ -11,12 +12,13 @@ import cn.iocoder.mall.user.dataobject.OAuth2RefreshTokenDO;
import cn.iocoder.mall.user.dataobject.UserDO; import cn.iocoder.mall.user.dataobject.UserDO;
import cn.iocoder.mall.user.service.api.OAuth2Service; import cn.iocoder.mall.user.service.api.OAuth2Service;
import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum; 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.bo.OAuth2AccessTokenBO;
import cn.iocoder.mall.user.service.api.dto.OAuth2AuthenticationDTO; import cn.iocoder.mall.user.service.api.bo.OAuth2AuthenticationBO;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
@ -61,15 +63,39 @@ public class OAuth2ServiceImpl implements OAuth2Service {
// 创建刷新令牌 // 创建刷新令牌
OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(userDO.getId()); OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(userDO.getId());
// 创建访问令牌 // 创建访问令牌
OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(userDO.getId(), oauth2RefreshTokenDO.getTokenId()); OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(userDO.getId(), oauth2RefreshTokenDO.getId());
// 标记已使用 // 标记已使用
mobileCodeService.useMobileCode(mobileCodeDO.getId(), userDO.getId()); mobileCodeService.useMobileCode(mobileCodeDO.getId(), userDO.getId());
// 转换返回 // 转换返回
return OAuth2Convert.INSTANCE.convertWithExpiresIn(oauth2AccessTokenDO); return OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO);
} }
@Override @Override
public OAuth2AuthenticationDTO checkToken(String accessToken) throws ServiceException { @Transactional
public CommonResult<OAuth2AccessTokenBO> getAccessToken2(String mobile, String code) {
// 校验传入的 mobile code 是否合法
CommonResult<MobileCodeDO> result = mobileCodeService.validLastMobileCode2(mobile, code);
if (result.isError()) {
return CommonResult.error(result);
}
// 获取用户
UserDO userDO = userService.getUser(mobile);
if (userDO == null) { // 用户不存在则进行创建用户
userDO = userService.createUser(mobile);
Assert.notNull(userDO, "创建用户必然成功");
}
// 创建刷新令牌
OAuth2RefreshTokenDO oauth2RefreshTokenDO = createOAuth2RefreshToken(userDO.getId());
// 创建访问令牌
OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(userDO.getId(), oauth2RefreshTokenDO.getId());
// 标记已使用
// mobileCodeService.useMobileCode(result.getData().getId(), userDO.getId());
// 转换返回
return CommonResult.success(OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO));
}
@Override
public OAuth2AuthenticationBO checkToken(String accessToken) throws ServiceException {
OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByTokenId(accessToken); OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByTokenId(accessToken);
if (accessTokenDO == null) { // 不存在 if (accessTokenDO == null) { // 不存在
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.OAUTH_INVALID_TOKEN_NOT_FOUND.getCode()); throw ServiceExceptionUtil.exception(UserErrorCodeEnum.OAUTH_INVALID_TOKEN_NOT_FOUND.getCode());
@ -81,11 +107,11 @@ public class OAuth2ServiceImpl implements OAuth2Service {
throw ServiceExceptionUtil.exception(UserErrorCodeEnum.OAUTH_INVALID_TOKEN_INVALID.getCode()); throw ServiceExceptionUtil.exception(UserErrorCodeEnum.OAUTH_INVALID_TOKEN_INVALID.getCode());
} }
// 转换返回 // 转换返回
return new OAuth2AuthenticationDTO().setUid(accessTokenDO.getUid()); return OAuth2Convert.INSTANCE.convertToAuthentication(accessTokenDO);
} }
private OAuth2AccessTokenDO createOAuth2AccessToken(Long uid, String refreshToken) { private OAuth2AccessTokenDO createOAuth2AccessToken(Long uid, String refreshToken) {
OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setTokenId(generateAccessToken()) OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setId(generateAccessToken())
.setRefreshToken(refreshToken) .setRefreshToken(refreshToken)
.setUid(uid) .setUid(uid)
.setExpiresTime(new Date(System.currentTimeMillis() + accessTokenExpireTimeMillis)) .setExpiresTime(new Date(System.currentTimeMillis() + accessTokenExpireTimeMillis))
@ -95,7 +121,7 @@ public class OAuth2ServiceImpl implements OAuth2Service {
} }
private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long uid) { private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long uid) {
OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setTokenId(generateRefreshToken()) OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setId(generateRefreshToken())
.setUid(uid) .setUid(uid)
.setExpiresTime(new Date(System.currentTimeMillis() + refreshTokenExpireTimeMillis)) .setExpiresTime(new Date(System.currentTimeMillis() + refreshTokenExpireTimeMillis))
.setValid(true); .setValid(true);

View File

@ -1,14 +1,14 @@
package cn.iocoder.mall.user.service; package cn.iocoder.mall.user.service;
import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.mall.user.convert.UserConvert;
import cn.iocoder.mall.user.dao.UserMapper; import cn.iocoder.mall.user.dao.UserMapper;
import cn.iocoder.mall.user.dao.UserRegisterMapper; import cn.iocoder.mall.user.dao.UserRegisterMapper;
import cn.iocoder.mall.user.dataobject.UserDO; import cn.iocoder.mall.user.dataobject.UserDO;
import cn.iocoder.mall.user.dataobject.UserRegisterDO; import cn.iocoder.mall.user.dataobject.UserRegisterDO;
import cn.iocoder.mall.user.service.api.UserService; import cn.iocoder.mall.user.service.api.UserService;
import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum; import cn.iocoder.mall.user.service.api.constant.UserErrorCodeEnum;
import cn.iocoder.mall.user.service.api.dto.UserDTO; import cn.iocoder.mall.user.service.api.bo.UserBO;
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -34,7 +34,7 @@ public class UserServiceImpl implements UserService {
@Override @Override
@Transactional @Transactional
public UserDTO createUser(String mobile, String code) { public UserBO createUser(String mobile, String code) {
// TODO 芋艿校验手机格式 // TODO 芋艿校验手机格式
// 校验手机号的最后一个手机验证码是否有效 // 校验手机号的最后一个手机验证码是否有效
mobileCodeService.validLastMobileCode(mobile, code); mobileCodeService.validLastMobileCode(mobile, code);
@ -48,7 +48,22 @@ public class UserServiceImpl implements UserService {
// 插入注册信息 // 插入注册信息
createUserRegister(userDO); createUserRegister(userDO);
// 转换返回 // 转换返回
return new UserDTO().setUid(userDO.getId()); return UserConvert.INSTANCE.convert(userDO);
}
@Transactional
public UserDO createUser(String mobile) {
// 校验用户是否已经存在
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 userDO;
} }
private void createUserRegister(UserDO userDO) { private void createUserRegister(UserDO userDO) {

View File

@ -4,10 +4,10 @@
<insert id="insert" parameterType="OAuth2AccessTokenDO"> <insert id="insert" parameterType="OAuth2AccessTokenDO">
INSERT INTO oauth2_access_token ( INSERT INTO oauth2_access_token (
token_id, refresh_token, id, valid, expires_time, id, refresh_token, uid, valid, expires_time,
create_time create_time
) VALUES ( ) VALUES (
#{tokenId}, #{refreshToken}, #{id}, #{valid}, #{expiresTime}, #{id}, #{refreshToken}, #{uid}, #{valid}, #{expiresTime},
#{createTime} #{createTime}
) )
</insert> </insert>
@ -16,7 +16,7 @@
SELECT SELECT
id, valid, expires_time id, valid, expires_time
FROM oauth2_access_token FROM oauth2_access_token
WHERE token_id = #{tokenId} WHERE token_id = #{id}
</select> </select>
</mapper> </mapper>

View File

@ -4,9 +4,9 @@
<insert id="insert" parameterType="OAuth2RefreshTokenDO"> <insert id="insert" parameterType="OAuth2RefreshTokenDO">
INSERT INTO oauth2_refresh_token ( INSERT INTO oauth2_refresh_token (
token_id, id, valid, expires_time, create_time id, uid, valid, expires_time, create_time
) VALUES ( ) VALUES (
#{tokenId}, #{id}, #{valid}, #{expiresTime}, #{createTime} #{id}, #{uid}, #{valid}, #{expiresTime}, #{createTime}
) )
</insert> </insert>