多模块重构 2:在 yudao-admin-server 中,引入 yudao-module-member 模块

This commit is contained in:
YunaiV 2022-01-28 20:21:01 +08:00
parent 06fa85a353
commit 928b7dbe23
59 changed files with 622 additions and 460 deletions

View File

@ -17,6 +17,13 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<dependencies> <dependencies>
<!-- TODO 芋艿:多模块 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-member-impl</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 --> <!-- 业务组件 -->
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>

View File

@ -4,7 +4,8 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${yudao.info.base-package} ${yudao.core-service.base-package} @SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${yudao.info.base-package} ${yudao.core-service.base-package}
@SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}", "${yudao.core-service.base-package}"}) @SpringBootApplication(scanBasePackages = {"${yudao.info.base-package}", "${yudao.core-service.base-package}",
"${yudao.info.member-package}"}) // TODO 芋艿重构
public class AdminServerApplication { public class AdminServerApplication {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -23,19 +23,28 @@ public class SecurityConfiguration {
public Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() { public Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() {
return registry -> { return registry -> {
// 验证码的接口 // 验证码的接口
registry.antMatchers(api("/system/captcha/**")).anonymous(); registry.antMatchers(buildAdminApi("/system/captcha/**")).anonymous();
// 获得租户编号的接口 // 获得租户编号的接口
registry.antMatchers(api("/system/tenant/get-id-by-name")).anonymous(); registry.antMatchers(buildAdminApi("/system/tenant/get-id-by-name")).anonymous();
// Spring Boot Admin Server 的安全配置 // Spring Boot Admin Server 的安全配置
registry.antMatchers(adminSeverContextPath).anonymous() registry.antMatchers(adminSeverContextPath).anonymous()
.antMatchers(adminSeverContextPath + "/**").anonymous(); .antMatchers(adminSeverContextPath + "/**").anonymous();
// 短信回调 API // 短信回调 API
registry.antMatchers(api("/system/sms/callback/**")).anonymous(); registry.antMatchers(buildAdminApi("/system/sms/callback/**")).anonymous();
// 设置 App API 无需认证
registry.antMatchers(buildAppApi("/**"));
}; };
} }
private String api(String url) { private String buildAdminApi(String url) {
return webProperties.getApiPrefix() + url; // TODO 芋艿多模块
return webProperties.getAdminApi().getPrefix() + url;
}
private String buildAppApi(String url) {
// TODO 芋艿多模块
return webProperties.getAppApi().getPrefix() + url;
} }
} }

View File

@ -11,10 +11,12 @@ import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService; import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import org.activiti.api.runtime.shared.identity.UserGroupManager; import org.activiti.api.runtime.shared.identity.UserGroupManager;
import org.activiti.core.common.spring.identity.ActivitiUserGroupManagerImpl;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer; import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -28,7 +30,15 @@ import static org.activiti.spring.boot.ProcessEngineAutoConfiguration.BEHAVIOR_F
public class BpmActivitiConfiguration { public class BpmActivitiConfiguration {
/** /**
* BPM 模块的 ProcessEngineConfigurationConfigurer 实现类主要设置各种监听器用户组管理 * 空用户组的 Bean
*/
@Bean
public UserGroupManager userGroupManager() {
return new EmptyUserGroupManager();
}
/**
* BPM 模块的 ProcessEngineConfigurationConfigurer 实现类主要设置各种监听器
*/ */
@Bean @Bean
public ProcessEngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer( public ProcessEngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer(
@ -36,8 +46,6 @@ public class BpmActivitiConfiguration {
return configuration -> { return configuration -> {
// 注册监听器例如说 BpmActivitiEventListener // 注册监听器例如说 BpmActivitiEventListener
configuration.setEventListeners(Collections.singletonList(taskActivitiEventListener)); configuration.setEventListeners(Collections.singletonList(taskActivitiEventListener));
// 用户组
configuration.setUserGroupManager(new EmptyUserGroupManager());
}; };
} }

View File

@ -60,8 +60,6 @@ import static java.util.Collections.singleton;
@Slf4j @Slf4j
public class SysAuthServiceImpl implements SysAuthService { public class SysAuthServiceImpl implements SysAuthService {
private static final UserTypeEnum USER_TYPE_ENUM = UserTypeEnum.ADMIN;
@Resource @Resource
@Lazy // 延迟加载因为存在相互依赖的问题 @Lazy // 延迟加载因为存在相互依赖的问题
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
@ -83,7 +81,6 @@ public class SysAuthServiceImpl implements SysAuthService {
@Resource @Resource
private SysSocialCoreService socialService; private SysSocialCoreService socialService;
@Override @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 获取 username 对应的 SysUserDO // 获取 username 对应的 SysUserDO
@ -216,7 +213,7 @@ public class SysAuthServiceImpl implements SysAuthService {
// 如果未绑定 SysSocialUserDO 用户则无法自动登录进行报错 // 如果未绑定 SysSocialUserDO 用户则无法自动登录进行报错
String unionId = socialService.getAuthUserUnionId(authUser); String unionId = socialService.getAuthUserUnionId(authUser);
List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, USER_TYPE_ENUM); List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, getUserType());
if (CollUtil.isEmpty(socialUsers)) { if (CollUtil.isEmpty(socialUsers)) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND); throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
} }
@ -232,7 +229,7 @@ public class SysAuthServiceImpl implements SysAuthService {
LoginUser loginUser = this.buildLoginUser(user); LoginUser loginUser = this.buildLoginUser(user);
// 绑定社交用户更新 // 绑定社交用户更新
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType());
// 缓存登录用户到 Redis 返回 sessionId 编号 // 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
@ -248,7 +245,7 @@ public class SysAuthServiceImpl implements SysAuthService {
LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword()); LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
// 绑定社交用户新增 // 绑定社交用户新增
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType());
// 缓存登录用户到 Redis 返回 sessionId 编号 // 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
@ -261,7 +258,7 @@ public class SysAuthServiceImpl implements SysAuthService {
Assert.notNull(authUser, "授权用户不为空"); Assert.notNull(authUser, "授权用户不为空");
// 绑定社交用户新增 // 绑定社交用户新增
socialService.bindSocialUser(userId, reqVO.getType(), authUser, USER_TYPE_ENUM); socialService.bindSocialUser(userId, reqVO.getType(), authUser, getUserType());
} }
@Override @Override
@ -277,12 +274,17 @@ public class SysAuthServiceImpl implements SysAuthService {
this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); this.createLogoutLog(loginUser.getId(), loginUser.getUsername());
} }
@Override
public UserTypeEnum getUserType() {
return UserTypeEnum.ADMIN;
}
private void createLogoutLog(Long userId, String username) { private void createLogoutLog(Long userId, String username) {
SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setTraceId(TracerUtils.getTraceId());
reqDTO.setUserId(userId); reqDTO.setUserId(userId);
reqDTO.setUserType(USER_TYPE_ENUM.getValue()); reqDTO.setUserType(getUserType().getValue());
reqDTO.setUsername(username); reqDTO.setUsername(username);
reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserAgent(ServletUtils.getUserAgent());
reqDTO.setUserIp(ServletUtils.getClientIP()); reqDTO.setUserIp(ServletUtils.getClientIP());

View File

@ -23,6 +23,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -47,7 +48,7 @@ public class SysUserServiceImpl implements SysUserService {
@Value("${sys.user.init-password:yudaoyuanma}") @Value("${sys.user.init-password:yudaoyuanma}")
private String userInitPassword; private String userInitPassword;
@Resource @Resource(name = "sysUserMapper") // userMapper 存在重名
private SysUserMapper userMapper; private SysUserMapper userMapper;
@Resource @Resource

View File

@ -174,6 +174,18 @@ logging:
cn.iocoder.yudao.coreservice.modules.system.dal.mysql: debug cn.iocoder.yudao.coreservice.modules.system.dal.mysql: debug
cn.iocoder.yudao.coreservice.modules.tool.dal.mysql: debug cn.iocoder.yudao.coreservice.modules.tool.dal.mysql: debug
--- #################### 微信公众号相关配置 ####################
wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
mp:
# 公众号配置(必填)
app-id: wx041349c6f39b268b
secret: 5abee519483bc9f8cb37ce280e814bd0
# 存储配置,解决 AccessToken 的跨节点的共享
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
--- #################### 芋道相关配置 #################### --- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置 # 芋道配置项,设置当前项目所有自定义的配置

View File

@ -48,11 +48,17 @@ yudao:
info: info:
version: 1.0.0 version: 1.0.0
base-package: cn.iocoder.yudao.adminserver base-package: cn.iocoder.yudao.adminserver
member-package: cn.iocoder.yudao.module.member
core-service: core-service:
base-package: cn.iocoder.yudao.coreservice base-package: cn.iocoder.yudao.coreservice
web: web:
api-prefix: /api admin-api:
controller-package: ${yudao.info.base-package} prefix: /api
controller: ${yudao.info.base-package}
app-api:
prefix: /app-api
controller: cn.iocoder.yudao.module.member.controller.app
swagger: swagger:
title: 管理后台 title: 管理后台
description: 提供管理员管理的所有功能 description: 提供管理员管理的所有功能
@ -72,7 +78,13 @@ yudao:
- cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants - cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants
tenant: # 多租户相关配置项 tenant: # 多租户相关配置项
tables: # 配置需要开启多租户的表;如果实体已经继承 TenantBaseDO 类,则无需重复配置 tables: # 配置需要开启多租户的表;如果实体已经继承 TenantBaseDO 类,则无需重复配置
url: url: ## TODO 芋艿:迁移到 web 配置项下,
admin-ui: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址 admin-ui: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址
sms-code: # 短信验证码相关的配置项
expire-times: 10m
send-frequency: 1m
send-maximum-quantity-per-day: 10
begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
end-code: 9999 # 这里配置 9999 的原因是,测试方便。
debug: false debug: false

View File

@ -9,7 +9,6 @@ import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
*/ */
public interface InfFileCoreService { public interface InfFileCoreService {
/** /**
* 保存文件并返回文件的访问路径 * 保存文件并返回文件的访问路径
* *

View File

@ -2,8 +2,6 @@ package cn.iocoder.yudao.coreservice.modules.system.service.sms.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.coreservice.modules.member.service.user.MbrUserCoreService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.mq.message.sms.SysSmsSendMessage; import cn.iocoder.yudao.coreservice.modules.system.mq.message.sms.SysSmsSendMessage;
@ -43,8 +41,6 @@ public class SysSmsCoreServiceImpl implements SysSmsCoreService {
@Resource @Resource
private SysUserCoreService sysUserCoreService; private SysUserCoreService sysUserCoreService;
@Resource @Resource
private MbrUserCoreService mbrUserCoreService;
@Resource
private SysSmsTemplateCoreService smsTemplateCoreService; private SysSmsTemplateCoreService smsTemplateCoreService;
@Resource @Resource
private SysSmsLogCoreService smsLogCoreService; private SysSmsLogCoreService smsLogCoreService;
@ -72,10 +68,11 @@ public class SysSmsCoreServiceImpl implements SysSmsCoreService {
public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) { public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map<String, Object> templateParams) {
// 如果 mobile 为空则加载用户编号对应的手机号 // 如果 mobile 为空则加载用户编号对应的手机号
if (StrUtil.isEmpty(mobile)) { if (StrUtil.isEmpty(mobile)) {
MbrUserDO user = mbrUserCoreService.getUser(userId); // MbrUserDO user = mbrUserCoreService.getUser(userId);
if (user != null) { // if (user != null) {
mobile = user.getMobile(); // mobile = user.getMobile();
} // }
// TODO 芋艿重构
} }
// 执行发送 // 执行发送
return this.sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); return this.sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);

View File

@ -15,7 +15,8 @@ import org.springframework.context.annotation.Configuration;
* @author 芋道源码 * @author 芋道源码
*/ */
@Configuration @Configuration
@MapperScan(value = {"${yudao.info.base-package}", "${yudao.core-service.base-package}"}, annotationClass = Mapper.class, @MapperScan(value = {"${yudao.info.base-package}", "${yudao.core-service.base-package}", "${yudao.info.member-package}"},
annotationClass = Mapper.class,
lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载目前仅用于单元测试 lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载目前仅用于单元测试
public class YudaoMybatisAutoConfiguration { public class YudaoMybatisAutoConfiguration {

View File

@ -7,6 +7,9 @@ import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;
import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;
import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthServiceImpl;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -20,6 +23,7 @@ import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
/** /**
* Spring Security 自动配置类主要用于相关组件的配置 * Spring Security 自动配置类主要用于相关组件的配置
@ -29,7 +33,7 @@ import javax.annotation.Resource;
* *
* @author 芋道源码 * @author 芋道源码
*/ */
@Configuration @Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SecurityProperties.class) @EnableConfigurationProperties(SecurityProperties.class)
public class YudaoSecurityAutoConfiguration { public class YudaoSecurityAutoConfiguration {
@ -64,8 +68,8 @@ public class YudaoSecurityAutoConfiguration {
* 退出处理类 Bean * 退出处理类 Bean
*/ */
@Bean @Bean
public LogoutSuccessHandler logoutSuccessHandler(SecurityAuthFrameworkService securityFrameworkService) { public LogoutSuccessHandler logoutSuccessHandler(SecurityAuthService securityAuthService) {
return new LogoutSuccessHandlerImpl(securityProperties, securityFrameworkService); return new LogoutSuccessHandlerImpl(securityProperties, securityAuthService);
} }
/** /**
@ -83,9 +87,18 @@ public class YudaoSecurityAutoConfiguration {
* Token 认证过滤器 Bean * Token 认证过滤器 Bean
*/ */
@Bean @Bean
public JWTAuthenticationTokenFilter authenticationTokenFilter(SecurityAuthFrameworkService securityFrameworkService, public JWTAuthenticationTokenFilter authenticationTokenFilter(SecurityAuthService securityAuthService,
GlobalExceptionHandler globalExceptionHandler) { GlobalExceptionHandler globalExceptionHandler) {
return new JWTAuthenticationTokenFilter(securityProperties, securityFrameworkService, globalExceptionHandler); return new JWTAuthenticationTokenFilter(securityProperties, securityAuthService, globalExceptionHandler);
}
/**
* 安全认证的 Service Bean
*/
@Bean
public SecurityAuthService securityAuthService(List<SecurityAuthFrameworkService> securityFrameworkServices,
WebProperties webProperties) {
return new SecurityAuthServiceImpl(securityFrameworkServices, webProperties);
} }
/** /**

View File

@ -65,13 +65,15 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
*/ */
@Resource @Resource
private JWTAuthenticationTokenFilter authenticationTokenFilter; private JWTAuthenticationTokenFilter authenticationTokenFilter;
/** /**
* 自定义的权限映射 Bean * 自定义的权限映射 Bean
* *
* @see #configure(HttpSecurity) * @see #configure(HttpSecurity)
*/ */
@Resource @Resource
private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer; private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry>
authorizeRequestsCustomizer;
/** /**
* 由于 Spring Security 创建 AuthenticationManager 对象时没声明 @Bean 注解导致无法被注入 * 由于 Spring Security 创建 AuthenticationManager 对象时没声明 @Bean 注解导致无法被注入
@ -89,8 +91,8 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
*/ */
@Override @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService) auth
.passwordEncoder(passwordEncoder); .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
} }
/** /**
@ -123,16 +125,16 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
// 一堆自定义的 Spring Security 处理器 // 一堆自定义的 Spring Security 处理器
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler).and() .accessDeniedHandler(accessDeniedHandler).and()
.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出 .logout().logoutUrl(buildAdminApi("/logout")).logoutSuccessHandler(logoutSuccessHandler); // 登出
// 设置每个请求的权限 全局共享规则 // 设置每个请求的权限 全局共享规则
httpSecurity.authorizeRequests() httpSecurity.authorizeRequests()
// 登录的接口可匿名访问 // 登录的接口可匿名访问
.antMatchers(api("/login")).anonymous() .antMatchers(buildAdminApi("/login")).anonymous()
// 静态资源可匿名访问 // 静态资源可匿名访问
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
// 文件的获取接口可匿名访问 // 文件的获取接口可匿名访问
.antMatchers(api("/infra/file/get/**")).anonymous() .antMatchers(buildAdminApi("/infra/file/get/**")).anonymous()
// Swagger 接口文档 // Swagger 接口文档
.antMatchers("/swagger-ui.html").anonymous() .antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous() .antMatchers("/swagger-resources/**").anonymous()
@ -143,11 +145,11 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
.antMatchers("/actuator/**").anonymous() .antMatchers("/actuator/**").anonymous()
// Druid 监控 TODO 芋艿等对接了 druid admin 在调整下 // Druid 监控 TODO 芋艿等对接了 druid admin 在调整下
.antMatchers("/druid/**").anonymous() .antMatchers("/druid/**").anonymous()
// oAuth2 auth2/login/gitee // oAuth2 auth2/login/gitee TODO 芋艿貌似可以删除
.antMatchers(api("/auth2/login/**")).anonymous() .antMatchers(buildAdminApi("/auth2/login/**")).anonymous()
.antMatchers(api("/auth2/authorization/**")).anonymous() .antMatchers(buildAdminApi("/auth2/authorization/**")).anonymous()
.antMatchers("/api/callback/**").anonymous() .antMatchers("/api/callback/**").anonymous()
// 设置每个请求的权限 每个项目的自定义规则 // 设置每个请求的权限 每个项目的自定义规则 TODO 芋艿改造成多个方便每个模块自定义规则
.and().authorizeRequests(authorizeRequestsCustomizer) .and().authorizeRequests(authorizeRequestsCustomizer)
// 设置每个请求的权限 兜底规则必须认证 // 设置每个请求的权限 兜底规则必须认证
.authorizeRequests().anyRequest().authenticated() .authorizeRequests().anyRequest().authenticated()
@ -156,8 +158,14 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
} }
private String api(String url) { private String buildAdminApi(String url) {
return webProperties.getApiPrefix() + url; // TODO 芋艿多模块
return webProperties.getAdminApi().getPrefix() + url;
}
private String buildAppApi(String url) {
// TODO 芋艿多模块
return webProperties.getAppApi().getPrefix() + url;
} }
} }

View File

@ -2,17 +2,15 @@ package cn.iocoder.yudao.framework.security.core.filter;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -30,7 +28,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
private final SecurityProperties securityProperties; private final SecurityProperties securityProperties;
private final SecurityAuthFrameworkService authService; private final SecurityAuthService authService;
private final GlobalExceptionHandler globalExceptionHandler; private final GlobalExceptionHandler globalExceptionHandler;
@ -42,10 +40,10 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
if (StrUtil.isNotEmpty(token)) { if (StrUtil.isNotEmpty(token)) {
try { try {
// 验证 token 有效性 // 验证 token 有效性
LoginUser loginUser = authService.verifyTokenAndRefresh(token); LoginUser loginUser = authService.verifyTokenAndRefresh(request, token);
// 模拟 Login 功能方便日常开发调试 // 模拟 Login 功能方便日常开发调试
if (loginUser == null) { if (loginUser == null) {
loginUser = this.mockLoginUser(token); loginUser = this.mockLoginUser(request, token);
} }
// 设置当前用户 // 设置当前用户
if (loginUser != null) { if (loginUser != null) {
@ -67,10 +65,11 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
* *
* 注意在线上环境下一定要关闭该功能 * 注意在线上环境下一定要关闭该功能
* *
* @param request 请求
* @param token 模拟的 token格式为 {@link SecurityProperties#getTokenSecret()} + 用户编号 * @param token 模拟的 token格式为 {@link SecurityProperties#getTokenSecret()} + 用户编号
* @return 模拟的 LoginUser * @return 模拟的 LoginUser
*/ */
private LoginUser mockLoginUser(String token) { private LoginUser mockLoginUser(HttpServletRequest request, String token) {
if (!securityProperties.getMockEnable()) { if (!securityProperties.getMockEnable()) {
return null; return null;
} }
@ -79,7 +78,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
return null; return null;
} }
Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));
return authService.mockLogin(userId); return authService.mockLogin(request, userId);
} }
} }

View File

@ -2,16 +2,14 @@ package cn.iocoder.yudao.framework.security.core.handler;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -26,14 +24,14 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
private final SecurityProperties securityProperties; private final SecurityProperties securityProperties;
private final SecurityAuthFrameworkService securityFrameworkService; private final SecurityAuthService authService;
@Override @Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// 执行退出 // 执行退出
String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
if (StrUtil.isNotBlank(token)) { if (StrUtil.isNotBlank(token)) {
securityFrameworkService.logout(token); authService.logout(request, token);
} }
// 返回成功 // 返回成功
ServletUtils.writeJSON(response, CommonResult.success(null)); ServletUtils.writeJSON(response, CommonResult.success(null));

View File

@ -1,10 +1,11 @@
package cn.iocoder.yudao.framework.security.core.service; package cn.iocoder.yudao.framework.security.core.service;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.LoginUser;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
/** /**
* Security 框架 Auth Service 接口定义 security 组件需要的功能 * Security 框架 Auth Service 接口定义不同用户类型的 {@link UserTypeEnum} 需要实现的方法
* *
* @author 芋道源码 * @author 芋道源码
*/ */
@ -34,4 +35,11 @@ public interface SecurityAuthFrameworkService extends UserDetailsService {
*/ */
void logout(String token); void logout(String token);
/**
* 获得用户类型每个用户类型对应一个 SecurityAuthFrameworkService 实现类
*
* @return 用户类型
*/
UserTypeEnum getUserType();
} }

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.framework.security.core.service;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import javax.servlet.http.HttpServletRequest;
/**
* 安全认证的 Service 接口 security 组件提供统一的 Auth 相关的方法
* 主要是会基于 {@link HttpServletRequest} 参数匹配对应的 {@link SecurityAuthFrameworkService} 实现然后调用其方法
* 因此在方法的定义上 {@link SecurityAuthFrameworkService} 差不多
*
* @author 芋道源码
*/
public interface SecurityAuthService {
/**
* 校验 token 的有效性并获取用户信息
* 通过后刷新 token 的过期时间
*
* @param request 请求
* @param token token
* @return 用户信息
*/
LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token);
/**
* 模拟指定用户编号的 LoginUser
*
* @param request 请求
* @param userId 用户编号
* @return 登录用户
*/
LoginUser mockLogin(HttpServletRequest request, Long userId);
/**
* 基于 token 退出登录
*
* @param request 请求
* @param token token
*/
void logout(HttpServletRequest request, String token);
}

View File

@ -0,0 +1,64 @@
package cn.iocoder.yudao.framework.security.core.service;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 安全认证的 Service 实现类基于请求地址计算到对应的 {@link UserTypeEnum} 枚举从而拿到对应的 {@link SecurityAuthFrameworkService} 实现
*
* @author 芋道源码
*/
public class SecurityAuthServiceImpl implements SecurityAuthService {
private final Map<UserTypeEnum, SecurityAuthFrameworkService> services = new HashMap<>();
private final WebProperties properties;
public SecurityAuthServiceImpl(List<SecurityAuthFrameworkService> serviceList, WebProperties properties) {
serviceList.forEach(service -> services.put(service.getUserType(), service));
this.properties = properties;
}
@Override
public LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token) {
return selectService(request).verifyTokenAndRefresh(token);
}
@Override
public LoginUser mockLogin(HttpServletRequest request, Long userId) {
return selectService(request).mockLogin(userId);
}
@Override
public void logout(HttpServletRequest request, String token) {
selectService(request).logout(token);
}
private SecurityAuthFrameworkService selectService(HttpServletRequest request) {
// 第一步获得用户类型
UserTypeEnum userType = getUserType(request);
// 第二步获得 SecurityAuthFrameworkService
SecurityAuthFrameworkService service = services.get(userType);
Assert.notNull(service, "URI({}) 用户类型({}) 找不到 SecurityAuthFrameworkService 实现类",
request.getRequestURI(), userType);
return service;
}
private UserTypeEnum getUserType(HttpServletRequest request) {
if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
return UserTypeEnum.ADMIN;
}
if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) {
return UserTypeEnum.MEMBER;
}
throw new IllegalArgumentException(StrUtil.format("URI({}) 找不到匹配的用户类型", request.getRequestURI()));
}
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.apilog.core.filter;
import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
@ -25,6 +26,8 @@ import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.*;
/** /**
* API 访问日志 Filter * API 访问日志 Filter
* *
@ -42,7 +45,8 @@ public class ApiAccessLogFilter extends OncePerRequestFilter {
@Override @Override
protected boolean shouldNotFilter(HttpServletRequest request) { protected boolean shouldNotFilter(HttpServletRequest request) {
// 只过滤 API 请求的地址 // 只过滤 API 请求的地址
return !request.getRequestURI().startsWith(webProperties.getApiPrefix()); return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAppApi().getPrefix(),
webProperties.getAppApi().getPrefix());
} }
@Override @Override
@ -73,7 +77,7 @@ public class ApiAccessLogFilter extends OncePerRequestFilter {
this.buildApiAccessLogDTO(accessLog, request, beginTime, queryString, requestBody, ex); this.buildApiAccessLogDTO(accessLog, request, beginTime, queryString, requestBody, ex);
apiAccessLogFrameworkService.createApiAccessLogAsync(accessLog); apiAccessLogFrameworkService.createApiAccessLogAsync(accessLog);
} catch (Throwable th) { } catch (Throwable th) {
log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), JsonUtils.toJsonString(accessLog), th); log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th);
} }
} }
@ -99,7 +103,7 @@ public class ApiAccessLogFilter extends OncePerRequestFilter {
accessLog.setApplicationName(applicationName); accessLog.setApplicationName(applicationName);
accessLog.setRequestUrl(request.getRequestURI()); accessLog.setRequestUrl(request.getRequestURI());
Map<String, Object> requestParams = MapUtil.<String, Object>builder().put("query", queryString).put("body", requestBody).build(); Map<String, Object> requestParams = MapUtil.<String, Object>builder().put("query", queryString).put("body", requestBody).build();
accessLog.setRequestParams(JsonUtils.toJsonString(requestParams)); accessLog.setRequestParams(toJsonString(requestParams));
accessLog.setRequestMethod(request.getMethod()); accessLog.setRequestMethod(request.getMethod());
accessLog.setUserAgent(ServletUtils.getUserAgent(request)); accessLog.setUserAgent(ServletUtils.getUserAgent(request));
accessLog.setUserIp(ServletUtil.getClientIP(request)); accessLog.setUserIp(ServletUtil.getClientIP(request));

View File

@ -5,6 +5,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ConfigurationProperties(prefix = "yudao.web") @ConfigurationProperties(prefix = "yudao.web")
@ -12,26 +14,37 @@ import javax.validation.constraints.NotNull;
@Data @Data
public class WebProperties { public class WebProperties {
/** @NotNull(message = "APP API 不能为空")
* API 前缀实现所有 Controller 提供的 RESTFul API 的统一前缀 private Api appApi;
* @NotNull(message = "Admin API 不能为空")
* private Api adminApi;
* 意义通过该前缀避免 SwaggerActuator 意外通过 Nginx 暴露出来给外部带来安全性问题
* 这样Nginx 只需要配置转发到 /api/* 的所有接口即可
*
* @see YudaoWebAutoConfiguration#configurePathMatch(PathMatchConfigurer)
*/
@NotNull(message = "API 前缀不能为空")
private String apiPrefix;
/** @Data
* Controller 所在包 @Valid
* public static class Api {
* 主要目的是给该 Controller 设置指定的 {@link #apiPrefix}
* /**
* 因为我们有多个 modules 包里会包含 Controller所以只需要写到 cn.iocoder.yudao 这样的层级 * API 前缀实现所有 Controller 提供的 RESTFul API 的统一前缀
*/ *
@NotNull(message = "Controller 所在包不能为空") *
private String controllerPackage; * 意义通过该前缀避免 SwaggerActuator 意外通过 Nginx 暴露出来给外部带来安全性问题
* 这样Nginx 只需要配置转发到 /api/* 的所有接口即可
*
* @see YudaoWebAutoConfiguration#configurePathMatch(PathMatchConfigurer)
*/
@NotEmpty(message = "API 前缀不能为空")
private String prefix;
/**
* Controller 所在包
*
* 主要目的是给该 Controller 设置指定的 {@link #prefix}
*
* 因为我们有多个 modules 包里会包含 Controller所以只需要写到 cn.iocoder.yudao 这样的层级
*/
@NotEmpty(message = "Controller 所在包不能为空")
private String controller;
}
} }

View File

@ -38,10 +38,19 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
@Override @Override
public void configurePathMatch(PathMatchConfigurer configurer) { public void configurePathMatch(PathMatchConfigurer configurer) {
// 设置 API 前缀仅仅匹配 controller 包下的 configurePathMatch(configurer, webProperties.getAdminApi());
configurer.addPathPrefix(webProperties.getApiPrefix(), clazz -> configurePathMatch(configurer, webProperties.getAppApi());
clazz.isAnnotationPresent(RestController.class) }
&& clazz.getPackage().getName().startsWith(webProperties.getControllerPackage())); // 仅仅匹配 controller
/**
* 设置 API 前缀仅仅匹配 controller 包下的
*
* @param configurer 配置
* @param api API 配置
*/
private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) {
configurer.addPathPrefix(api.getPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class)
&& clazz.getPackage().getName().startsWith(api.getController())); // 仅仅匹配 controller
} }
@Bean @Bean

View File

@ -1,13 +1,13 @@
package cn.iocoder.yudao.module.member.api.user; package cn.iocoder.yudao.module.member.api.user;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
/** /**
* 会员用户的 API 接口 * 会员用户的 API 接口
* *
* @author 芋道源码 * @author 芋道源码
*/ */
public interface MemberUserApi { public interface UserApi {
/** /**
* 获得会员用户信息 * 获得会员用户信息
@ -15,6 +15,6 @@ public interface MemberUserApi {
* @param id 用户编号 * @param id 用户编号
* @return 用户信息 * @return 用户信息
*/ */
MemberUserRespDTO getMemberUser(Long id); UserRespDTO getUser(Long id);
} }

View File

@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
* *
* @author 芋道源码 * @author 芋道源码
*/ */
public class MemberUserRespDTO { public class UserRespDTO {
/** /**
* 用户ID * 用户ID

View File

@ -18,16 +18,18 @@
</description> </description>
<dependencies> <dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-member-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 --> <!-- 业务组件 -->
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-core-service</artifactId> <artifactId>yudao-core-service</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
</dependency>
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-sms</artifactId> <artifactId>yudao-spring-boot-starter-biz-sms</artifactId>
@ -38,11 +40,6 @@
</dependency> </dependency>
<!-- Web 相关 --> <!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-web</artifactId>
</dependency>
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-security</artifactId> <artifactId>yudao-spring-boot-starter-security</artifactId>
@ -59,32 +56,12 @@
<artifactId>yudao-spring-boot-starter-redis</artifactId> <artifactId>yudao-spring-boot-starter-redis</artifactId>
</dependency> </dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-config</artifactId>
</dependency>
<!-- Job 定时任务相关 -->
<!-- 消息队列相关 --> <!-- 消息队列相关 -->
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-mq</artifactId> <artifactId>yudao-spring-boot-starter-mq</artifactId>
</dependency> </dependency>
<!-- 服务保障相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-protection</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-monitor</artifactId>
</dependency>
<!-- Test 测试相关 --> <!-- Test 测试相关 -->
<dependency> <dependency>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.module.member.api;

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.member.api.user;
import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
import cn.iocoder.yudao.module.member.convert.user.UserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.module.member.service.user.UserService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 会员用户的 API 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class UserApiImpl implements UserApi {
@Resource
private UserService userService;
@Override
public UserRespDTO getUser(Long id) {
UserDO user = userService.getUser(id);
return UserConvert.INSTANCE.convert2(user);
}
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.module.member.controller.admin.address;

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.module.member.controller.admin.user;

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.module.member.controller.app.address;

View File

@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; import cn.iocoder.yudao.module.member.service.auth.AuthService;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiImplicitParams;
@ -31,7 +31,8 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
public class AppAuthController { public class AppAuthController {
@Resource @Resource
private SysAuthService authService; private AuthService authService;
@Resource @Resource
private SysSmsCodeService smsCodeService; private SysSmsCodeService smsCodeService;
@Resource @Resource
@ -55,7 +56,7 @@ public class AppAuthController {
@PostMapping("/send-sms-code") @PostMapping("/send-sms-code")
@ApiOperation(value = "发送手机验证码") @ApiOperation(value = "发送手机验证码")
public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) { public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid AppAuthSendSmsReqVO reqVO) {
smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP()); smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP());
return success(true); return success(true);
} }

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.member.controller.app.auth.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -15,6 +15,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern; import javax.validation.constraints.Pattern;
// TODO 芋艿code review 相关逻辑
@ApiModel("APP 端 - 校验验证码 Request VO") @ApiModel("APP 端 - 校验验证码 Request VO")
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -37,4 +38,5 @@ public class AppAuthCheckCodeReqVO {
@NotNull(message = "发送场景不能为空") @NotNull(message = "发送场景不能为空")
@InEnum(SysSmsSceneEnum.class) @InEnum(SysSmsSceneEnum.class)
private Integer scene; private Integer scene;
} }

View File

@ -13,6 +13,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern; import javax.validation.constraints.Pattern;
// TODO 芋艿code review 相关逻辑
@ApiModel("APP 端 - 重置密码 Request VO") @ApiModel("APP 端 - 重置密码 Request VO")
@Data @Data
@NoArgsConstructor @NoArgsConstructor

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.member.controller.app.auth.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull;
@ApiModel("APP 端 - 发送手机验证码 Response VO") @ApiModel("APP 端 - 发送手机验证码 Response VO")
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class AppSendSmsReqVO { public class AppAuthSendSmsReqVO {
@ApiModelProperty(value = "手机号", example = "15601691234") @ApiModelProperty(value = "手机号", example = "15601691234")
@Mobile @Mobile

View File

@ -11,6 +11,7 @@ import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
// TODO 芋艿code review 相关逻辑
@ApiModel("APP 端 - 修改密码 Request VO") @ApiModel("APP 端 - 修改密码 Request VO")
@Data @Data
@NoArgsConstructor @NoArgsConstructor

View File

@ -1,13 +1,12 @@
package cn.iocoder.yudao.module.member.controller.app.user; package cn.iocoder.yudao.module.member.controller.app.user;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.module.member.convert.user.UserConvert;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.module.member.service.user.UserService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -17,13 +16,12 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.FILE_IS_EMPTY; import static cn.iocoder.yudao.module.member.enums.MemberErrorCodeConstants.FILE_IS_EMPTY;
@Api(tags = "APP 端 - 用户个人中心") @Api(tags = "APP 端 - 用户个人中心")
@RestController @RestController
@ -33,39 +31,40 @@ import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConst
public class AppUserController { public class AppUserController {
@Resource @Resource
private MbrUserService userService; private UserService userService;
@PutMapping("/update-nickname") @PutMapping("/update-nickname")
@ApiOperation("修改用户昵称") @ApiOperation("修改用户昵称")
@PreAuthenticated @PreAuthenticated
public CommonResult<Boolean> updateNickname(@RequestParam("nickname") String nickname) { public CommonResult<Boolean> updateUserNickname(@RequestParam("nickname") String nickname) {
userService.updateNickname(getLoginUserId(), nickname); userService.updateUserNickname(getLoginUserId(), nickname);
return success(true); return success(true);
} }
@PutMapping("/update-avatar") @PutMapping("/update-avatar")
@ApiOperation("修改用户头像") @ApiOperation("修改用户头像")
@PreAuthenticated @PreAuthenticated
public CommonResult<String> updateAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException { public CommonResult<String> updateUserAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException {
if (file.isEmpty()) { if (file.isEmpty()) {
throw ServiceExceptionUtil.exception(MbrErrorCodeConstants.FILE_IS_EMPTY); throw exception(FILE_IS_EMPTY);
} }
String avatar = userService.updateAvatar(getLoginUserId(), file.getInputStream()); String avatar = userService.updateUserAvatar(getLoginUserId(), file.getInputStream());
return success(avatar); return success(avatar);
} }
@GetMapping("/get") @GetMapping("/get")
@ApiOperation("获得基本信息") @ApiOperation("获得基本信息")
@PreAuthenticated @PreAuthenticated
public CommonResult<MbrUserInfoRespVO> getUserInfo() { public CommonResult<AppUserInfoRespVO> getUserInfo() {
return success(userService.getUserInfo(getLoginUserId())); UserDO user = userService.getUser(getLoginUserId());
return success(UserConvert.INSTANCE.convert(user));
} }
@PostMapping("/update-mobile") @PostMapping("/update-mobile")
@ApiOperation(value = "修改用户手机") @ApiOperation(value = "修改用户手机")
@PreAuthenticated @PreAuthenticated
public CommonResult<Boolean> updateMobile(@RequestBody @Valid MbrUserUpdateMobileReqVO reqVO) { public CommonResult<Boolean> updateMobile(@RequestBody @Valid AppUserUpdateMobileReqVO reqVO) {
userService.updateMobile(getLoginUserId(), reqVO); userService.updateUserMobile(getLoginUserId(), reqVO);
return success(true); return success(true);
} }

View File

@ -38,6 +38,8 @@ public class AppUserUpdateMobileReqVO {
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
private String oldCode; private String oldCode;
// TODO @芋艿oldMobile 应该不用传递
@ApiModelProperty(value = "原手机号",required = true,example = "15823654487") @ApiModelProperty(value = "原手机号",required = true,example = "15823654487")
@NotBlank(message = "手机号不能为空") @NotBlank(message = "手机号不能为空")
@Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")

View File

@ -1,21 +1,21 @@
package cn.iocoder.yudao.module.member.convert.auth; package cn.iocoder.yudao.module.member.convert.auth;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
@Mapper @Mapper
public interface SysAuthConvert { public interface AuthConvert {
SysAuthConvert INSTANCE = Mappers.getMapper(SysAuthConvert.class); AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
@Mapping(source = "mobile", target = "username") @Mapping(source = "mobile", target = "username")
LoginUser convert0(MbrUserDO bean); LoginUser convert0(UserDO bean);
default LoginUser convert(MbrUserDO bean) { default LoginUser convert(UserDO bean) {
// 目的为了设置 UserTypeEnum.MEMBER.getValue() // 目的为了设置 UserTypeEnum.MEMBER.getValue()
return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue()); return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue());
} }

View File

@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.member.convert.user; package cn.iocoder.yudao.module.member.convert.user;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
@ -9,6 +11,7 @@ public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
AppUserInfoRespVO convert(MemberUserDO bean); AppUserInfoRespVO convert(UserDO bean);
UserRespDTO convert2(UserDO bean);
} }

View File

@ -10,19 +10,19 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Date; import java.util.Date;
/** /**
* 会员中心的用户 DO * 会员用户 DO
* *
* uk_mobile 索引基于 {@link #mobile} 字段 * uk_mobile 索引基于 {@link #mobile} 字段
* *
* @author 芋道源码 * @author 芋道源码
*/ */
@TableName(value = "member_user", autoResultMap = true) @TableName(value = "mbr_user", autoResultMap = true)
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class MemberUserDO extends TenantBaseDO { public class UserDO extends TenantBaseDO {
/** /**
* 用户ID * 用户ID

View File

@ -3,10 +3,9 @@ package cn.iocoder.yudao.module.member.dal.mysql.sms;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO; import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
// TODO @芋艿 // TODO @芋艿拿到 system 模块下
@Mapper @Mapper
public interface SysSmsCodeMapper extends BaseMapperX<SysSmsCodeDO> { public interface SysSmsCodeMapper extends BaseMapperX<SysSmsCodeDO> {

View File

@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.member.dal.mysql.user;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* MbrUserDO Mapper
*
* @author 芋道源码
*/
@Mapper
public interface MbrUserMapper extends BaseMapperX<MbrUserDO> {
default MbrUserDO selectByMobile(String mobile) {
return selectOne(MbrUserDO::getMobile, mobile);
}
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.member.dal.mysql.user;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 会员 User Mapper
*
* @author 芋道源码
*/
@Mapper
public interface UserMapper extends BaseMapperX<UserDO> {
default UserDO selectByMobile(String mobile) {
return selectOne(UserDO::getMobile, mobile);
}
}

View File

@ -3,5 +3,7 @@
* 1. data object数据对象 * 1. data object数据对象
* 2. redisRedis CRUD 操作 * 2. redisRedis CRUD 操作
* 3. mysqlMySQL CRUD 操作 * 3. mysqlMySQL CRUD 操作
*
* 其中MySQL 的表以 mbr_ 作为前缀
*/ */
package cn.iocoder.yudao.module.member.dal; package cn.iocoder.yudao.module.member.dal;

View File

@ -1 +1,4 @@
/**
* 占位后续有类后可以删除避免 package 无法提交到 Git
*/
package cn.iocoder.yudao.module.member.dal.redis; package cn.iocoder.yudao.module.member.dal.redis;

View File

@ -1,29 +0,0 @@
package cn.iocoder.yudao.module.member.framework.security;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import javax.annotation.Resource;
@Configuration
public class SecurityConfiguration {
@Resource
private WebProperties webProperties;
@Bean
public Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer() {
return registry -> {
registry.antMatchers(api("/**")).permitAll(); // 默认 API 都是用户可访问
};
}
private String api(String url) {
return webProperties.getApiPrefix() + url;
}
}

View File

@ -6,13 +6,13 @@ import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
import javax.validation.Valid; import javax.validation.Valid;
/** /**
* 用户前台的认证 Service 接口 * 会员的认证 Service 接口
* *
* 提供用户的账号密码登录token 的校验等认证相关的功能 * 提供用户的账号密码登录token 的校验等认证相关的功能
* *
* @author 芋道源码 * @author 芋道源码
*/ */
public interface SysAuthService extends SecurityAuthFrameworkService { public interface AuthService extends SecurityAuthFrameworkService {
/** /**
* 手机 + 密码登录 * 手机 + 密码登录
@ -22,7 +22,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
* @param userAgent 用户 UA * @param userAgent 用户 UA
* @return 身份令牌使用 JWT 方式 * @return 身份令牌使用 JWT 方式
*/ */
String login(@Valid SysAuthLoginReqVO reqVO, String userIp, String userAgent); String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent);
/** /**
* 手机 + 验证码登陆 * 手机 + 验证码登陆
@ -32,7 +32,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
* @param userAgent 用户 UA * @param userAgent 用户 UA
* @return 身份令牌使用 JWT 方式 * @return 身份令牌使用 JWT 方式
*/ */
String smsLogin(@Valid SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent); String smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent);
/** /**
@ -43,7 +43,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
* @param userAgent 用户 UA * @param userAgent 用户 UA
* @return 身份令牌使用 JWT 方式 * @return 身份令牌使用 JWT 方式
*/ */
String socialLogin(@Valid MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent); String socialLogin(@Valid AppAuthSocialLoginReqVO reqVO, String userIp, String userAgent);
/** /**
* 社交登录使用 手机号 + 手机验证码 * 社交登录使用 手机号 + 手机验证码
@ -53,7 +53,7 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
* @param userAgent 用户 UA * @param userAgent 用户 UA
* @return 身份令牌使用 JWT 方式 * @return 身份令牌使用 JWT 方式
*/ */
String socialLogin2(@Valid MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent); String socialLogin2(@Valid AppAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent);
/** /**
* 社交绑定使用 code 授权码 * 社交绑定使用 code 授权码
@ -61,18 +61,19 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
* @param userId 用户编号 * @param userId 用户编号
* @param reqVO 绑定信息 * @param reqVO 绑定信息
*/ */
void socialBind(Long userId, @Valid MbrAuthSocialBindReqVO reqVO); void socialBind(Long userId, @Valid AppAuthSocialBindReqVO reqVO);
/** /**
* 修改用户密码 * 修改用户密码
* @param userId 用户id * @param userId 用户id
* @param userReqVO 用户请求实体类 * @param userReqVO 用户请求实体类
*/ */
void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO userReqVO); void updatePassword(Long userId, AppAuthUpdatePasswordReqVO userReqVO);
/** /**
* 忘记密码 * 忘记密码
* @param userReqVO 用户请求实体类 * @param userReqVO 用户请求实体类
*/ */
void resetPassword(MbrAuthResetPasswordReqVO userReqVO); void resetPassword(AppAuthResetPasswordReqVO userReqVO);
} }

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.member.service.auth;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum; import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum;
import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum; import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum;
@ -12,22 +11,20 @@ import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLo
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert; import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum;
import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService;
import cn.iocoder.yudao.module.member.service.user.UserService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.model.AuthUser;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.DisabledException;
@ -46,25 +43,23 @@ import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*; import static cn.iocoder.yudao.module.member.enums.SysErrorCodeConstants.*;
/** /**
* Auth Service 实现类 * 会员的认证 Service 接口
* *
* @author 芋道源码 * @author 芋道源码
*/ */
@Service @Service
@Slf4j @Slf4j
public class SysAuthServiceImpl implements SysAuthService { public class AuthServiceImpl implements AuthService {
private static final UserTypeEnum USER_TYPE_ENUM = UserTypeEnum.MEMBER;
@Resource @Resource
@Lazy // 延迟加载因为存在相互依赖的问题 @Lazy // 延迟加载因为存在相互依赖的问题
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
@Resource @Resource
private MbrUserService userService; private UserService userService;
@Resource @Resource
private SysSmsCodeService smsCodeService; private SysSmsCodeService smsCodeService;
@Resource @Resource
@ -74,28 +69,24 @@ public class SysAuthServiceImpl implements SysAuthService {
@Resource @Resource
private SysSocialCoreService socialService; private SysSocialCoreService socialService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource @Resource
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Resource @Resource
private MbrUserMapper userMapper; private UserMapper userMapper;
private static final UserTypeEnum userTypeEnum = UserTypeEnum.MEMBER;
@Override @Override
public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
// 获取 username 对应的 SysUserDO // 获取 username 对应的 SysUserDO
MbrUserDO user = userService.getUserByMobile(mobile); UserDO user = userService.getUserByMobile(mobile);
if (user == null) { if (user == null) {
throw new UsernameNotFoundException(mobile); throw new UsernameNotFoundException(mobile);
} }
// 创建 LoginUser 对象 // 创建 LoginUser 对象
return SysAuthConvert.INSTANCE.convert(user); return AuthConvert.INSTANCE.convert(user);
} }
@Override @Override
public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) { public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) {
// 使用手机 + 密码进行登录 // 使用手机 + 密码进行登录
LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword()); LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword());
@ -105,80 +96,77 @@ public class SysAuthServiceImpl implements SysAuthService {
@Override @Override
@Transactional @Transactional
public String smsLogin(SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent) { public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
// 校验验证码 // 校验验证码
smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.LOGIN_BY_SMS.getScene(), smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.LOGIN_BY_SMS.getScene(),
reqVO.getCode(), userIp); reqVO.getCode(), userIp);
// 获得获得注册用户 // 获得获得注册用户
MbrUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp); UserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp);
Assert.notNull(user, "获取用户失败,结果为空"); Assert.notNull(user, "获取用户失败,结果为空");
// 执行登陆 // 执行登陆
this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SMS, SysLoginResultEnum.SUCCESS); this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SMS, SysLoginResultEnum.SUCCESS);
LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user); LoginUser loginUser = AuthConvert.INSTANCE.convert(user);
// 缓存登录用户到 Redis 返回 sessionId 编号 // 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
} }
@Override @Override
public String socialLogin(MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent) { public String socialLogin(AppAuthSocialLoginReqVO reqVO, String userIp, String userAgent) {
// 使用 code 授权码进行登录 // 使用 code 授权码进行登录
AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
// 如果未绑定 SysSocialUserDO 用户则无法自动登录进行报错 // 如果未绑定 SysSocialUserDO 用户则无法自动登录进行报错
String unionId = socialService.getAuthUserUnionId(authUser); String unionId = socialService.getAuthUserUnionId(authUser);
List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, USER_TYPE_ENUM); List<SysSocialUserDO> socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, getUserType());
if (CollUtil.isEmpty(socialUsers)) { if (CollUtil.isEmpty(socialUsers)) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.AUTH_THIRD_LOGIN_NOT_BIND); throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
} }
// 自动登录 // 自动登录
MbrUserDO user = userService.getUser(socialUsers.get(0).getUserId()); UserDO user = userService.getUser(socialUsers.get(0).getUserId());
if (user == null) { if (user == null) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }
this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SOCIAL, SysLoginResultEnum.SUCCESS); this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SOCIAL, SysLoginResultEnum.SUCCESS);
// 创建 LoginUser 对象 // 创建 LoginUser 对象
LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user); LoginUser loginUser = AuthConvert.INSTANCE.convert(user);
// 绑定社交用户更新 // 绑定社交用户更新
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType());
// 缓存登录用户到 Redis 返回 sessionId 编号 // 缓存登录用户到 Redis 返回 sessionId 编号
return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
} }
@Override @Override
public String socialLogin2(MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) { public String socialLogin2(AppAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) {
AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
// 使用手机号手机验证码登录 // 使用手机号手机验证码登录
SysAuthSmsLoginReqVO loginReqVO = SysAuthSmsLoginReqVO AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder()
.builder() .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build();
.mobile(reqVO.getMobile())
.code(reqVO.getSmsCode())
.build();
String sessionId = this.smsLogin(loginReqVO, userIp, userAgent); String sessionId = this.smsLogin(loginReqVO, userIp, userAgent);
LoginUser loginUser = userSessionCoreService.getLoginUser(sessionId); LoginUser loginUser = userSessionCoreService.getLoginUser(sessionId);
// 绑定社交用户新增 // 绑定社交用户新增
socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, USER_TYPE_ENUM); socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, getUserType());
return sessionId; return sessionId;
} }
@Override @Override
public void socialBind(Long userId, MbrAuthSocialBindReqVO reqVO) { public void socialBind(Long userId, AppAuthSocialBindReqVO reqVO) {
// 使用 code 授权码进行登录 // 使用 code 授权码进行登录
AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); org.springframework.util.Assert.notNull(authUser, "授权用户不为空");
// 绑定社交用户新增 // 绑定社交用户新增
socialService.bindSocialUser(userId, reqVO.getType(), authUser, USER_TYPE_ENUM); socialService.bindSocialUser(userId, reqVO.getType(), authUser, getUserType());
} }
private LoginUser login0(String username, String password) { private LoginUser login0(String username, String password) {
@ -191,14 +179,14 @@ public class SysAuthServiceImpl implements SysAuthService {
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (BadCredentialsException badCredentialsException) { } catch (BadCredentialsException badCredentialsException) {
this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS); this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS); throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
} catch (DisabledException disabledException) { } catch (DisabledException disabledException) {
this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.USER_DISABLED); this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.USER_DISABLED);
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.AUTH_LOGIN_USER_DISABLED); throw exception(AUTH_LOGIN_USER_DISABLED);
} catch (AuthenticationException authenticationException) { } catch (AuthenticationException authenticationException) {
log.error("[login0][username({}) 发生未知异常]", username, authenticationException); log.error("[login0][username({}) 发生未知异常]", username, authenticationException);
this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.UNKNOWN_ERROR); this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.UNKNOWN_ERROR);
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.AUTH_LOGIN_FAIL_UNKNOWN); throw exception(AUTH_LOGIN_FAIL_UNKNOWN);
} }
// 登录成功的日志 // 登录成功的日志
Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); Assert.notNull(authentication.getPrincipal(), "Principal 不会为空");
@ -208,7 +196,7 @@ public class SysAuthServiceImpl implements SysAuthService {
private void createLoginLog(String mobile, SysLoginLogTypeEnum logTypeEnum, SysLoginResultEnum loginResult) { private void createLoginLog(String mobile, SysLoginLogTypeEnum logTypeEnum, SysLoginResultEnum loginResult) {
// 获得用户 // 获得用户
MbrUserDO user = userService.getUserByMobile(mobile); UserDO user = userService.getUserByMobile(mobile);
// 插入登录日志 // 插入登录日志
SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
reqDTO.setLogType(logTypeEnum.getType()); reqDTO.setLogType(logTypeEnum.getType());
@ -246,10 +234,11 @@ public class SysAuthServiceImpl implements SysAuthService {
return; return;
} }
// 重新加载 MbrUserDO 信息 // 重新加载 UserDO 信息
MbrUserDO user = userService.getUser(loginUser.getId()); UserDO user = userService.getUser(loginUser.getId());
if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.AUTH_TOKEN_EXPIRED); // 校验 token 用户被禁用的情况下也认为 token 过期方便前端跳转到登录界面 // 校验 token 用户被禁用的情况下也认为 token 过期方便前端跳转到登录界面
throw exception(AUTH_TOKEN_EXPIRED);
} }
// 刷新 LoginUser 缓存 // 刷新 LoginUser 缓存
@ -258,8 +247,8 @@ public class SysAuthServiceImpl implements SysAuthService {
@Override @Override
public LoginUser mockLogin(Long userId) { public LoginUser mockLogin(Long userId) {
// 获取用户编号对应的 MbrUserDO // 获取用户编号对应的 UserDO
MbrUserDO user = userService.getUser(userId); UserDO user = userService.getUser(userId);
if (user == null) { if (user == null) {
throw new UsernameNotFoundException(String.valueOf(userId)); throw new UsernameNotFoundException(String.valueOf(userId));
} }
@ -268,7 +257,7 @@ public class SysAuthServiceImpl implements SysAuthService {
this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_MOCK, SysLoginResultEnum.SUCCESS); this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_MOCK, SysLoginResultEnum.SUCCESS);
// 创建 LoginUser 对象 // 创建 LoginUser 对象
return SysAuthConvert.INSTANCE.convert(user); return AuthConvert.INSTANCE.convert(user);
} }
@Override @Override
@ -285,27 +274,32 @@ public class SysAuthServiceImpl implements SysAuthService {
} }
@Override @Override
public void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO reqVO) { public UserTypeEnum getUserType() {
return UserTypeEnum.MEMBER;
}
@Override
public void updatePassword(Long userId, AppAuthUpdatePasswordReqVO reqVO) {
// 检验旧密码 // 检验旧密码
MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword()); UserDO userDO = checkOldPassword(userId, reqVO.getOldPassword());
// 更新用户密码 // 更新用户密码
MbrUserDO mbrUserDO = MbrUserDO.builder().build(); UserDO mbrUserDO = UserDO.builder().id(userDO.getId())
mbrUserDO.setId(userDO.getId()); .password(passwordEncoder.encode(reqVO.getPassword())).build();
mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
userMapper.updateById(mbrUserDO); userMapper.updateById(mbrUserDO);
} }
@Override @Override
public void resetPassword(MbrAuthResetPasswordReqVO reqVO) { public void resetPassword(AppAuthResetPasswordReqVO reqVO) {
// 检验用户是否存在 // 检验用户是否存在
MbrUserDO userDO = checkUserIfExists(reqVO.getMobile()); UserDO userDO = checkUserIfExists(reqVO.getMobile());
// 使用验证码 // 使用验证码
smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(),reqVO.getCode(),getClientIP()); smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(),
getClientIP());
// 更新密码 // 更新密码
MbrUserDO mbrUserDO = MbrUserDO.builder().build(); UserDO mbrUserDO = UserDO.builder().build();
mbrUserDO.setId(userDO.getId()); mbrUserDO.setId(userDO.getId());
mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword())); mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
userMapper.updateById(mbrUserDO); userMapper.updateById(mbrUserDO);
@ -316,36 +310,35 @@ public class SysAuthServiceImpl implements SysAuthService {
* *
* @param id 用户 id * @param id 用户 id
* @param oldPassword 旧密码 * @param oldPassword 旧密码
* @return MbrUserDO 用户实体 * @return MemberUserDO 用户实体
*/ */
@VisibleForTesting @VisibleForTesting
public MbrUserDO checkOldPassword(Long id, String oldPassword) { public UserDO checkOldPassword(Long id, String oldPassword) {
MbrUserDO user = userMapper.selectById(id); UserDO user = userMapper.selectById(id);
if (user == null) { if (user == null) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }
// 参数未加密密码编码后的密码 // 参数未加密密码编码后的密码
if (!passwordEncoder.matches(oldPassword,user.getPassword())) { if (!passwordEncoder.matches(oldPassword,user.getPassword())) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_PASSWORD_FAILED); throw exception(USER_PASSWORD_FAILED);
} }
return user; return user;
} }
public MbrUserDO checkUserIfExists(String mobile) { public UserDO checkUserIfExists(String mobile) {
MbrUserDO user = userMapper.selectByMobile(mobile); UserDO user = userMapper.selectByMobile(mobile);
if (user == null) { if (user == null) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
} }
return user; return user;
} }
private void createLogoutLog(Long userId, String username) { private void createLogoutLog(Long userId, String username) {
SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setTraceId(TracerUtils.getTraceId());
reqDTO.setUserId(userId); reqDTO.setUserId(userId);
reqDTO.setUserType(USER_TYPE_ENUM.getValue()); reqDTO.setUserType(getUserType().getValue());
reqDTO.setUsername(username); reqDTO.setUsername(username);
reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserAgent(ServletUtils.getUserAgent());
reqDTO.setUserIp(getClientIP()); reqDTO.setUserIp(getClientIP());

View File

@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.member.service.sms;
import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum;
/** /**
* 短信验证码 Service 接口 * 短信验证码 Service 接口
@ -40,7 +40,6 @@ public interface SysSmsCodeService {
*/ */
void sendSmsCodeLogin(Long userId); void sendSmsCodeLogin(Long userId);
/** /**
* 检查验证码是否有效 * 检查验证码是否有效
* @param mobile 手机 * @param mobile 手机

View File

@ -1,15 +1,14 @@
package cn.iocoder.yudao.module.member.service.sms; package cn.iocoder.yudao.module.member.service.sms;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper; import cn.iocoder.yudao.module.member.dal.mysql.sms.SysSmsCodeMapper;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum;
import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties; import cn.iocoder.yudao.module.member.framework.sms.SmsCodeProperties;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; import cn.iocoder.yudao.module.member.service.user.UserService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -17,9 +16,8 @@ import javax.annotation.Resource;
import java.util.Date; import java.util.Date;
import static cn.hutool.core.util.RandomUtil.randomInt; import static cn.hutool.core.util.RandomUtil.randomInt;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*; import static cn.iocoder.yudao.module.member.enums.SysErrorCodeConstants.*;
/** /**
* 短信验证码 Service 实现类 * 短信验证码 Service 实现类
@ -37,7 +35,7 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
private SysSmsCodeMapper smsCodeMapper; private SysSmsCodeMapper smsCodeMapper;
@Resource @Resource
private MbrUserService mbrUserService; private UserService userService;
@Resource @Resource
private SysSmsCoreService smsCoreService; private SysSmsCoreService smsCoreService;
@ -62,9 +60,9 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
public void checkMobileIsRegister(String mobile, Integer scene) { public void checkMobileIsRegister(String mobile, Integer scene) {
// 检测手机号是否已被使用 // 检测手机号是否已被使用
MbrUserDO userByMobile = mbrUserService.getUserByMobile(mobile); UserDO user = userService.getUserByMobile(mobile);
if (userByMobile != null){ if (user != null) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_IS_EXISTS); throw ServiceExceptionUtil.exception(USER_SMS_CODE_IS_EXISTS);
} }
// 发送短信 // 发送短信
@ -76,11 +74,11 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null); SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null);
if (lastSmsCode != null) { if (lastSmsCode != null) {
if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限 if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); throw ServiceExceptionUtil.exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
} }
if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
< smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_SEND_TOO_FAST); throw ServiceExceptionUtil.exception(USER_SMS_CODE_SEND_TOO_FAST);
} }
// TODO 芋艿提升每个 IP 每天可发送数量 // TODO 芋艿提升每个 IP 每天可发送数量
// TODO 芋艿提升每个 IP 每小时可发送数量 // TODO 芋艿提升每个 IP 每小时可发送数量
@ -97,13 +95,12 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
@Override @Override
public void useSmsCode(String mobile, Integer scene, String code, String usedIp) { public void useSmsCode(String mobile, Integer scene, String code, String usedIp) {
// 检测验证码是否有效 // 检测验证码是否有效
SysSmsCodeDO lastSmsCode = this.checkCodeIsExpired(mobile, code, scene); SysSmsCodeDO lastSmsCode = this.checkCodeIsExpired(mobile, code, scene);
// 判断验证码是否已被使用 // 判断验证码是否已被使用
if (Boolean.TRUE.equals(lastSmsCode.getUsed())) { if (Boolean.TRUE.equals(lastSmsCode.getUsed())) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_USED); throw ServiceExceptionUtil.exception(USER_SMS_CODE_USED);
} }
// 使用验证码 // 使用验证码
@ -113,9 +110,9 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
@Override @Override
public void sendSmsCodeLogin(Long userId) { public void sendSmsCodeLogin(Long userId) {
MbrUserDO user = mbrUserService.getUser(userId); UserDO user = userService.getUser(userId);
if (user == null){ if (user == null){
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_NOT_EXISTS); throw ServiceExceptionUtil.exception(USER_NOT_EXISTS);
} }
// 发送验证码 // 发送验证码
this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP()); this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP());
@ -128,15 +125,13 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
// 若验证码不存在抛出异常 // 若验证码不存在抛出异常
if (lastSmsCode == null) { if (lastSmsCode == null) {
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_NOT_FOUND); throw ServiceExceptionUtil.exception(USER_SMS_CODE_NOT_FOUND);
} }
if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
>= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_EXPIRED); throw ServiceExceptionUtil.exception(USER_SMS_CODE_EXPIRED);
} }
return lastSmsCode; return lastSmsCode;
} }
} }

View File

@ -1,18 +1,18 @@
package cn.iocoder.yudao.module.member.service.user; package cn.iocoder.yudao.module.member.service.user;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import java.io.InputStream; import java.io.InputStream;
/** /**
* 前台用户 Service 接口 * 会员用户 Service 接口
* *
* @author 芋道源码 * @author 芋道源码
*/ */
public interface MbrUserService { public interface UserService {
/** /**
* 通过手机查询用户 * 通过手机查询用户
@ -20,7 +20,7 @@ public interface MbrUserService {
* @param mobile 手机 * @param mobile 手机
* @return 用户对象 * @return 用户对象
*/ */
MbrUserDO getUserByMobile(String mobile); UserDO getUserByMobile(String mobile);
/** /**
* 基于手机号创建用户 * 基于手机号创建用户
@ -30,7 +30,7 @@ public interface MbrUserService {
* @param registerIp 注册 IP * @param registerIp 注册 IP
* @return 用户对象 * @return 用户对象
*/ */
MbrUserDO createUserIfAbsent(@Mobile String mobile, String registerIp); UserDO createUserIfAbsent(@Mobile String mobile, String registerIp);
/** /**
* 更新用户的最后登陆信息 * 更新用户的最后登陆信息
@ -46,14 +46,14 @@ public interface MbrUserService {
* @param id 用户ID * @param id 用户ID
* @return 用户对象信息 * @return 用户对象信息
*/ */
MbrUserDO getUser(Long id); UserDO getUser(Long id);
/** /**
* 修改用户昵称 * 修改用户昵称
* @param userId 用户id * @param userId 用户id
* @param nickname 用户新昵称 * @param nickname 用户新昵称
*/ */
void updateNickname(Long userId, String nickname); void updateUserNickname(Long userId, String nickname);
/** /**
* 修改用户头像 * 修改用户头像
@ -61,21 +61,13 @@ public interface MbrUserService {
* @param inputStream 头像文件 * @param inputStream 头像文件
* @return 头像url * @return 头像url
*/ */
String updateAvatar(Long userId, InputStream inputStream); String updateUserAvatar(Long userId, InputStream inputStream);
/**
* 根据用户id获取用户头像与昵称
*
* @param userId 用户id
* @return 用户响应实体类
*/
MbrUserInfoRespVO getUserInfo(Long userId);
/** /**
* 修改手机 * 修改手机
* @param userId 用户id * @param userId 用户id
* @param reqVO 请求实体 * @param reqVO 请求实体
*/ */
void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO); void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO);
} }

View File

@ -3,17 +3,15 @@ package cn.iocoder.yudao.module.member.service.user;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService; import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO;
import cn.iocoder.yudao.userserver.modules.member.convert.user.UserProfileConvert; import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; import cn.iocoder.yudao.module.member.enums.SysErrorCodeConstants;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum;
import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@ -27,41 +25,38 @@ import java.util.Date;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.USER_NOT_EXISTS; import static cn.iocoder.yudao.module.member.enums.MemberErrorCodeConstants.USER_NOT_EXISTS;
import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.USER_SMS_CODE_IS_UNUSED;
/** /**
* User Service 实现类 * 会员 User Service 实现类
* *
* @author 芋道源码 * @author 芋道源码
*/ */
@Service @Service
@Valid @Valid
@Slf4j @Slf4j
public class MbrUserServiceImpl implements MbrUserService { public class UserServiceImpl implements UserService {
@Resource @Resource
private MbrUserMapper userMapper; private UserMapper memberUserMapper;
@Resource @Resource
private InfFileCoreService fileCoreService; private InfFileCoreService fileCoreService;
@Resource
private SysSmsCodeService smsCodeService;
@Resource @Resource
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Resource
private SysSmsCodeService smsCodeService;
@Override @Override
public MbrUserDO getUserByMobile(String mobile) { public UserDO getUserByMobile(String mobile) {
return userMapper.selectByMobile(mobile); return memberUserMapper.selectByMobile(mobile);
} }
@Override @Override
public MbrUserDO createUserIfAbsent(String mobile, String registerIp) { public UserDO createUserIfAbsent(String mobile, String registerIp) {
// 用户已经存在 // 用户已经存在
MbrUserDO user = userMapper.selectByMobile(mobile); UserDO user = memberUserMapper.selectByMobile(mobile);
if (user != null) { if (user != null) {
return user; return user;
} }
@ -69,94 +64,84 @@ public class MbrUserServiceImpl implements MbrUserService {
return this.createUser(mobile, registerIp); return this.createUser(mobile, registerIp);
} }
private MbrUserDO createUser(String mobile, String registerIp) { private UserDO createUser(String mobile, String registerIp) {
// 生成密码 // 生成密码
String password = IdUtil.fastSimpleUUID(); String password = IdUtil.fastSimpleUUID();
// 插入用户 // 插入用户
MbrUserDO user = new MbrUserDO(); UserDO user = new UserDO();
user.setMobile(mobile); user.setMobile(mobile);
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
user.setPassword(passwordEncoder.encode(password)); // 加密密码 user.setPassword(passwordEncoder.encode(password)); // 加密密码
user.setRegisterIp(registerIp); user.setRegisterIp(registerIp);
userMapper.insert(user); memberUserMapper.insert(user);
return user; return user;
} }
@Override @Override
public void updateUserLogin(Long id, String loginIp) { public void updateUserLogin(Long id, String loginIp) {
userMapper.updateById(new MbrUserDO().setId(id).setLoginIp(loginIp).setLoginDate(new Date())); memberUserMapper.updateById(new UserDO().setId(id)
.setLoginIp(loginIp).setLoginDate(new Date()));
} }
@Override @Override
public MbrUserDO getUser(Long id) { public UserDO getUser(Long id) {
return userMapper.selectById(id); return memberUserMapper.selectById(id);
} }
@Override @Override
public void updateNickname(Long userId, String nickname) { public void updateUserNickname(Long userId, String nickname) {
MbrUserDO user = this.checkUserExists(userId); UserDO user = this.checkUserExists(userId);
// 仅当新昵称不等于旧昵称时进行修改 // 仅当新昵称不等于旧昵称时进行修改
if (nickname.equals(user.getNickname())){ if (nickname.equals(user.getNickname())){
return; return;
} }
MbrUserDO userDO = new MbrUserDO(); UserDO userDO = new UserDO();
userDO.setId(user.getId()); userDO.setId(user.getId());
userDO.setNickname(nickname); userDO.setNickname(nickname);
userMapper.updateById(userDO); memberUserMapper.updateById(userDO);
} }
@Override @Override
public String updateAvatar(Long userId, InputStream avatarFile) { public String updateUserAvatar(Long userId, InputStream avatarFile) {
this.checkUserExists(userId); this.checkUserExists(userId);
// 创建文件 // 创建文件
String avatar = fileCoreService.createFile(IdUtil.fastUUID(), IoUtil.readBytes(avatarFile)); String avatar = fileCoreService.createFile(IdUtil.fastUUID(), IoUtil.readBytes(avatarFile));
// 更新头像路径 // 更新头像路径
MbrUserDO userDO = MbrUserDO.builder() memberUserMapper.updateById(UserDO.builder().id(userId).avatar(avatar).build());
.id(userId)
.avatar(avatar)
.build();
userMapper.updateById(userDO);
return avatar; return avatar;
} }
@Override
public MbrUserInfoRespVO getUserInfo(Long userId) {
MbrUserDO user = this.checkUserExists(userId);
// 拼接返回结果
return UserProfileConvert.INSTANCE.convert(user);
}
@Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) { public void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO) {
// 检测用户是否存在 // 检测用户是否存在
checkUserExists(userId); checkUserExists(userId);
// 校验旧手机和旧验证码 // 校验旧手机和旧验证码
SysSmsCodeDO sysSmsCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getOldMobile(), reqVO.getOldCode(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene()); SysSmsCodeDO sysSmsCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getOldMobile(), reqVO.getOldCode(),
// 判断旧code是否未被使用如果是抛出异常 SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
// 判断旧 code 是否未被使用如果是抛出异常
if (Boolean.FALSE.equals(sysSmsCodeDO.getUsed())){ if (Boolean.FALSE.equals(sysSmsCodeDO.getUsed())){
throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_IS_UNUSED); throw ServiceExceptionUtil.exception(SysErrorCodeConstants.USER_SMS_CODE_IS_UNUSED);
} }
// 使用新验证码 // 使用新验证码
smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP()); smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(),
reqVO.getCode(),getClientIP());
// 更新用户手机 // 更新用户手机
MbrUserDO userDO = MbrUserDO.builder().id(userId).mobile(reqVO.getMobile()).build(); memberUserMapper.updateById(UserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
userMapper.updateById(userDO);
} }
@VisibleForTesting @VisibleForTesting
public MbrUserDO checkUserExists(Long id) { public UserDO checkUserExists(Long id) {
if (id == null) { if (id == null) {
return null; return null;
} }
MbrUserDO user = userMapper.selectById(id); UserDO user = memberUserMapper.selectById(id);
if (user == null) { if (user == null) {
throw exception(USER_NOT_EXISTS); throw exception(USER_NOT_EXISTS);
}else{
return user;
} }
return user;
} }
} }

View File

@ -1,20 +1,18 @@
package cn.iocoder.yudao.module.member.service.auth; package cn.iocoder.yudao.module.member.service.auth;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest; import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthResetPasswordReqVO;
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthUpdatePasswordReqVO;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.module.member.controller.app.auth.vo.MbrAuthResetPasswordReqVO; import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper;
import cn.iocoder.yudao.module.member.controller.app.auth.vo.MbrAuthUpdatePasswordReqVO; import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService;
import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; import cn.iocoder.yudao.module.member.service.user.UserService;
import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl; import cn.iocoder.yudao.module.member.test.BaseDbAndRedisUnitTest;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -34,17 +32,17 @@ import static org.mockito.Mockito.when;
// TODO @芋艿单测的 review等逻辑都达成一致后 // TODO @芋艿单测的 review等逻辑都达成一致后
/** /**
* {@link SysAuthService} 的单元测试类 * {@link AuthService} 的单元测试类
* *
* @author 宋天 * @author 宋天
*/ */
@Import({SysAuthServiceImpl.class, YudaoRedisAutoConfiguration.class}) @Import({AuthServiceImpl.class, YudaoRedisAutoConfiguration.class})
public class SysAuthServiceTest extends BaseDbAndRedisUnitTest { public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
@MockBean @MockBean
private AuthenticationManager authenticationManager; private AuthenticationManager authenticationManager;
@MockBean @MockBean
private MbrUserService userService; private UserService userService;
@MockBean @MockBean
private SysSmsCodeService smsCodeService; private SysSmsCodeService smsCodeService;
@MockBean @MockBean
@ -58,21 +56,21 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
@MockBean @MockBean
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Resource @Resource
private MbrUserMapper mbrUserMapper; private UserMapper mbrUserMapper;
@Resource @Resource
private SysAuthServiceImpl authService; private AuthServiceImpl authService;
@Test @Test
public void testUpdatePassword_success(){ public void testUpdatePassword_success(){
// 准备参数 // 准备参数
MbrUserDO userDO = randomMbrUserDO(); UserDO userDO = randomUserDO();
mbrUserMapper.insert(userDO); mbrUserMapper.insert(userDO);
// 新密码 // 新密码
String newPassword = randomString(); String newPassword = randomString();
// 请求实体 // 请求实体
MbrAuthUpdatePasswordReqVO reqVO = MbrAuthUpdatePasswordReqVO.builder() AppAuthUpdatePasswordReqVO reqVO = AppAuthUpdatePasswordReqVO.builder()
.oldPassword(userDO.getPassword()) .oldPassword(userDO.getPassword())
.password(newPassword) .password(newPassword)
.build(); .build();
@ -83,14 +81,14 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
when(passwordEncoder.encode(newPassword)).thenReturn(newPassword); when(passwordEncoder.encode(newPassword)).thenReturn(newPassword);
// 更新用户密码 // 更新用户密码
authService.updatePassword(userDO.getId(),reqVO); authService.updatePassword(userDO.getId(), reqVO);
assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),newPassword); assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),newPassword);
} }
@Test @Test
public void testResetPassword_success(){ public void testResetPassword_success(){
// 准备参数 // 准备参数
MbrUserDO userDO = randomMbrUserDO(); UserDO userDO = randomUserDO();
mbrUserMapper.insert(userDO); mbrUserMapper.insert(userDO);
// 随机密码 // 随机密码
@ -102,7 +100,7 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
when(passwordEncoder.encode(password)).thenReturn(password); when(passwordEncoder.encode(password)).thenReturn(password);
// 更新用户密码 // 更新用户密码
MbrAuthResetPasswordReqVO reqVO = new MbrAuthResetPasswordReqVO(); AppAuthResetPasswordReqVO reqVO = new AppAuthResetPasswordReqVO();
reqVO.setMobile(userDO.getMobile()); reqVO.setMobile(userDO.getMobile());
reqVO.setPassword(password); reqVO.setPassword(password);
reqVO.setCode(code); reqVO.setCode(code);
@ -115,12 +113,12 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
// ========== 随机对象 ========== // ========== 随机对象 ==========
@SafeVarargs @SafeVarargs
private static MbrUserDO randomMbrUserDO(Consumer<MbrUserDO>... consumers) { private static UserDO randomUserDO(Consumer<UserDO>... consumers) {
Consumer<MbrUserDO> consumer = (o) -> { Consumer<UserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setPassword(randomString()); o.setPassword(randomString());
}; };
return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers)); return randomPojo(UserDO.class, ArrayUtils.append(consumer, consumers));
} }

View File

@ -2,18 +2,17 @@ package cn.iocoder.yudao.module.member.service.user;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService; import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
import cn.iocoder.yudao.userserver.BaseDbAndRedisUnitTest; import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO; import cn.iocoder.yudao.module.member.dal.dataobject.sms.SysSmsCodeDO;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; import cn.iocoder.yudao.module.member.dal.mysql.user.UserMapper;
import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl; import cn.iocoder.yudao.module.member.enums.sms.SysSmsSceneEnum;
import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; import cn.iocoder.yudao.module.member.service.auth.AuthServiceImpl;
import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl; import cn.iocoder.yudao.module.member.service.sms.SysSmsCodeService;
import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; import cn.iocoder.yudao.module.member.test.BaseDbAndRedisUnitTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -27,30 +26,29 @@ import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.*; import static cn.hutool.core.util.RandomUtil.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
// TODO @芋艿单测的 review等逻辑都达成一致后 // TODO @芋艿单测的 review等逻辑都达成一致后
/** /**
* {@link MbrUserServiceImpl} 的单元测试类 * {@link UserServiceImpl} 的单元测试类
* *
* @author 宋天 * @author 宋天
*/ */
@Import({MbrUserServiceImpl.class, YudaoRedisAutoConfiguration.class}) @Import({UserServiceImpl.class, YudaoRedisAutoConfiguration.class})
public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest { public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource @Resource
private MbrUserServiceImpl mbrUserService; private UserServiceImpl mbrUserService;
@Resource @Resource
private StringRedisTemplate stringRedisTemplate; private StringRedisTemplate stringRedisTemplate;
@Resource @Resource
private MbrUserMapper userMapper; private UserMapper userMapper;
@MockBean @MockBean
private SysAuthServiceImpl authService; private AuthServiceImpl authService;
@MockBean @MockBean
private InfFileCoreService fileCoreService; private InfFileCoreService fileCoreService;
@ -64,35 +62,24 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
@Test @Test
public void testUpdateNickName_success(){ public void testUpdateNickName_success(){
// mock 数据 // mock 数据
MbrUserDO userDO = randomMbrUserDO(); UserDO userDO = randomUserDO();
userMapper.insert(userDO); userMapper.insert(userDO);
// 随机昵称 // 随机昵称
String newNickName = randomString(); String newNickName = randomString();
// 调用接口修改昵称 // 调用接口修改昵称
mbrUserService.updateNickname(userDO.getId(),newNickName); mbrUserService.updateUserNickname(userDO.getId(),newNickName);
// 查询新修改后的昵称 // 查询新修改后的昵称
String nickname = mbrUserService.getUser(userDO.getId()).getNickname(); String nickname = mbrUserService.getUser(userDO.getId()).getNickname();
// 断言 // 断言
assertEquals(newNickName,nickname); assertEquals(newNickName,nickname);
} }
@Test
public void testGetUserInfo_success(){
// mock 数据
MbrUserDO userDO = randomMbrUserDO();
userMapper.insert(userDO);
// 查询用户昵称与头像
MbrUserInfoRespVO userInfo = mbrUserService.getUserInfo(userDO.getId());
System.out.println(userInfo);
}
@Test @Test
public void testUpdateAvatar_success(){ public void testUpdateAvatar_success(){
// mock 数据 // mock 数据
MbrUserDO dbUser = randomMbrUserDO(); UserDO dbUser = randomUserDO();
userMapper.insert(dbUser); userMapper.insert(dbUser);
// 准备参数 // 准备参数
@ -103,7 +90,7 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
String avatar = randomString(); String avatar = randomString();
when(fileCoreService.createFile(anyString(), eq(avatarFileBytes))).thenReturn(avatar); when(fileCoreService.createFile(anyString(), eq(avatarFileBytes))).thenReturn(avatar);
// 调用 // 调用
String str = mbrUserService.updateAvatar(userId, avatarFile); String str = mbrUserService.updateUserAvatar(userId, avatarFile);
// 断言 // 断言
assertEquals(avatar, str); assertEquals(avatar, str);
} }
@ -112,11 +99,10 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
public void updateMobile_success(){ public void updateMobile_success(){
// mock数据 // mock数据
String oldMobile = randomNumbers(11); String oldMobile = randomNumbers(11);
MbrUserDO userDO = randomMbrUserDO(); UserDO userDO = randomUserDO();
userDO.setMobile(oldMobile); userDO.setMobile(oldMobile);
userMapper.insert(userDO); userMapper.insert(userDO);
// 旧手机和旧验证码 // 旧手机和旧验证码
SysSmsCodeDO codeDO = new SysSmsCodeDO(); SysSmsCodeDO codeDO = new SysSmsCodeDO();
String oldCode = RandomUtil.randomString(4); String oldCode = RandomUtil.randomString(4);
@ -129,12 +115,12 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
// 更新手机号 // 更新手机号
String newMobile = randomNumbers(11); String newMobile = randomNumbers(11);
String newCode = randomNumbers(4); String newCode = randomNumbers(4);
MbrUserUpdateMobileReqVO reqVO = new MbrUserUpdateMobileReqVO(); AppUserUpdateMobileReqVO reqVO = new AppUserUpdateMobileReqVO();
reqVO.setMobile(newMobile); reqVO.setMobile(newMobile);
reqVO.setCode(newCode); reqVO.setCode(newCode);
reqVO.setOldMobile(oldMobile); reqVO.setOldMobile(oldMobile);
reqVO.setOldCode(oldCode); reqVO.setOldCode(oldCode);
mbrUserService.updateMobile(userDO.getId(),reqVO); mbrUserService.updateUserMobile(userDO.getId(),reqVO);
assertEquals(mbrUserService.getUser(userDO.getId()).getMobile(),newMobile); assertEquals(mbrUserService.getUser(userDO.getId()).getMobile(),newMobile);
} }
@ -142,11 +128,11 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
// ========== 随机对象 ========== // ========== 随机对象 ==========
@SafeVarargs @SafeVarargs
private static MbrUserDO randomMbrUserDO(Consumer<MbrUserDO>... consumers) { private static UserDO randomUserDO(Consumer<UserDO>... consumers) {
Consumer<MbrUserDO> consumer = (o) -> { Consumer<UserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
}; };
return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers)); return randomPojo(UserDO.class, ArrayUtils.append(consumer, consumers));
} }
} }

View File

@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.member.test;
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration; import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration; import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
import cn.iocoder.yudao.userserver.config.RedisTestConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.redisson.spring.starter.RedissonAutoConfiguration; import org.redisson.spring.starter.RedissonAutoConfiguration;

View File

@ -42,3 +42,8 @@ mybatis:
--- #################### 芋道相关配置 #################### --- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置 # 芋道配置项,设置当前项目所有自定义的配置
yudao:
info:
base-package: cn.iocoder.yudao.module.member.dal.mysql
core-service:
base-package: cn.iocoder.yudao.module.member.dal.mysql # TODO 芋艿:要清理掉

View File

@ -124,6 +124,19 @@ logging:
file: file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
--- #################### 微信公众号相关配置 ####################
wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
mp:
# 公众号配置(必填)
app-id: wx041349c6f39b268b
secret: 5abee519483bc9f8cb37ce280e814bd0
# 存储配置,解决 AccessToken 的跨节点的共享
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
--- #################### 芋道相关配置 #################### --- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置 # 芋道配置项,设置当前项目所有自定义的配置

View File

@ -66,11 +66,6 @@ yudao:
constants-class-list: constants-class-list:
- cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants - cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants
- cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants - cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants
sms-code: # 短信验证码相关的配置项
expire-times: 10m
send-frequency: 1m
send-maximum-quantity-per-day: 10
begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
end-code: 9999 # 这里配置 9999 的原因是,测试方便。
debug: false debug: false