diff --git a/docs/guides/功能列表/功能列表-管理后台.md b/docs/guides/功能列表/功能列表-管理后台.md index bb534edfc..be9b5190f 100644 --- a/docs/guides/功能列表/功能列表-管理后台.md +++ b/docs/guides/功能列表/功能列表-管理后台.md @@ -14,11 +14,11 @@ - [x] 发布商品 - [x] 商品列表 - [x] 展示类目 - - [ ] 品牌管理【待认领】 + - [ ] 品牌管理【开发中 @黑子】 - [ ] 订单管理 - [ ] 销售单 开发中 - [ ] 售后单 开发中 - - [ ] 订单评价【开发中】 + - [ ] 订单评价【开发中 @wang171776704】 - [ ] 会员管理 - [ ] 会员资料 20%【待认领】 - TODO 需要补充 @@ -33,8 +33,10 @@ - [ ] 系统管理 - [x] 员工管理 - [x] 角色管理 - - [ ] 权限管理 - - [ ] 短信管理 + - [x] 权限管理 + - [ ] 部门管理【待认领】 + - [x] 数据字典 + - [ ] 短信管理【开发中 @小范】 - [ ] 短信模板 - [ ] 发送日志 - [ ] 员工操作日志 diff --git a/mobile-web/src/page/account/phonelogin.vue b/mobile-web/src/page/account/phonelogin.vue index 66bf1921c..a17b071ff 100644 --- a/mobile-web/src/page/account/phonelogin.vue +++ b/mobile-web/src/page/account/phonelogin.vue @@ -69,7 +69,7 @@ export default { let that = this; let response = doPassportMobileRegister(this.mobile, this.code); response.then(data => { - setLoginToken(data.accessToken, data.refreshToken); + setLoginToken(data.token.accessToken, data.token.refreshToken); Dialog.alert({ title: '系统提示', message: '登陆成功', diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java index da12818b9..dac695c57 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/AdminService.java @@ -17,7 +17,7 @@ import java.util.Map; public interface AdminService { /** - * 用户认证。认证成功后,返回认证信息 + * 管理员认证。认证成功后,返回认证信息 * * 实际上,就是用户名 + 密码登陆 * diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java index ccc78681a..eadf0d373 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/OAuth2Service.java @@ -20,6 +20,8 @@ public interface OAuth2Service { // TODO @see 刷新 token + void removeToken(Integer userId); // TODO 需要优化 + /** * 通过 accessToken 获得身份信息 * diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java index dfd6abb19..2e5146538 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/oauth2/OAuth2AuthenticationBO.java @@ -5,10 +5,12 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; +import java.io.Serializable; + @ApiModel("OAUTH2 认证 BO") @Data @Accessors(chain = true) -public class OAuth2AuthenticationBO { +public class OAuth2AuthenticationBO implements Serializable { @ApiModelProperty(value = "用户编号", required = true, example = "1") private Integer userId; diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2CreateTokenDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2CreateTokenDTO.java index a70e2298f..fbd46456c 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2CreateTokenDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2CreateTokenDTO.java @@ -8,11 +8,12 @@ import lombok.Data; import lombok.experimental.Accessors; import javax.validation.constraints.NotNull; +import java.io.Serializable; @ApiModel("OAuth2 创建 Token DTO") @Data @Accessors(chain = true) -public class OAuth2CreateTokenDTO { +public class OAuth2CreateTokenDTO implements Serializable { @ApiModelProperty(value = "用户编号", required = true, example = "1") @NotNull(message = "用户编号不能为空") diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2GetTokenDTO.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2GetTokenDTO.java index 0b0d4862c..b2d2b602a 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2GetTokenDTO.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/dto/oauth2/OAuth2GetTokenDTO.java @@ -9,11 +9,12 @@ import lombok.experimental.Accessors; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; +import java.io.Serializable; @ApiModel("OAuth2 身份验证 DTO") @Data @Accessors(chain = true) -public class OAuth2GetTokenDTO { +public class OAuth2GetTokenDTO implements Serializable { @ApiModelProperty(value = "accessToken", required = true, example = "001e8f49b20e47f7b3a2de774497cd50") @NotEmpty(message = "accessToken 不能为空") diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java index 5db47a9c1..035cf4edf 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/OAuth2ServiceImpl.java @@ -64,6 +64,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { * * @param adminId 管理员编号 */ + @Override @Transactional public void removeToken(Integer adminId) { // 设置 access token 失效 diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java index 8d852a9d3..80f4b3853 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java @@ -1,9 +1,9 @@ package cn.iocoder.mall.admin.service; +import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.mall.admin.api.SmsPlatform; import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; import cn.iocoder.mall.admin.api.constant.SmsApplyStatusEnum; -import cn.iocoder.mall.admin.api.exception.SmsFailException; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -104,7 +104,7 @@ public class SmsYunPianPlatform implements SmsPlatform { String result = post(URL_SIGN_ADD, params); JSONObject jsonObject = JSON.parseObject(result); if (!(jsonObject.getInteger("code") == SUCCESS_CODE)) { - throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getCode(), + throw new ServiceException(AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getCode(), AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getMessage()); } @@ -124,13 +124,13 @@ public class SmsYunPianPlatform implements SmsPlatform { JSONObject jsonObject = JSON.parseObject(result); if (!(jsonObject.getInteger("code") == SUCCESS_CODE)) { - throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getCode(), + throw new ServiceException(AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getCode(), AdminErrorCodeEnum.SMS_SIGN_ADD_FAIL.getMessage()); } JSONArray jsonArray = jsonObject.getJSONArray("sign"); if (jsonArray.size() <= 0) { - throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getCode(), + throw new ServiceException(AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getCode(), AdminErrorCodeEnum.SMS_SIGN_NOT_EXISTENT.getMessage()); } @@ -151,7 +151,7 @@ public class SmsYunPianPlatform implements SmsPlatform { JSONObject jsonObject = JSON.parseObject(result); if (!(jsonObject.getInteger("code") == SUCCESS_CODE)) { - throw new SmsFailException(AdminErrorCodeEnum.SMS_SIGN_UPDATE_FAIL.getCode(), + throw new ServiceException(AdminErrorCodeEnum.SMS_SIGN_UPDATE_FAIL.getCode(), AdminErrorCodeEnum.SMS_SIGN_UPDATE_FAIL.getMessage()); } diff --git a/user/user-application/src/main/java/cn/iocoder/mall/user/application/controller/users/PassportController.java b/user/user-application/src/main/java/cn/iocoder/mall/user/application/controller/users/PassportController.java index 80db85f14..c51c64ad7 100644 --- a/user/user-application/src/main/java/cn/iocoder/mall/user/application/controller/users/PassportController.java +++ b/user/user-application/src/main/java/cn/iocoder/mall/user/application/controller/users/PassportController.java @@ -5,13 +5,13 @@ import cn.iocoder.mall.user.api.MobileCodeService; import cn.iocoder.mall.user.api.OAuth2Service; import cn.iocoder.mall.user.api.UserService; import cn.iocoder.mall.user.api.bo.OAuth2AccessTokenBO; +import cn.iocoder.mall.user.api.bo.user.UserAuthenticationBO; +import cn.iocoder.mall.user.api.dto.user.UserAuthenticationByMobileCodeDTO; import cn.iocoder.mall.user.application.convert.PassportConvert; import cn.iocoder.mall.user.application.vo.users.UsersAccessTokenVO; -import cn.iocoder.mall.user.application.vo.users.UsersMobileRegisterVO; import cn.iocoder.mall.user.sdk.annotation.PermitAll; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.apache.dubbo.config.annotation.Reference; import org.springframework.web.bind.annotation.PostMapping; @@ -43,14 +43,8 @@ public class PassportController { @PermitAll @PostMapping("/mobile/register") @ApiOperation(value = "手机号 + 验证码登陆(注册)", notes = "如果手机对应的账号不存在,则会自动创建") - @ApiImplicitParams({ - @ApiImplicitParam(name = "mobile", value = "手机号", required = true, example = "15601691300"), - @ApiImplicitParam(name = "code", value = "验证码", required = true, example = "9999") - }) - public CommonResult mobileRegister(@RequestParam("mobile") String mobile, - @RequestParam("code") String code) { - OAuth2AccessTokenBO result = oauth2Service.getAccessToken(mobile, code); - return success(PassportConvert.INSTANCE.convert(result)); + public CommonResult mobileRegister(UserAuthenticationByMobileCodeDTO userAuthenticationByMobileCodeDTO) { + return success(userService.authenticationByMobileCode(userAuthenticationByMobileCodeDTO)); } @PermitAll diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContext.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContext.java index c43328309..c6ab1a707 100644 --- a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContext.java +++ b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContext.java @@ -1,18 +1,18 @@ package cn.iocoder.mall.user.sdk.context; +import lombok.Data; +import lombok.experimental.Accessors; + /** * User Security 上下文 */ +@Data +@Accessors(chain = true) public class UserSecurityContext { - private final Integer userId; + /** + * 用户编号 + */ + private Integer userId; - public UserSecurityContext(Integer userId) { - this.userId = userId; - } - - public Integer getUserId() { - return userId; - } - -} \ No newline at end of file +} diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java index 09ca1bcd1..c63c1a43b 100644 --- a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java +++ b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/context/UserSecurityContextHolder.java @@ -17,7 +17,7 @@ public class UserSecurityContextHolder { UserSecurityContext ctx = SECURITY_CONTEXT.get(); // 为空时,设置一个空的进去 if (ctx == null) { - ctx = new UserSecurityContext(null); + ctx = new UserSecurityContext(); SECURITY_CONTEXT.set(ctx); } return ctx; diff --git a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/UserSecurityInterceptor.java b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/UserSecurityInterceptor.java index 9404aa9bf..f559222f2 100644 --- a/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/UserSecurityInterceptor.java +++ b/user/user-sdk/src/main/java/cn/iocoder/mall/user/sdk/interceptor/UserSecurityInterceptor.java @@ -1,11 +1,14 @@ package cn.iocoder.mall.user.sdk.interceptor; -import cn.iocoder.common.framework.constant.MallConstants; +import cn.iocoder.common.framework.constant.UserTypeEnum; import cn.iocoder.common.framework.exception.ServiceException; import cn.iocoder.common.framework.util.HttpUtil; import cn.iocoder.common.framework.util.MallUtil; -import cn.iocoder.mall.user.api.OAuth2Service; -import cn.iocoder.mall.user.api.bo.OAuth2AuthenticationBO; +import cn.iocoder.common.framework.util.StringUtil; +import cn.iocoder.mall.admin.api.OAuth2Service; +import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AuthenticationBO; +import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2GetTokenDTO; import cn.iocoder.mall.user.sdk.annotation.PermitAll; import cn.iocoder.mall.user.sdk.context.UserSecurityContext; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; @@ -18,40 +21,55 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** - * 安全拦截器 + * User 安全拦截器 */ @Component public class UserSecurityInterceptor extends HandlerInterceptorAdapter { - @Reference(validation = "true", version = "${dubbo.provider.OAuth2Service.version:1.0.0}") + @Reference(validation = "true", version = "${dubbo.consumer.OAuth2Service.version:1.0.0}") private OAuth2Service oauth2Service; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 设置当前访问的用户类型。注意,即使未登陆,我们也认为是用户 - MallUtil.setUserType(request, MallConstants.USER_TYPE_USER); - // 校验访问令牌是否正确。若正确,返回授权信息 + MallUtil.setUserType(request, UserTypeEnum.USER.getValue()); + + // 根据 accessToken 获得认证信息,判断是谁 String accessToken = HttpUtil.obtainAuthorization(request); OAuth2AuthenticationBO authentication = null; - if (accessToken != null) { - authentication = oauth2Service.checkToken(accessToken); // TODO 芋艿,如果访问的地址无需登录,这里也不用抛异常 - // 添加到 SecurityContext - UserSecurityContext context = new UserSecurityContext(authentication.getUserId()); - UserSecurityContextHolder.setContext(context); - // 同时也记录管理员编号到 AdminAccessLogInterceptor 中。因为: - // AdminAccessLogInterceptor 需要在 AdminSecurityInterceptor 之前执行,这样记录的访问日志才健全 - // AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号 - // 因此,这里需要进行记录 - if (authentication.getUserId() != null) { - MallUtil.setUserId(request, authentication.getUserId()); + ServiceException serviceException = null; + if (StringUtil.hasText(accessToken)) { + try { + authentication = oauth2Service.getAuthentication(new OAuth2GetTokenDTO().setAccessToken(accessToken) + .setUserType(UserTypeEnum.USER.getValue())); + } catch (ServiceException e) { + serviceException = e; } } - // 校验是否需要已授权 + + // 进行鉴权 HandlerMethod method = (HandlerMethod) handler; boolean isPermitAll = method.hasMethodAnnotation(PermitAll.class); - if (!isPermitAll && authentication == null) { - throw new ServiceException(-1, "未授权"); // TODO 这里要改下 + if (!isPermitAll) { // 如果需要鉴权 + if (serviceException != null) { // 认证失败,抛出上面认证失败的 ServiceException 异常 + throw serviceException; + } + if (authentication == null) { // 无认证信息,抛出未登陆 ServiceException 异常 + throw new ServiceException(AdminErrorCodeEnum.OAUTH2_NOT_LOGIN.getCode(), AdminErrorCodeEnum.OAUTH2_NOT_LOGIN.getMessage()); + } + // TODO 芋艿,后续拓展读取用户信息 } + + // 鉴权完成,初始化 AdminSecurityContext 上下文 + UserSecurityContext context = new UserSecurityContext(); + UserSecurityContextHolder.setContext(context); + if (authentication != null) { + context.setUserId(authentication.getUserId()); + MallUtil.setUserId(request, authentication.getUserId()); // 记录到 request 中,避免 AdminSecurityContext 后续清理掉后,其它地方需要用到 userId + // TODO 芋艿,后续拓展读取用户信息 + } + + // 返回成功 return super.preHandle(request, response, handler); } diff --git a/user/user-service-api/pom.xml b/user/user-service-api/pom.xml index 8054f41cd..92eade7ab 100644 --- a/user/user-service-api/pom.xml +++ b/user/user-service-api/pom.xml @@ -17,6 +17,11 @@ common-framework 1.0-SNAPSHOT + + cn.iocoder.mall + system-service-api + 1.0-SNAPSHOT + diff --git a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/OAuth2Service.java b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/OAuth2Service.java index 2a3ae3870..b6cbb6f27 100644 --- a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/OAuth2Service.java +++ b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/OAuth2Service.java @@ -4,10 +4,9 @@ package cn.iocoder.mall.user.api; import cn.iocoder.mall.user.api.bo.OAuth2AccessTokenBO; import cn.iocoder.mall.user.api.bo.OAuth2AuthenticationBO; +@Deprecated public interface OAuth2Service { - OAuth2AccessTokenBO getAccessToken(String mobile, String code); - /** * 校验访问令牌,获取身份信息( 不包括 accessToken 等等 ) * diff --git a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserAccessLogService.java b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserAccessLogService.java index 972d6e195..8e5fcaa6b 100644 --- a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserAccessLogService.java +++ b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserAccessLogService.java @@ -2,6 +2,7 @@ package cn.iocoder.mall.user.api; import cn.iocoder.mall.user.api.dto.UserAccessLogAddDTO; +@Deprecated public interface UserAccessLogService { void addUserAccessLog(UserAccessLogAddDTO userAccessLogAddDTO); diff --git a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserService.java b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserService.java index b97c94aeb..94b3d6031 100644 --- a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserService.java +++ b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/UserService.java @@ -2,13 +2,17 @@ package cn.iocoder.mall.user.api; import cn.iocoder.common.framework.constant.CommonStatusEnum; import cn.iocoder.common.framework.validator.InEnum; +import cn.iocoder.mall.user.api.bo.user.UserAuthenticationBO; import cn.iocoder.mall.user.api.bo.UserBO; import cn.iocoder.mall.user.api.bo.UserPageBO; import cn.iocoder.mall.user.api.dto.UserPageDTO; import cn.iocoder.mall.user.api.dto.UserUpdateDTO; +import cn.iocoder.mall.user.api.dto.user.UserAuthenticationByMobileCodeDTO; public interface UserService { + UserAuthenticationBO authenticationByMobileCode(UserAuthenticationByMobileCodeDTO userAuthenticationByMobileCodeDTO); + UserPageBO getUserPage(UserPageDTO userPageDTO); UserBO getUser(Integer userId); diff --git a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/bo/user/UserAuthenticationBO.java b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/bo/user/UserAuthenticationBO.java new file mode 100644 index 000000000..06e401011 --- /dev/null +++ b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/bo/user/UserAuthenticationBO.java @@ -0,0 +1,22 @@ +package cn.iocoder.mall.user.api.bo.user; + +import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +@ApiModel("用户认证 BO") +@Data +@Accessors(chain = true) +public class UserAuthenticationBO { + + @ApiModelProperty(value = "用户编号", required = true, example = "1") + private Integer id; + + @ApiModelProperty(value = "昵称", required = true, example = "小王") + private String nickname; + + private OAuth2AccessTokenBO token; + +} diff --git a/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/dto/user/UserAuthenticationByMobileCodeDTO.java b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/dto/user/UserAuthenticationByMobileCodeDTO.java new file mode 100644 index 000000000..c63ca07ed --- /dev/null +++ b/user/user-service-api/src/main/java/cn/iocoder/mall/user/api/dto/user/UserAuthenticationByMobileCodeDTO.java @@ -0,0 +1,29 @@ +package cn.iocoder.mall.user.api.dto.user; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; + +@ApiModel("用户认证 DTO") +@Data +@Accessors(chain = true) +public class UserAuthenticationByMobileCodeDTO { + + @ApiModelProperty(value = "手机号", required = true, example = "15601691300") + @NotEmpty(message = "手机号不能为空") + @Length(min = 11, max = 11, message = "账号长度为 11 位") + @Pattern(regexp = "^[0-9]+$", message = "手机号必须都是数字") + private String mobile; + + @ApiModelProperty(value = "手机验证码", required = true, example = "1024") + @NotEmpty(message = "手机验证码不能为空") + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") + private String code; + +} diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/convert/UserConvert.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/convert/UserConvert.java index 3b7a37a21..ea9bb8dff 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/convert/UserConvert.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/convert/UserConvert.java @@ -1,8 +1,9 @@ package cn.iocoder.mall.user.biz.convert; -import cn.iocoder.mall.user.biz.dataobject.UserDO; +import cn.iocoder.mall.user.api.bo.user.UserAuthenticationBO; import cn.iocoder.mall.user.api.bo.UserBO; import cn.iocoder.mall.user.api.dto.UserUpdateDTO; +import cn.iocoder.mall.user.biz.dataobject.UserDO; import org.mapstruct.Mapper; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; @@ -17,10 +18,13 @@ public interface UserConvert { @Mappings({}) UserBO convert(UserDO userDO); + @Mappings({}) + UserAuthenticationBO convert2(UserDO userDO); + @Mappings({}) UserDO convert(UserUpdateDTO userUpdateDTO); @Mappings({}) List convert(List userDOs); -} \ No newline at end of file +} diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dao/MobileCodeMapper.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dao/MobileCodeMapper.java index 6691a75c5..1e68c74b7 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dao/MobileCodeMapper.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dao/MobileCodeMapper.java @@ -1,19 +1,12 @@ package cn.iocoder.mall.user.biz.dao; import cn.iocoder.mall.user.biz.dataobject.MobileCodeDO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; @Repository // 实际不加也没问entity,就是不想 IDEA 那看到有个报错 -public interface MobileCodeMapper { - - void insert(MobileCodeDO entity); - - /** - * 更新手机验证码 - * - * @param entity 更新信息 - */ - void update(MobileCodeDO entity); +public interface MobileCodeMapper extends BaseMapper { /** * 获得手机号的最后一个手机验证码 @@ -21,6 +14,12 @@ public interface MobileCodeMapper { * @param mobile 手机号 * @return 手机验证码 */ - MobileCodeDO selectLast1ByMobile(String mobile); + default MobileCodeDO selectLast1ByMobile(String mobile) { + QueryWrapper query = new QueryWrapper() + .eq("mobile", mobile) + .orderByDesc("id") + .last("limit 1"); + return selectOne(query); + } -} \ No newline at end of file +} diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dataobject/MobileCodeDO.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dataobject/MobileCodeDO.java index aec91c673..0963e41ff 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dataobject/MobileCodeDO.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/dataobject/MobileCodeDO.java @@ -1,14 +1,17 @@ package cn.iocoder.mall.user.biz.dataobject; +import cn.iocoder.common.framework.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.experimental.Accessors; import java.util.Date; // TODO 优化,IP +@TableName("mobile_code") @Data @Accessors(chain = true) -public class MobileCodeDO { +public class MobileCodeDO extends BaseDO { /** * 编号 @@ -34,10 +37,6 @@ public class MobileCodeDO { * 注册的用户编号 */ private Integer usedUserId; - /** - * 创建时间 - */ - private Date createTime; /** * 使用时间 */ diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/MobileCodeServiceImpl.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/MobileCodeServiceImpl.java index 11e78ae95..eba7226cb 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/MobileCodeServiceImpl.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/MobileCodeServiceImpl.java @@ -50,21 +50,21 @@ public class MobileCodeServiceImpl implements MobileCodeService { */ public MobileCodeDO validLastMobileCode(String mobile, String code) { // TODO: 2019-04-09 Sin 暂时先忽略掉验证码校验 - return new MobileCodeDO().setCode(code).setCreateTime(new Date()).setId(1); -// 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; +// return new MobileCodeDO().setCode(code).setCreateTime(new Date()).setId(1); + 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; } /** @@ -75,7 +75,7 @@ public class MobileCodeServiceImpl implements MobileCodeService { */ public void useMobileCode(Integer id, Integer userId) { MobileCodeDO update = new MobileCodeDO().setId(id).setUsed(true).setUsedUserId(userId).setUsedTime(new Date()); - mobileCodeMapper.update(update); + mobileCodeMapper.updateById(update); } // TODO 芋艿,后面要返回有效时间 @@ -99,7 +99,8 @@ public class MobileCodeServiceImpl implements MobileCodeService { MobileCodeDO newMobileCodePO = new MobileCodeDO().setMobile(mobile) .setCode("9999") // TODO 芋艿,随机 4 位验证码 or 6 位验证码 .setTodayIndex(lastMobileCodePO != null ? lastMobileCodePO.getTodayIndex() : 1) - .setUsed(false).setCreateTime(new Date()); + .setUsed(false); + newMobileCodePO.setCreateTime(new Date()); mobileCodeMapper.insert(newMobileCodePO); // TODO 发送验证码短信 } diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/OAuth2ServiceImpl.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/OAuth2ServiceImpl.java index b4880370c..a568d0526 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/OAuth2ServiceImpl.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/OAuth2ServiceImpl.java @@ -49,27 +49,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; - @Override - @Transactional - public OAuth2AccessTokenBO getAccessToken(String mobile, String code) { - // 校验传入的 mobile 和 code 是否合法 - MobileCodeDO mobileCodeDO = mobileCodeService.validLastMobileCode(mobile, code); - // 获取用户 - 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(mobileCodeDO.getId(), userDO.getId()); - // 转换返回 - return OAuth2Convert.INSTANCE.convertToAccessTokenWithExpiresIn(oauth2AccessTokenDO); - } - @Override public OAuth2AuthenticationBO checkToken(String accessToken) throws ServiceException { OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByTokenId(accessToken); diff --git a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/UserServiceImpl.java b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/UserServiceImpl.java index f9fd61acb..874a48ebd 100644 --- a/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/UserServiceImpl.java +++ b/user/user-service-impl/src/main/java/cn/iocoder/mall/user/biz/service/UserServiceImpl.java @@ -3,20 +3,28 @@ package cn.iocoder.mall.user.biz.service; import cn.iocoder.common.framework.constant.CommonStatusEnum; import cn.iocoder.common.framework.constant.DeletedStatusEnum; import cn.iocoder.common.framework.constant.SysErrorCodeEnum; +import cn.iocoder.common.framework.constant.UserTypeEnum; import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ValidationUtil; +import cn.iocoder.mall.admin.api.OAuth2Service; +import cn.iocoder.mall.admin.api.bo.oauth2.OAuth2AccessTokenBO; +import cn.iocoder.mall.admin.api.dto.oauth2.OAuth2CreateTokenDTO; import cn.iocoder.mall.user.api.UserService; +import cn.iocoder.mall.user.api.bo.user.UserAuthenticationBO; import cn.iocoder.mall.user.api.bo.UserBO; import cn.iocoder.mall.user.api.bo.UserPageBO; import cn.iocoder.mall.user.api.constant.UserConstants; import cn.iocoder.mall.user.api.constant.UserErrorCodeEnum; import cn.iocoder.mall.user.api.dto.UserPageDTO; import cn.iocoder.mall.user.api.dto.UserUpdateDTO; +import cn.iocoder.mall.user.api.dto.user.UserAuthenticationByMobileCodeDTO; import cn.iocoder.mall.user.biz.convert.UserConvert; import cn.iocoder.mall.user.biz.dao.UserMapper; import cn.iocoder.mall.user.biz.dao.UserRegisterMapper; +import cn.iocoder.mall.user.biz.dataobject.MobileCodeDO; import cn.iocoder.mall.user.biz.dataobject.UserDO; import cn.iocoder.mall.user.biz.dataobject.UserRegisterDO; +import org.apache.dubbo.config.annotation.Reference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,7 +43,10 @@ public class UserServiceImpl implements UserService { @Autowired private UserRegisterMapper userRegisterMapper; @Autowired - private OAuth2ServiceImpl oAuth2Service; + private MobileCodeServiceImpl mobileCodeService; + + @Reference(validation = "true", version = "${dubbo.consumer.OAuth2Service.version}") + private OAuth2Service oAuth2Service; public UserDO getUser(String mobile) { return userMapper.selectByMobile(mobile); @@ -67,6 +78,36 @@ public class UserServiceImpl implements UserService { userRegisterMapper.insert(userRegisterDO); } + @Override + @Transactional + public UserAuthenticationBO authenticationByMobileCode(UserAuthenticationByMobileCodeDTO userAuthenticationByMobileCodeDTO) { + String mobile = userAuthenticationByMobileCodeDTO.getMobile(); + String code = userAuthenticationByMobileCodeDTO.getCode(); + // 校验手机格式 + if (!ValidationUtil.isMobile(mobile)) { + throw ServiceExceptionUtil.exception(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), "手机格式不正确"); // TODO 有点搓 + } + // 校验验证码是否正确 + MobileCodeDO mobileCodeDO = mobileCodeService.validLastMobileCode(mobile, code); + // 获得用户 + UserDO user = userMapper.selectByMobile(mobile); + if (user == null) { // 用户不存在,则进行创建 + user = new UserDO().setMobile(mobile).setStatus(UserConstants.STATUS_ENABLE); + user.setCreateTime(new Date()); + user.setDeleted(DeletedStatusEnum.DELETED_NO.getValue()); + userMapper.insert(user); + // 插入注册信息 TODO 芋艿 后续完善,记录 ip、ua 等等 + createUserRegister(user); + } + // 更新验证码已使用 + mobileCodeService.useMobileCode(mobileCodeDO.getId(), user.getId()); + // 创建 accessToken + OAuth2AccessTokenBO accessTokenBO = oAuth2Service.createToken(new OAuth2CreateTokenDTO().setUserId(user.getId()) + .setUserType(UserTypeEnum.USER.getValue())); + // 转换返回 + return UserConvert.INSTANCE.convert2(user).setToken(accessTokenBO); + } + @Override public UserPageBO getUserPage(UserPageDTO userPageDTO) { UserPageBO userPageBO = new UserPageBO(); diff --git a/user/user-service-impl/src/main/resources/config/application.yaml b/user/user-service-impl/src/main/resources/config/application.yaml index 515785456..105bfc298 100644 --- a/user/user-service-impl/src/main/resources/config/application.yaml +++ b/user/user-service-impl/src/main/resources/config/application.yaml @@ -6,19 +6,17 @@ spring: username: root password: ${MALL_MYSQL_PASSWORD} -# mybatis -#mybatis: -# config-location: classpath:mybatis-config.xml -# mapper-locations: classpath:mapper/*.xml -# type-aliases-package: cn.iocoder.mall.user.biz.dataobject - # mybatis-plus mybatis-plus: configuration: - mapUnderscoreToCamelCase: true # 虽然默认为 true ,但是还是显示去指定下。 + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: auto + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapperLocations: classpath*:mapper/*.xml typeAliasesPackage: cn.iocoder.mall.user.biz.dataobject - config-location: classpath:mybatis-config.xml # dubbo dubbo: @@ -43,3 +41,6 @@ dubbo: version: 1.0.0 UserService: version: 1.0.0 + consumer: + OAuth2Service: + version: 1.0.0 diff --git a/user/user-service-impl/src/main/resources/mapper/MobileCodeMapper.xml b/user/user-service-impl/src/main/resources/mapper/MobileCodeMapper.xml deleted file mode 100644 index a5599ef9a..000000000 --- a/user/user-service-impl/src/main/resources/mapper/MobileCodeMapper.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - INSERT INTO mobile_code ( - id, mobile, code, today_index, used, - userd_user_id, used_time, create_time - ) VALUES ( - #{id}, #{mobile}, #{code}, #{todayIndex}, #{used}, - #{usedUserId}, #{usedTime}, #{createTime} - ) - - - - UPDATE mobile_code - - used = #{used}, - userd_user_id = #{usedUserId}, - used_time = #{usedTime}, - - WHERE id = #{id} - - - - - \ No newline at end of file diff --git a/user/user-service-impl/src/main/resources/mybatis-config.xml b/user/user-service-impl/src/main/resources/mybatis-config.xml deleted file mode 100644 index 7f604cc7e..000000000 --- a/user/user-service-impl/src/main/resources/mybatis-config.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file