Bladeren bron

框架优化

24282 2 jaren geleden
bovenliggende
commit
ad42410eca
26 gewijzigde bestanden met toevoegingen van 444 en 288 verwijderingen
  1. 22 16
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
  2. 13 20
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
  3. 5 1
      ruoyi-admin/src/main/resources/application-prod.yml
  4. 5 1
      ruoyi-admin/src/main/resources/application-test.yml
  5. 13 0
      ruoyi-common/src/main/java/com/ruoyi/common/annotation/TenantIgnore.java
  6. 50 29
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
  7. 6 42
      ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java
  8. 8 9
      ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
  9. 36 44
      ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
  10. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/DataScopeAspect.java
  11. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/DataSourceAspect.java
  12. 2 2
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/LogAspect.java
  13. 2 2
      ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/RateLimiterAspect.java
  14. 7 10
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
  15. 1 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java
  16. 4 1
      ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java
  17. 19 21
      ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/MybatisPlusConfig.java
  18. 28 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/aspectj/TenantAspect.java
  19. 29 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/holder/TenantHolder.java
  20. 85 0
      ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/interceptor/MyTenantInterceptor.java
  21. 33 44
      ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
  22. 24 22
      ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
  23. 4 2
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
  24. 2 2
      ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
  25. 4 5
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
  26. 40 12
      ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

+ 22 - 16
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -1,12 +1,5 @@
 package com.ruoyi.web.controller.system;
 
-import java.util.List;
-import java.util.Set;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysMenu;
@@ -16,15 +9,23 @@ import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.framework.web.service.SysLoginService;
 import com.ruoyi.framework.web.service.SysPermissionService;
 import com.ruoyi.system.service.ISysMenuService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Set;
 
 /**
  * 登录验证
- * 
+ *
  * @author ruoyi
  */
 @RestController
-public class SysLoginController
-{
+public class SysLoginController {
     @Autowired
     private SysLoginService loginService;
 
@@ -34,19 +35,24 @@ public class SysLoginController
     @Autowired
     private SysPermissionService permissionService;
 
+    @Autowired
+    private HttpServletRequest request;
+
     /**
      * 登录方法
-     * 
+     *
      * @param loginBody 登录信息
      * @return 结果
      */
     @PostMapping("/login")
-    public AjaxResult login(@RequestBody LoginBody loginBody)
-    {
-        AjaxResult ajax = AjaxResult.success();
+    public AjaxResult login(@RequestBody LoginBody loginBody) {
+        String tenantId = request.getHeader("tenantId");
+
         // 生成令牌
-        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
-                loginBody.getUuid());
+        String token = loginService.login(tenantId, loginBody.getUsername(), loginBody.getPassword(),
+                loginBody.getCode(), loginBody.getUuid());
+
+        AjaxResult ajax = AjaxResult.success();
         ajax.put(Constants.TOKEN, token);
         return ajax;
     }

+ 13 - 20
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java

@@ -1,18 +1,5 @@
 package com.ruoyi.web.controller.system;
 
-import java.util.List;
-import javax.servlet.http.HttpServletResponse;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
@@ -30,6 +17,13 @@ import com.ruoyi.system.domain.SysUserRole;
 import com.ruoyi.system.service.ISysDeptService;
 import com.ruoyi.system.service.ISysRoleService;
 import com.ruoyi.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
 
 /**
  * 角色信息
@@ -125,15 +119,14 @@ public class SysRoleController extends BaseController
             return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
         }
         role.setUpdateBy(getUsername());
-        
-        if (roleService.updateRole(role) > 0)
-        {
+
+        if (roleService.updateRole(role) > 0) {
             // 更新缓存用户权限
             LoginUser loginUser = getLoginUser();
-            if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
-            {
-                loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
-                loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
+            SysUser user = loginUser.getUser();
+            if (StringUtils.isNotNull(user) && !user.isAdmin()) {
+                loginUser.setPermissions(permissionService.getMenuPermission(user));
+                loginUser.setUser(userService.selectUserByUserName(user.getTenantId(), user.getUserName()));
                 tokenService.setLoginUser(loginUser);
             }
             return success();

+ 5 - 1
ruoyi-admin/src/main/resources/application-prod.yml

@@ -81,4 +81,8 @@ spring:
                 # 连接池的最大数据库连接数
                 max-active: 8
                 # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
+                max-wait: -1ms
+
+server:
+    servlet:
+        context-path: /prod-api

+ 5 - 1
ruoyi-admin/src/main/resources/application-test.yml

@@ -81,4 +81,8 @@ spring:
                 # 连接池的最大数据库连接数
                 max-active: 8
                 # #连接池最大阻塞等待时间(使用负值表示没有限制)
-                max-wait: -1ms
+                max-wait: -1ms
+
+server:
+    servlet:
+        context-path: /prod-api

+ 13 - 0
ruoyi-common/src/main/java/com/ruoyi/common/annotation/TenantIgnore.java

@@ -0,0 +1,13 @@
+package com.ruoyi.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 排除租户逻辑
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface TenantIgnore {
+
+}

+ 50 - 29
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java

@@ -1,16 +1,19 @@
 package com.ruoyi.common.core.domain.entity;
 
-import java.util.Date;
-import java.util.List;
-import javax.validation.constraints.*;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
 import com.ruoyi.common.annotation.Excel.ColumnType;
 import com.ruoyi.common.annotation.Excel.Type;
 import com.ruoyi.common.annotation.Excels;
 import com.ruoyi.common.core.domain.BaseEntity;
 import com.ruoyi.common.xss.Xss;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.util.Date;
+import java.util.List;
 
 /**
  * 用户对象 sys_user
@@ -70,37 +73,49 @@ public class SysUser extends BaseEntity
     @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
     private Date loginDate;
 
-    /** 部门对象 */
+    /**
+     * 部门对象
+     */
     @Excels({
-        @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
-        @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
+            @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
+            @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
     })
     private SysDept dept;
 
-    /** 角色对象 */
+    /**
+     * 角色对象
+     */
     private List<SysRole> roles;
 
-    /** 角色组 */
+    /**
+     * 角色组
+     */
     private Long[] roleIds;
 
-    /** 岗位组 */
+    /**
+     * 岗位组
+     */
     private Long[] postIds;
 
-    /** 角色ID */
+    /**
+     * 角色ID
+     */
     private Long roleId;
 
-    public SysUser()
-    {
+    /**
+     * 租户id
+     */
+    private String tenantId;
+
+    public SysUser() {
 
     }
 
-    public SysUser(Long userId)
-    {
+    public SysUser(Long userId) {
         this.userId = userId;
     }
 
-    public Long getUserId()
-    {
+    public Long getUserId() {
         return userId;
     }
 
@@ -287,25 +302,31 @@ public class SysUser extends BaseEntity
         this.postIds = postIds;
     }
 
-    public Long getRoleId()
-    {
+    public Long getRoleId() {
         return roleId;
     }
 
-    public void setRoleId(Long roleId)
-    {
+    public void setRoleId(Long roleId) {
         this.roleId = roleId;
     }
 
+    public String getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(String tenantId) {
+        this.tenantId = tenantId;
+    }
+
     @Override
     public String toString() {
-        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
-            .append("userId", getUserId())
-            .append("deptId", getDeptId())
-            .append("userName", getUserName())
-            .append("nickName", getNickName())
-            .append("email", getEmail())
-            .append("phonenumber", getPhonenumber())
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("userId", getUserId())
+                .append("deptId", getDeptId())
+                .append("userName", getUserName())
+                .append("nickName", getNickName())
+                .append("email", getEmail())
+                .append("phonenumber", getPhonenumber())
             .append("sex", getSex())
             .append("avatar", getAvatar())
             .append("password", getPassword())

+ 6 - 42
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java

@@ -1,12 +1,15 @@
 package com.ruoyi.common.core.domain.model;
 
+import lombok.Data;
+
 /**
  * 用户登录对象
- * 
+ *
  * @author ruoyi
  */
-public class LoginBody
-{
+@Data
+public class LoginBody {
+
     /**
      * 用户名
      */
@@ -27,43 +30,4 @@ public class LoginBody
      */
     private String uuid;
 
-    public String getUsername()
-    {
-        return username;
-    }
-
-    public void setUsername(String username)
-    {
-        this.username = username;
-    }
-
-    public String getPassword()
-    {
-        return password;
-    }
-
-    public void setPassword(String password)
-    {
-        this.password = password;
-    }
-
-    public String getCode()
-    {
-        return code;
-    }
-
-    public void setCode(String code)
-    {
-        this.code = code;
-    }
-
-    public String getUuid()
-    {
-        return uuid;
-    }
-
-    public void setUuid(String uuid)
-    {
-        this.uuid = uuid;
-    }
 }

+ 8 - 9
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java

@@ -1,16 +1,17 @@
 package com.ruoyi.common.filter;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.html.EscapeUtil;
+import org.apache.commons.io.IOUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
 import javax.servlet.ReadListener;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
-import org.apache.commons.io.IOUtils;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.common.utils.html.EscapeUtil;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 
 /**
  * XSS过滤处理
@@ -100,8 +101,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
 
     /**
      * 是否是Json请求
-     * 
-     * @param request
      */
     public boolean isJsonRequest()
     {

+ 36 - 44
ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java

@@ -1,30 +1,26 @@
 package com.ruoyi.common.utils;
 
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import com.ruoyi.common.constant.HttpStatus;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.exception.ServiceException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
 /**
  * 安全服务工具类
- * 
+ *
  * @author ruoyi
  */
-public class SecurityUtils
-{
+public class SecurityUtils {
+
     /**
      * 用户ID
      **/
-    public static Long getUserId()
-    {
-        try
-        {
+    public static Long getUserId() {
+        try {
             return getLoginUser().getUserId();
-        }
-        catch (Exception e)
-        {
+        } catch (Exception e) {
             throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED);
         }
     }
@@ -32,29 +28,32 @@ public class SecurityUtils
     /**
      * 获取部门ID
      **/
-    public static Long getDeptId()
-    {
-        try
-        {
+    public static Long getDeptId() {
+        try {
             return getLoginUser().getDeptId();
-        }
-        catch (Exception e)
-        {
+        } catch (Exception e) {
             throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
         }
     }
-    
+
     /**
      * 获取用户账户
      **/
-    public static String getUsername()
-    {
-        try
-        {
+    public static String getUsername() {
+        try {
             return getLoginUser().getUsername();
+        } catch (Exception e) {
+            throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
         }
-        catch (Exception e)
-        {
+    }
+
+    /**
+     * 获取租户id
+     **/
+    public static String getTenantId() {
+        try {
+            return getLoginUser().getUser().getTenantId();
+        } catch (Exception e) {
             throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
         }
     }
@@ -62,14 +61,10 @@ public class SecurityUtils
     /**
      * 获取用户
      **/
-    public static LoginUser getLoginUser()
-    {
-        try
-        {
+    public static LoginUser getLoginUser() {
+        try {
             return (LoginUser) getAuthentication().getPrincipal();
-        }
-        catch (Exception e)
-        {
+        } catch (Exception e) {
             throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED);
         }
     }
@@ -77,8 +72,7 @@ public class SecurityUtils
     /**
      * 获取Authentication
      */
-    public static Authentication getAuthentication()
-    {
+    public static Authentication getAuthentication() {
         return SecurityContextHolder.getContext().getAuthentication();
     }
 
@@ -88,8 +82,7 @@ public class SecurityUtils
      * @param password 密码
      * @return 加密字符串
      */
-    public static String encryptPassword(String password)
-    {
+    public static String encryptPassword(String password) {
         BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
         return passwordEncoder.encode(password);
     }
@@ -97,24 +90,23 @@ public class SecurityUtils
     /**
      * 判断密码是否相同
      *
-     * @param rawPassword 真实密码
+     * @param rawPassword     真实密码
      * @param encodedPassword 加密后字符
      * @return 结果
      */
-    public static boolean matchesPassword(String rawPassword, String encodedPassword)
-    {
+    public static boolean matchesPassword(String rawPassword, String encodedPassword) {
         BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
         return passwordEncoder.matches(rawPassword, encodedPassword);
     }
 
     /**
      * 是否为管理员
-     * 
+     *
      * @param userId 用户ID
      * @return 结果
      */
-    public static boolean isAdmin(Long userId)
-    {
+    public static boolean isAdmin(Long userId) {
         return userId != null && 1L == userId;
     }
+
 }

+ 1 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java → ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/DataScopeAspect.java

@@ -1,4 +1,4 @@
-package com.ruoyi.framework.aspectj;
+package com.ruoyi.framework.aspect;
 
 import java.util.ArrayList;
 import java.util.List;

+ 1 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java → ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/DataSourceAspect.java

@@ -1,4 +1,4 @@
-package com.ruoyi.framework.aspectj;
+package com.ruoyi.framework.aspect;
 
 import java.util.Objects;
 import org.aspectj.lang.ProceedingJoinPoint;

+ 2 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java → ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/LogAspect.java

@@ -1,4 +1,4 @@
-package com.ruoyi.framework.aspectj;
+package com.ruoyi.framework.aspect;
 
 import java.util.Collection;
 import java.util.Map;
@@ -45,7 +45,7 @@ public class LogAspect
     public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
 
     /** 计算操作消耗时间 */
-    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
+    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<>("Cost Time");
 
     /**
      * 处理请求前执行

+ 2 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java → ruoyi-framework/src/main/java/com/ruoyi/framework/aspect/RateLimiterAspect.java

@@ -1,4 +1,4 @@
-package com.ruoyi.framework.aspectj;
+package com.ruoyi.framework.aspect;
 
 import java.lang.reflect.Method;
 import java.util.Collections;
@@ -75,7 +75,7 @@ public class RateLimiterAspect
 
     public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
     {
-        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
+        StringBuilder stringBuffer = new StringBuilder(rateLimiter.key());
         if (rateLimiter.limitType() == LimitType.IP)
         {
             stringBuffer.append(IpUtils.getIpAddr()).append("-");

+ 7 - 10
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java

@@ -32,20 +32,19 @@ import java.util.concurrent.TimeUnit;
 
 /**
  * 通用配置
- * 
+ *
  * @author ruoyi
  */
 @EnableWebMvc
 @Configuration
 @SuppressWarnings({"unchecked", "rawtypes"})
-public class ResourcesConfig implements WebMvcConfigurer
-{
+public class ResourcesConfig implements WebMvcConfigurer {
+
     @Autowired
     private RepeatSubmitInterceptor repeatSubmitInterceptor;
 
     @Override
-    public void addResourceHandlers(ResourceHandlerRegistry registry)
-    {
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
         /** 本地文件上传路径 */
         registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
                 .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
@@ -53,15 +52,14 @@ public class ResourcesConfig implements WebMvcConfigurer
         /** swagger配置 */
         registry.addResourceHandler("/swagger-ui/**")
                 .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
-                .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
+                .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
     }
 
     /**
      * 自定义拦截规则
      */
     @Override
-    public void addInterceptors(InterceptorRegistry registry)
-    {
+    public void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
     }
 
@@ -69,8 +67,7 @@ public class ResourcesConfig implements WebMvcConfigurer
      * 跨域配置
      */
     @Bean
-    public CorsFilter corsFilter()
-    {
+    public CorsFilter corsFilter() {
         CorsConfiguration config = new CorsConfiguration();
         config.setAllowCredentials(true);
         // 设置访问源地址

+ 1 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java

@@ -1,8 +1,8 @@
 package com.ruoyi.framework.config.properties;
 
+import com.alibaba.druid.pool.DruidDataSource;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
-import com.alibaba.druid.pool.DruidDataSource;
 
 /**
  * druid 配置属性

+ 4 - 1
ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java

@@ -1,5 +1,7 @@
 package com.ruoyi.framework.datasource;
 
+import cn.hutool.core.util.ObjectUtil;
+import com.ruoyi.common.enums.DataSourceType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,7 +34,7 @@ public class DynamicDataSourceContextHolder
      */
     public static String getDataSourceType()
     {
-        return CONTEXT_HOLDER.get();
+        return ObjectUtil.defaultIfNull(CONTEXT_HOLDER.get(), DataSourceType.MASTER.name());
     }
 
     /**
@@ -42,4 +44,5 @@ public class DynamicDataSourceContextHolder
     {
         CONTEXT_HOLDER.remove();
     }
+
 }

+ 19 - 21
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java → ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/MybatisPlusConfig.java

@@ -1,10 +1,10 @@
-package com.ruoyi.framework.config;
+package com.ruoyi.framework.mybatis;
 
 import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.ruoyi.framework.mybatis.interceptor.MyTenantInterceptor;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
@@ -24,36 +24,34 @@ public class MybatisPlusConfig {
         // 分页插件
         interceptor.addInnerInterceptor(paginationInnerInterceptor());
         // 乐观锁插件
-        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
-        // 阻断插件
-        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
+        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+
+
+        interceptor.addInnerInterceptor(new MyTenantInterceptor());
         return interceptor;
     }
 
-    /**
-     * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
-     */
     public PaginationInnerInterceptor paginationInnerInterceptor() {
         PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
         // 设置数据库类型为mysql
         paginationInnerInterceptor.setDbType(DbType.MYSQL);
         // 设置最大单页限制数量,默认 500 条,-1 不受限制
         paginationInnerInterceptor.setMaxLimit(-1L);
+        // 翻页溢出处理
+        paginationInnerInterceptor.setOverflow(true);
         return paginationInnerInterceptor;
     }
 
-    /**
-     * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
-     */
-    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
-        return new OptimisticLockerInnerInterceptor();
-    }
-
-    /**
-     * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
-     */
-    public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
-        return new BlockAttackInnerInterceptor();
-    }
+    // public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
+    //     TenantLineInnerInterceptor tenantLineInnerInterceptor = new TenantLineInnerInterceptor(new TenantLineHandler() {
+    //         @Override
+    //         public Expression getTenantId() {
+    //             return null;
+    //         }
+    //     });
+    //
+    //
+    //     return yt
+    // }
 
 }

+ 28 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/aspectj/TenantAspect.java

@@ -0,0 +1,28 @@
+package com.ruoyi.framework.mybatis.aspectj;
+
+import com.ruoyi.common.annotation.TenantIgnore;
+import com.ruoyi.framework.mybatis.holder.TenantHolder;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+/**
+ * 租户拦截器
+ */
+@Aspect
+public class TenantAspect {
+
+    @Around("@annotation(tenantIgnore)")
+    public Object around(ProceedingJoinPoint point, TenantIgnore tenantIgnore) throws Throwable {
+        try {
+            // 开启忽略
+            TenantHolder.setIgnore(Boolean.TRUE);
+            // 执行方法
+            return point.proceed();
+        } finally {
+            // 关闭忽略
+            TenantHolder.clear();
+        }
+    }
+
+}

+ 29 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/holder/TenantHolder.java

@@ -0,0 +1,29 @@
+package com.ruoyi.framework.mybatis.holder;
+
+import org.springframework.core.NamedThreadLocal;
+
+/**
+ * 租户线程处理
+ */
+public class TenantHolder {
+
+    private static final ThreadLocal<Boolean> TENANT_KEY_HOLDER = new NamedThreadLocal<Boolean>("saas-tenant") {
+        @Override
+        protected Boolean initialValue() {
+            return Boolean.FALSE;
+        }
+    };
+
+    public static void setIgnore(Boolean ignore) {
+        TENANT_KEY_HOLDER.set(ignore);
+    }
+
+    public static Boolean isIgnore() {
+        return TENANT_KEY_HOLDER.get();
+    }
+
+    public static void clear() {
+        TENANT_KEY_HOLDER.remove();
+    }
+
+}

+ 85 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/mybatis/interceptor/MyTenantInterceptor.java

@@ -0,0 +1,85 @@
+package com.ruoyi.framework.mybatis.interceptor;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.alibaba.druid.pool.DruidDataSource;
+import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import com.ruoyi.common.enums.DataSourceType;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;
+import com.ruoyi.framework.mybatis.holder.TenantHolder;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+public class MyTenantInterceptor extends TenantLineInnerInterceptor {
+
+    private static final String TenantName = "tenant_id";
+
+    private static final Map<String, List<String>> notIncludeTenantIdTableNameMap = new ConcurrentHashMap<>();
+
+    public MyTenantInterceptor() {
+
+        // 获取主库数据源
+        DruidDataSource dataSource = SpringUtil.getBean("masterDataSource");
+        putTableMap(dataSource, DataSourceType.MASTER);
+
+        // TODO 暂未配置从库
+
+        setTenantLineHandler(new TenantLineHandler() {
+
+            @Override
+            public Expression getTenantId() {
+                return new StringValue(SecurityUtils.getTenantId());
+            }
+
+            @Override
+            public String getTenantIdColumn() {
+                return TenantName;
+            }
+
+            @Override
+            public boolean ignoreTable(String tableName) {
+
+                // 存在忽略自动拼接租户注解,不拼接租户字段
+                if (TenantHolder.isIgnore()) {
+                    return true;
+                }
+
+                // 获取当前线程处理的数据源类型
+                String dataSourceType = DynamicDataSourceContextHolder.getDataSourceType();
+                // 获取当前数据原中不包涵租户字段的表名
+                List<String> tableNameList = notIncludeTenantIdTableNameMap.get(dataSourceType);
+                return tableNameList.contains(tableName);
+            }
+
+        });
+    }
+
+    /**
+     * 添加不存在租户字段的表map
+     *
+     * @param dataSource     数据源
+     * @param dataSourceType 数据源类型
+     */
+    private void putTableMap(DruidDataSource dataSource, DataSourceType dataSourceType) {
+        // 获取链接url
+        String url = dataSource.getUrl();
+        // 获取数据库名
+        String dbName = url.split("/")[3].split("\\?")[0];
+        // 查询不包含租户字段的表名
+        String sql = "SELECT DISTINCT table_name FROM information_schema.COLUMNS WHERE table_schema = ? AND column_name != ?";
+        // 获取jdbc
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
+        List<String> notIncludeTenantIdTableNameList = jdbcTemplate.queryForList(sql, String.class, dbName, TenantName);
+        notIncludeTenantIdTableNameMap.put(dataSourceType.name(), notIncludeTenantIdTableNameList);
+    }
+
+
+}

+ 33 - 44
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java

@@ -1,12 +1,5 @@
 package com.ruoyi.framework.web.service;
 
-import javax.annotation.Resource;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.stereotype.Component;
 import com.ruoyi.common.constant.CacheConstants;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.constant.UserConstants;
@@ -14,11 +7,7 @@ import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.common.exception.user.BlackListException;
-import com.ruoyi.common.exception.user.CaptchaException;
-import com.ruoyi.common.exception.user.CaptchaExpireException;
-import com.ruoyi.common.exception.user.UserNotExistsException;
-import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
+import com.ruoyi.common.exception.user.*;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.MessageUtils;
 import com.ruoyi.common.utils.StringUtils;
@@ -28,6 +17,14 @@ import com.ruoyi.framework.manager.factory.AsyncFactory;
 import com.ruoyi.framework.security.context.AuthenticationContextHolder;
 import com.ruoyi.system.service.ISysConfigService;
 import com.ruoyi.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
 
 /**
  * 登录校验方法
@@ -54,43 +51,37 @@ public class SysLoginService
 
     /**
      * 登录验证
-     * 
+     *
      * @param username 用户名
      * @param password 密码
-     * @param code 验证码
-     * @param uuid 唯一标识
+     * @param code     验证码
+     * @param uuid     唯一标识
      * @return 结果
      */
-    public String login(String username, String password, String code, String uuid)
-    {
+    public String login(String tenantId, String username, String password, String code, String uuid) {
+
         // 验证码校验
         validateCaptcha(username, code, uuid);
+
         // 登录前置校验
-        loginPreCheck(username, password);
+        loginPreCheck(tenantId, username, password);
+
         // 用户验证
-        Authentication authentication = null;
-        try
-        {
+        Authentication authentication;
+        try {
             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
             AuthenticationContextHolder.setContext(authenticationToken);
             // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
             authentication = authenticationManager.authenticate(authenticationToken);
-        }
-        catch (Exception e)
-        {
-            if (e instanceof BadCredentialsException)
-            {
+        } catch (Exception e) {
+            if (e instanceof BadCredentialsException) {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                 throw new UserPasswordNotMatchException();
-            }
-            else
-            {
+            } else {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                 throw new ServiceException(e.getMessage());
             }
-        }
-        finally
-        {
+        } finally {
             AuthenticationContextHolder.clearContext();
         }
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
@@ -131,35 +122,33 @@ public class SysLoginService
 
     /**
      * 登录前置校验
+     *
      * @param username 用户名
      * @param password 用户密码
      */
-    public void loginPreCheck(String username, String password)
-    {
+    public void loginPreCheck(String tenantId, String username, String password) {
+
+        if (StringUtils.isEmpty(tenantId)) {
+            throw new ServiceException("租户id不能为空");
+        }
         // 用户名或密码为空 错误
-        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
-        {
+        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
             throw new UserNotExistsException();
         }
         // 密码如果不在指定范围内 错误
-        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
-                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
-        {
+        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
             throw new UserPasswordNotMatchException();
         }
         // 用户名不在指定范围内 错误
-        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
-                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
-        {
+        if (username.length() < UserConstants.USERNAME_MIN_LENGTH || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
             throw new UserPasswordNotMatchException();
         }
         // IP黑名单校验
         String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
-        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
-        {
+        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
             throw new BlackListException();
         }

+ 24 - 22
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java

@@ -1,5 +1,11 @@
 package com.ruoyi.framework.web.service;
 
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.enums.UserStatus;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.service.ISysUserService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -7,12 +13,8 @@ import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
-import com.ruoyi.common.core.domain.entity.SysUser;
-import com.ruoyi.common.core.domain.model.LoginUser;
-import com.ruoyi.common.enums.UserStatus;
-import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.system.service.ISysUserService;
+
+import javax.servlet.http.HttpServletRequest;
 
 /**
  * 用户验证处理
@@ -20,35 +22,35 @@ import com.ruoyi.system.service.ISysUserService;
  * @author ruoyi
  */
 @Service
-public class UserDetailsServiceImpl implements UserDetailsService
-{
+public class UserDetailsServiceImpl implements UserDetailsService {
+
     private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
 
     @Autowired
     private ISysUserService userService;
-    
+
     @Autowired
     private SysPasswordService passwordService;
 
     @Autowired
     private SysPermissionService permissionService;
 
+    @Autowired
+    private HttpServletRequest request;
+
     @Override
-    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
-    {
-        SysUser user = userService.selectUserByUserName(username);
-        if (StringUtils.isNull(user))
-        {
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+
+        String tenantId = request.getHeader("tenantId");
+
+        SysUser user = userService.selectUserByUserName(tenantId, username);
+        if (StringUtils.isNull(user)) {
             log.info("登录用户:{} 不存在.", username);
             throw new ServiceException("登录用户:" + username + " 不存在");
-        }
-        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
-        {
+        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
             log.info("登录用户:{} 已被删除.", username);
             throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
-        }
-        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
-        {
+        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
             log.info("登录用户:{} 已被停用.", username);
             throw new ServiceException("对不起,您的账号:" + username + " 已停用");
         }
@@ -58,8 +60,8 @@ public class UserDetailsServiceImpl implements UserDetailsService
         return createLoginUser(user);
     }
 
-    public UserDetails createLoginUser(SysUser user)
-    {
+    public UserDetails createLoginUser(SysUser user) {
         return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
     }
+
 }

+ 4 - 2
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

@@ -1,6 +1,7 @@
 package com.ruoyi.system.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.common.annotation.TenantIgnore;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import org.apache.ibatis.annotations.Param;
 
@@ -38,11 +39,12 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
 
     /**
      * 通过用户名查询用户
-     * 
+     *
      * @param userName 用户名
      * @return 用户对象信息
      */
-    public SysUser selectUserByUserName(String userName);
+    @TenantIgnore
+    public SysUser selectUserByUserName(@Param("tenantId") String tenantId, @Param("userName") String userName);
 
     /**
      * 通过用户ID查询用户

+ 2 - 2
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java

@@ -37,11 +37,11 @@ public interface ISysUserService extends BaseService<SysUser> {
 
     /**
      * 通过用户名查询用户
-     * 
+     *
      * @param userName 用户名
      * @return 用户对象信息
      */
-    public SysUser selectUserByUserName(String userName);
+    public SysUser selectUserByUserName(String tenantId, String userName);
 
     /**
      * 通过用户ID查询用户

+ 4 - 5
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

@@ -100,14 +100,13 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
 
     /**
      * 通过用户名查询用户
-     * 
+     *
      * @param userName 用户名
      * @return 用户对象信息
      */
     @Override
-    public SysUser selectUserByUserName(String userName)
-    {
-        return userMapper.selectUserByUserName(userName);
+    public SysUser selectUserByUserName(String tenantId, String userName) {
+        return userMapper.selectUserByUserName(tenantId, userName);
     }
 
     /**
@@ -493,7 +492,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
             try
             {
                 // 验证是否存在这个用户
-                SysUser u = userMapper.selectUserByUserName(user.getUserName());
+                SysUser u = userMapper.selectUserByUserName(user.getTenantId(), user.getUserName());
                 if (StringUtils.isNull(u))
                 {
                     BeanValidators.validateWithException(validator, user);

+ 40 - 12
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -23,6 +23,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="updateBy"     column="update_by"    />
         <result property="updateTime"   column="update_time"  />
         <result property="remark"       column="remark"       />
+		<result property="tenantId" 	column="tenant_id"    />
         <association property="dept"    column="dept_id" javaType="SysDept" resultMap="deptResult" />
         <collection  property="roles"   javaType="java.util.List"           resultMap="RoleResult" />
     </resultMap>
@@ -45,16 +46,43 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="dataScope"     column="data_scope"    />
         <result property="status"       column="role_status"    />
     </resultMap>
-	
+
 	<sql id="selectUserVo">
-        select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, 
-        d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
-        r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
-        from sys_user u
-		    left join sys_dept d on u.dept_id = d.dept_id
-		    left join sys_user_role ur on u.user_id = ur.user_id
-		    left join sys_role r on r.role_id = ur.role_id
-    </sql>
+		select u.user_id,
+			   u.dept_id,
+			   u.user_name,
+			   u.nick_name,
+			   u.email,
+			   u.avatar,
+			   u.phonenumber,
+			   u.password,
+			   u.sex,
+			   u.status,
+			   u.del_flag,
+			   u.login_ip,
+			   u.login_date,
+			   u.create_by,
+			   u.create_time,
+			   u.remark,
+			   u.tenant_id,
+			   d.dept_id,
+			   d.parent_id,
+			   d.ancestors,
+			   d.dept_name,
+			   d.order_num,
+			   d.leader,
+			   d.status as dept_status,
+			   r.role_id,
+			   r.role_name,
+			   r.role_key,
+			   r.role_sort,
+			   r.data_scope,
+			   r.status as role_status
+		from sys_user u
+				 left join sys_dept d on u.dept_id = d.dept_id
+				 left join sys_user_role ur on u.user_id = ur.user_id
+				 left join sys_role r on r.role_id = ur.role_id
+	</sql>
     
     <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
 		select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u
@@ -119,10 +147,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 		<!-- 数据范围过滤 -->
 		${params.dataScope}
 	</select>
-	
+
 	<select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
-	    <include refid="selectUserVo"/>
-		where u.user_name = #{userName} and u.del_flag = '0'
+		<include refid="selectUserVo"/>
+		where u.user_name = #{userName} and u.tenant_id = #{tenantId} and u.del_flag = '0'
 	</select>
 	
 	<select id="selectUserById" parameterType="Long" resultMap="SysUserResult">