* 【新增】后端 `yudao.tenant.enable` 配置项,前端 `VUE_APP_TENANT_ENABLE` 配置项,用于开关租户功能
* 【优化】调整默认所有表开启多租户的特性,可通过 `yudao.tenant.ignore-tables` 配置项进行忽略,替代原本默认不开启的策略 * 【新增】通过 `yudao.tenant.ignore-urls` 配置忽略多租户的请求,例如说 ,例如说短信回调、支付回调等 Open APIpull/2/head
parent
27c30279a1
commit
79311ecc71
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.common.enums;
|
|||
/**
|
||||
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
|
||||
*
|
||||
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
|
||||
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 util 包下
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
|
|
|
@ -14,22 +14,28 @@ import java.util.Set;
|
|||
@Data
|
||||
public class TenantProperties {
|
||||
|
||||
// /**
|
||||
// * 租户是否开启
|
||||
// */
|
||||
// private static final Boolean ENABLE_DEFAULT = true;
|
||||
//
|
||||
// /**
|
||||
// * 是否开启
|
||||
// */
|
||||
// private Boolean enable = ENABLE_DEFAULT;
|
||||
/**
|
||||
* 租户是否开启
|
||||
*/
|
||||
private static final Boolean ENABLE_DEFAULT = true;
|
||||
|
||||
/**
|
||||
* 需要多租户的表
|
||||
*
|
||||
* 由于多租户并不作为 yudao 项目的重点功能,更多是扩展性的功能,所以采用正向配置需要多租户的表。
|
||||
* 如果需要,你可以改成 ignoreTables 来取消部分不需要的表
|
||||
* 是否开启
|
||||
*/
|
||||
private Set<String> tables;
|
||||
private Boolean enable = ENABLE_DEFAULT;
|
||||
|
||||
/**
|
||||
* 需要忽略多租户的请求
|
||||
*
|
||||
* 默认情况下,每个请求需要带上 tenant-id 的请求头。但是,部分请求是无需带上的,例如说短信回调、支付回调等 Open API!
|
||||
*/
|
||||
private Set<String> ignoreUrls;
|
||||
|
||||
/**
|
||||
* 需要忽略多租户的表
|
||||
*
|
||||
* 即默认所有表都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟
|
||||
*/
|
||||
private Set<String> ignoreTables;
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
|||
import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -14,6 +15,8 @@ import org.springframework.context.annotation.Configuration;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
// 允许使用 yudao.tenant.enable=false 禁用多租户
|
||||
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(TenantProperties.class)
|
||||
public class YudaoTenantDatabaseAutoConfiguration {
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
|
|||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
@ -16,6 +17,8 @@ import org.springframework.context.annotation.Configuration;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
// 允许使用 yudao.tenant.enable=false 禁用多租户
|
||||
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
|
||||
public class YudaoTenantJobAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.tenant.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
@ -10,6 +11,8 @@ import org.springframework.context.annotation.Configuration;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
// 允许使用 yudao.tenant.enable=false 禁用多租户
|
||||
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
|
||||
public class YudaoTenantMQAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -2,6 +2,9 @@ package cn.iocoder.yudao.framework.tenant.config;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -12,12 +15,16 @@ import org.springframework.context.annotation.Configuration;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
// 允许使用 yudao.tenant.enable=false 禁用多租户
|
||||
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(TenantProperties.class)
|
||||
public class YudaoTenantSecurityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter() {
|
||||
public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties,
|
||||
WebProperties webProperties) {
|
||||
FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new TenantSecurityWebFilter());
|
||||
registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties));
|
||||
registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER);
|
||||
return registrationBean;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.tenant.config;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -12,6 +13,8 @@ import org.springframework.context.annotation.Configuration;
|
|||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
// 允许使用 yudao.tenant.enable=false 禁用多租户
|
||||
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
|
||||
public class YudaoTenantWebAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -33,7 +33,7 @@ public class TenantContextHolder {
|
|||
public static Long getRequiredTenantId() {
|
||||
Long tenantId = getTenantId();
|
||||
if (tenantId == null) {
|
||||
throw new NullPointerException("TenantContextHolder 不存在租户编号");
|
||||
throw new NullPointerException("TenantContextHolder 不存在租户编号"); // TODO 芋艿:增加文档链接
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ package cn.iocoder.yudao.framework.tenant.core.db;
|
|||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
|
@ -27,13 +25,7 @@ public class TenantDatabaseInterceptor implements TenantLineHandler {
|
|||
|
||||
@Override
|
||||
public boolean ignoreTable(String tableName) {
|
||||
// 如果实体类继承 TenantBaseDO 类,则是多租户表,不进行忽略
|
||||
TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
|
||||
if (tableInfo != null && TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) {
|
||||
return false;
|
||||
}
|
||||
// 不包含,说明要过滤
|
||||
return !CollUtil.contains(properties.getTables(), tableName);
|
||||
return CollUtil.contains(properties.getIgnoreTables(), tableName);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.security;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -18,34 +22,72 @@ import java.util.Objects;
|
|||
|
||||
/**
|
||||
* 多租户 Security Web 过滤器
|
||||
* 校验用户访问的租户,是否是其所在的租户,避免越权问题
|
||||
* 1. 如果是登陆的用户,校验是否有权限访问该租户,避免越权问题。
|
||||
* 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。
|
||||
*
|
||||
* 校验用户访问的租户,是否是其所在的租户,
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantSecurityWebFilter extends OncePerRequestFilter {
|
||||
public class TenantSecurityWebFilter extends ApiRequestFilter {
|
||||
|
||||
private final TenantProperties tenantProperties;
|
||||
private final AntPathMatcher pathMatcher;
|
||||
|
||||
public TenantSecurityWebFilter(TenantProperties tenantProperties,
|
||||
WebProperties webProperties) {
|
||||
super(webProperties);
|
||||
this.tenantProperties = tenantProperties;
|
||||
this.pathMatcher = new AntPathMatcher();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
Long tenantId = TenantContextHolder.getTenantId();
|
||||
// 1. 登陆的用户,校验是否有权限访问该租户,避免越权问题。
|
||||
LoginUser user = SecurityFrameworkUtils.getLoginUser();
|
||||
assert user != null; // shouldNotFilter 已经校验
|
||||
// 校验租户是否匹配。
|
||||
if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) {
|
||||
log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]",
|
||||
user.getTenantId(), user.getId(), user.getUserType(),
|
||||
TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod());
|
||||
ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.FORBIDDEN.getCode(),
|
||||
"您无权访问该租户的数据"));
|
||||
if (user != null) {
|
||||
// 如果获取不到租户编号,则尝试使用登陆用户的租户编号
|
||||
if (tenantId == null) {
|
||||
tenantId = user.getTenantId();
|
||||
TenantContextHolder.setTenantId(tenantId);
|
||||
// 如果传递了租户编号,则进行比对租户编号,避免越权问题
|
||||
} else if (!Objects.equals(user.getTenantId(), TenantContextHolder.getTenantId())) {
|
||||
log.error("[doFilterInternal][租户({}) User({}/{}) 越权访问租户({}) URL({}/{})]",
|
||||
user.getTenantId(), user.getId(), user.getUserType(),
|
||||
TenantContextHolder.getTenantId(), request.getRequestURI(), request.getMethod());
|
||||
ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.FORBIDDEN.getCode(),
|
||||
"您无权访问该租户的数据"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。
|
||||
if (tenantId == null && !isIgnoreUrl(request)) {
|
||||
log.error("[doFilterInternal][URL({}/{}) 未传递租户编号]", request.getRequestURI(), request.getMethod());
|
||||
ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
|
||||
"租户的请求未传递,请进行排查"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续过滤
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
return SecurityFrameworkUtils.getLoginUser() == null;
|
||||
private boolean isIgnoreUrl(HttpServletRequest request) {
|
||||
// 快速匹配,保证性能
|
||||
if (CollUtil.contains(tenantProperties.getIgnoreUrls(), request.getRequestURI())) {
|
||||
return true;
|
||||
}
|
||||
// 逐个 Ant 路径匹配
|
||||
for (String url : tenantProperties.getIgnoreUrls()) {
|
||||
if (pathMatcher.match(url, request.getRequestURI())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,15 +8,9 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import springfox.documentation.RequestHandler;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.service.ApiKey;
|
||||
import springfox.documentation.service.AuthorizationScope;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.service.SecurityReference;
|
||||
import springfox.documentation.service.SecurityScheme;
|
||||
import springfox.documentation.service.*;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
@ -24,7 +18,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static springfox.documentation.builders.RequestHandlerSelectors.basePackage;
|
||||
|
||||
|
@ -37,8 +30,8 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka
|
|||
@EnableSwagger2
|
||||
@EnableKnife4j
|
||||
@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
|
||||
@ConditionalOnProperty(prefix = "yudao.swagger", value = "enable", matchIfMissing = true)
|
||||
// 允许使用 swagger.enable=false 禁用 Swagger
|
||||
@ConditionalOnProperty(prefix = "yudao.swagger", value = "enable", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SwaggerProperties.class)
|
||||
public class YudaoSwaggerAutoConfiguration {
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package cn.iocoder.yudao.framework.web.core.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 过滤 /admin-api、/app-api 等 API 请求的过滤器
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public abstract class ApiRequestFilter extends OncePerRequestFilter {
|
||||
|
||||
protected final WebProperties webProperties;
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
// 只过滤 API 请求的地址
|
||||
return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(),
|
||||
webProperties.getAppApi().getPrefix());
|
||||
}
|
||||
|
||||
}
|
|
@ -39,10 +39,6 @@
|
|||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
|
@ -67,6 +63,12 @@
|
|||
<artifactId>yudao-spring-boot-starter-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
|
|
|
@ -4,7 +4,6 @@ import cn.hutool.core.io.IoUtil;
|
|||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.FilePageReqVO;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.FileRespVO;
|
||||
import cn.iocoder.yudao.module.infra.convert.file.FileConvert;
|
||||
|
@ -62,7 +61,6 @@ public class FileController {
|
|||
@ApiOperation("下载文件")
|
||||
@ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class)
|
||||
public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException {
|
||||
TenantContextHolder.setNullTenantId();
|
||||
FileDO file = fileService.getFile(path);
|
||||
if (file == null) {
|
||||
log.warn("[getFile][path({}) 文件不存在]", path);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.file;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
|
@ -21,7 +21,7 @@ import java.io.InputStream;
|
|||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileDO extends TenantBaseDO {
|
||||
public class FileDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 文件路径
|
||||
|
|
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.infra.dal.dataobject.logger;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
@ -21,7 +21,7 @@ import java.util.Date;
|
|||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApiAccessLogDO extends TenantBaseDO {
|
||||
public class ApiAccessLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package cn.iocoder.yudao.module.infra.dal.dataobject.logger;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
@ -21,7 +21,7 @@ import java.util.Date;
|
|||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApiErrorLogDO extends TenantBaseDO {
|
||||
public class ApiErrorLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
|
|
@ -54,8 +54,9 @@ public class LoginLogCreateReqDTO {
|
|||
private String userIp;
|
||||
/**
|
||||
* 浏览器 UserAgent
|
||||
*
|
||||
* 允许空,原因:Job 过期登出时,是无法传递 UserAgent 的
|
||||
*/
|
||||
@NotEmpty(message = "浏览器 UserAgent 不能为空")
|
||||
private String userAgent;
|
||||
|
||||
}
|
||||
|
|
|
@ -53,11 +53,11 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-social</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-social</artifactId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
|
@ -77,6 +77,12 @@
|
|||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package cn.iocoder.yudao.module.system.dal.dataobject.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
@ -25,7 +25,7 @@ import java.util.Date;
|
|||
@Data
|
||||
@Builder
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UserSessionDO extends TenantBaseDO {
|
||||
public class UserSessionDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 会话编号, 即 sessionId
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package cn.iocoder.yudao.module.system.dal.dataobject.dept;
|
||||
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
@ -17,7 +17,7 @@ import lombok.EqualsAndHashCode;
|
|||
@TableName("system_dept")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DeptDO extends TenantBaseDO {
|
||||
public class DeptDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package cn.iocoder.yudao.module.system.dal.dataobject.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
@ -15,7 +15,7 @@ import lombok.EqualsAndHashCode;
|
|||
@TableName("system_post")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PostDO extends TenantBaseDO {
|
||||
public class PostDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 岗位序号
|
||||
|
|
|
@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.system.dal.dataobject.logger;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
@ -22,7 +22,7 @@ import java.util.Map;
|
|||
@TableName(value = "system_operate_log", autoResultMap = true)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OperateLogDO extends TenantBaseDO {
|
||||
public class OperateLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* {@link #javaMethodArgs} 的最大长度
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package cn.iocoder.yudao.module.system.dal.dataobject.notice;
|
||||
|
||||
import cn.iocoder.yudao.module.system.enums.notice.NoticeTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.system.enums.notice.NoticeTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
@ -16,7 +16,7 @@ import lombok.EqualsAndHashCode;
|
|||
@TableName("system_notice")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class NoticeDO extends TenantBaseDO {
|
||||
public class NoticeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 公告ID
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package cn.iocoder.yudao.module.system.dal.dataobject.user;
|
||||
|
||||
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
|
|
@ -19,12 +19,12 @@ import javax.annotation.Resource;
|
|||
public class UserSessionTimeoutJob implements JobHandler {
|
||||
|
||||
@Resource
|
||||
private UserSessionService sysUserSessionService;
|
||||
private UserSessionService userSessionService;
|
||||
|
||||
@Override
|
||||
public String execute(String param) throws Exception {
|
||||
// 执行过期
|
||||
Long timeoutCount = sysUserSessionService.clearSessionTimeout();
|
||||
Long timeoutCount = userSessionService.clearSessionTimeout();
|
||||
// 返回结果,记录每次的超时数量
|
||||
return String.format("移除在线会话数量为 %s 个", timeoutCount);
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ public class PermissionServiceImpl implements PermissionService {
|
|||
public List<MenuDO> getRoleMenusFromCache(Collection<Long> roleIds, Collection<Integer> menuTypes,
|
||||
Collection<Integer> menusStatuses) {
|
||||
// 任一一个参数为空时,不返回任何菜单
|
||||
if (CollectionUtils.isAnyEmpty(roleIds, menusStatuses, menusStatuses)) {
|
||||
if (CollectionUtils.isAnyEmpty(roleIds, menuTypes, menusStatuses)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 判断角色是否包含管理员
|
||||
|
|
|
@ -38,10 +38,6 @@
|
|||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
|
|
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.tool.service.codegen.inner;
|
|||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.tool.convert.codegen.CodegenConvert;
|
||||
import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenTableDO;
|
||||
|
@ -60,7 +60,7 @@ public class CodegenBuilder {
|
|||
*/
|
||||
public static final String TENANT_ID_FIELD = "tenant_id";
|
||||
/**
|
||||
* {@link TenantBaseDO} 的字段
|
||||
* {@link cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO} 的字段
|
||||
*/
|
||||
public static final Set<String> BASE_DO_FIELDS = new HashSet<>();
|
||||
/**
|
||||
|
@ -96,7 +96,8 @@ public class CodegenBuilder {
|
|||
.build();
|
||||
|
||||
static {
|
||||
Arrays.stream(ReflectUtil.getFields(TenantBaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
|
||||
Arrays.stream(ReflectUtil.getFields(BaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
|
||||
BASE_DO_FIELDS.add(TENANT_ID_FIELD);
|
||||
// 处理 OPERATION 相关的字段
|
||||
CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
|
||||
UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package cn.iocoder.yudao.module.tool.service.codegen.inner;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.template.TemplateConfig;
|
||||
import cn.hutool.extra.template.TemplateEngine;
|
||||
|
@ -11,23 +9,21 @@ import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
|||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.tool.enums.codegen.CodegenSceneEnum;
|
||||
import cn.iocoder.yudao.module.tool.framework.codegen.config.CodegenProperties;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
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.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
|
||||
import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.tool.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.module.tool.enums.codegen.CodegenSceneEnum;
|
||||
import cn.iocoder.yudao.module.tool.framework.codegen.config.CodegenProperties;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -115,7 +111,8 @@ public class CodegenEngine {
|
|||
private void initGlobalBindingMap() {
|
||||
// 全局配置
|
||||
globalBindingMap.put("basePackage", codegenProperties.getBasePackage());
|
||||
globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage() + '.' + "framework"); // 用于后续获取测试类的 package 地址
|
||||
globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage()
|
||||
+ '.' + "framework"); // 用于后续获取测试类的 package 地址
|
||||
// 全局 Java Bean
|
||||
globalBindingMap.put("CommonResultClassName", CommonResult.class.getName());
|
||||
globalBindingMap.put("PageResultClassName", PageResult.class.getName());
|
||||
|
@ -123,6 +120,7 @@ public class CodegenEngine {
|
|||
globalBindingMap.put("PageParamClassName", PageParam.class.getName());
|
||||
globalBindingMap.put("DictFormatClassName", DictFormat.class.getName());
|
||||
// DO 类,独有字段
|
||||
globalBindingMap.put("BaseDOClassName", BaseDO.class.getName());
|
||||
globalBindingMap.put("baseDOFields", CodegenBuilder.BASE_DO_FIELDS);
|
||||
globalBindingMap.put("QueryWrapperClassName", LambdaQueryWrapperX.class.getName());
|
||||
globalBindingMap.put("BaseMapperClassName", BaseMapperX.class.getName());
|
||||
|
@ -156,15 +154,6 @@ public class CodegenEngine {
|
|||
// permission 前缀
|
||||
bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase);
|
||||
|
||||
// 如果多租户,则进行覆盖 DB 独有字段
|
||||
if (CollectionUtils.findFirst(columns, column -> column.getColumnName().equals(CodegenBuilder.TENANT_ID_FIELD)) != null) {
|
||||
bindingMap.put("BaseDOClassName", TenantBaseDO.class.getName());
|
||||
bindingMap.put("BaseDOClassName_simple", TenantBaseDO.class.getSimpleName());
|
||||
} else {
|
||||
bindingMap.put("BaseDOClassName", BaseDO.class.getName());
|
||||
bindingMap.put("BaseDOClassName_simple", BaseDO.class.getSimpleName());
|
||||
}
|
||||
|
||||
// 执行生成
|
||||
final Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(TEMPLATES.size()); // 有序
|
||||
TEMPLATES.forEach((vmPath, filePath) -> {
|
||||
|
|
|
@ -17,7 +17,7 @@ import ${BaseDOClassName};
|
|||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ${table.className}DO extends ${BaseDOClassName_simple} {
|
||||
public class ${table.className}DO extends BaseDO {
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段
|
||||
|
|
|
@ -78,7 +78,9 @@ yudao:
|
|||
- cn.iocoder.yudao.module.system.enums.ErrorCodeConstants
|
||||
- cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants
|
||||
tenant: # 多租户相关配置项
|
||||
tables: # 配置需要开启多租户的表;如果实体已经继承 TenantBaseDO 类,则无需重复配置
|
||||
enable: true
|
||||
ignore-urls: /admin-api/system/captcha/get-image, /admin-api/infra/file/get/*
|
||||
ignore-tables: infra_config, infra_file, infra_job, infra_job_log, infra_job_log, system_tenant, system_dict_data, system_dict_type, system_error_code, system_menu, system_role, system_role_menu, system_sms_channel, tool_codegen_column, tool_codegen_table, tool_test_demo
|
||||
sms-code: # 短信验证码相关的配置项
|
||||
expire-times: 10m
|
||||
send-frequency: 1m
|
||||
|
|
|
@ -10,3 +10,6 @@ VUE_APP_BASE_API = '/dev-api'
|
|||
|
||||
# 路由懒加载
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
|
||||
# 多租户的开关
|
||||
VUE_APP_TENANT_ENABLE = true
|
||||
|
|
|
@ -4,6 +4,7 @@ import store from '@/store'
|
|||
import { getToken } from '@/utils/auth'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import Cookies from "js-cookie";
|
||||
import {getTenantEnable} from "@/utils/ruoyi";
|
||||
|
||||
// 是否显示重新登录
|
||||
let isReloginShow;
|
||||
|
@ -24,9 +25,11 @@ service.interceptors.request.use(config => {
|
|||
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
// 设置租户
|
||||
const tenantId = Cookies.get('tenantId');
|
||||
if (tenantId) {
|
||||
config.headers['tenant-id'] = tenantId;
|
||||
if (getTenantEnable()) {
|
||||
const tenantId = Cookies.get('tenantId');
|
||||
if (tenantId) {
|
||||
config.headers['tenant-id'] = tenantId;
|
||||
}
|
||||
}
|
||||
// get请求映射params参数
|
||||
if (config.method === 'get' && config.params) {
|
||||
|
|
|
@ -170,3 +170,17 @@ export function getNowDateTime(timeStr) {
|
|||
let seconds = now.getSeconds().toString().padStart(2, "0") // 得到秒;
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得租户功能是否开启
|
||||
*/
|
||||
export function getTenantEnable() {
|
||||
console.log("enable: " + process.env.VUE_APP_TENANT_ENABLE)
|
||||
if (process.env.VUE_APP_TENANT_ENABLE === "true") {
|
||||
return true;
|
||||
}
|
||||
if (process.env.VUE_APP_TENANT_ENABLE === "false") {
|
||||
return false;
|
||||
}
|
||||
return process.env.VUE_APP_TENANT_ENABLE || true;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="login">
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<h3 class="title">芋道后台管理系统</h3>
|
||||
<el-form-item prop="tenantName">
|
||||
<el-form-item prop="tenantName" v-if="tenantEnable">
|
||||
<el-input v-model="loginForm.tenantName" type="text" auto-complete="off" placeholder='租户'>
|
||||
<svg-icon slot="prefix" icon-class="tree" class="el-input__icon input-icon" />
|
||||
</el-input>
|
||||
|
@ -54,7 +54,8 @@ import { getCodeImg,socialAuthRedirect } from "@/api/login";
|
|||
import { getTenantIdByName } from "@/api/system/tenant";
|
||||
import Cookies from "js-cookie";
|
||||
import { encrypt, decrypt } from '@/utils/jsencrypt'
|
||||
import {InfraApiErrorLogProcessStatusEnum, SystemUserSocialTypeEnum} from "@/utils/constants";
|
||||
import {SystemUserSocialTypeEnum} from "@/utils/constants";
|
||||
import { getTenantEnable } from "@/utils/ruoyi";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
|
@ -62,6 +63,7 @@ export default {
|
|||
return {
|
||||
codeUrl: "",
|
||||
captchaEnable: true,
|
||||
tenantEnable: true,
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
|
@ -71,6 +73,13 @@ export default {
|
|||
tenantName: "芋道源码",
|
||||
},
|
||||
loginRules: {
|
||||
username: [
|
||||
{ required: true, trigger: "blur", message: "用户名不能为空" }
|
||||
],
|
||||
password: [
|
||||
{ required: true, trigger: "blur", message: "密码不能为空" }
|
||||
],
|
||||
code: [{ required: true, trigger: "change", message: "验证码不能为空" }],
|
||||
tenantName: [
|
||||
{ required: true, trigger: "blur", message: "租户不能为空" },
|
||||
{
|
||||
|
@ -90,13 +99,6 @@ export default {
|
|||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
username: [
|
||||
{ required: true, trigger: "blur", message: "用户名不能为空" }
|
||||
],
|
||||
password: [
|
||||
{ required: true, trigger: "blur", message: "密码不能为空" }
|
||||
],
|
||||
code: [{ required: true, trigger: "change", message: "验证码不能为空" }]
|
||||
},
|
||||
loading: false,
|
||||
redirect: undefined,
|
||||
|
@ -113,6 +115,8 @@ export default {
|
|||
// }
|
||||
// },
|
||||
created() {
|
||||
// 租户开关
|
||||
this.tenantEnable = getTenantEnable();
|
||||
// 重定向地址
|
||||
this.redirect = this.$route.query.redirect;
|
||||
this.getCode();
|
||||
|
|
Loading…
Reference in New Issue