若依框架修改为多租户
- 创业
- 2025-09-16 02:42:01

若依框架自带了 Spring Security 权限管理,但没有原生支持多租户,目前的需求就是改为多租户的形式
若依框架修改为多租户 前端部分1. 登录页面1.1 新增租户选择项 3. 菜单和权限处理3.1 根据租户动态加载菜单 4. 数据展示和操作4.1 处理不同租户的数据 5. 全局状态管理5.1 存储租户信息 后端部分1. 多租户实现思路2. 数据库设计修改2.1 新增租户表 2.2 修改用户表3. 请求层面处理3.1 拦截请求获取租户标识3.2 租户上下文工具类 4. 权限层面处理4.1 修改 Spring Security 配置4.2 修改权限验证逻辑 5. 数据访问层面处理6. 配置拦截器或过滤器 前端部分 1. 登录页面 1.1 新增租户选择项在登录页面添加一个租户选择框,让用户在登录时选择所属的租户。可以从后端获取租户列表,填充到选择框中。
<template> <div> <!-- 用户名输入框 --> <el-input v-model="username" placeholder="请输入用户名"></el-input> <!-- 密码输入框 --> <el-input v-model="password" type="password" placeholder="请输入密码"></el-input> <!-- 租户选择框 --> <el-select v-model="tenantId" placeholder="请选择租户"> <el-option v-for="tenant in tenantList" :key="tenant.tenantId" :label="tenant.tenantName" :value="tenant.tenantId"> </el-option> </el-select> <!-- 登录按钮 --> <el-button @click="login">登录</el-button> </div> </template> <script> export default { data() { return { username: '', password: '', tenantId: null, tenantList: [] }; }, created() { // 获取租户列表 this.getTenantList(); }, methods: { getTenantList() { // 调用后端接口获取租户列表 this.$axios.get('/api/tenant/list').then(response => { this.tenantList = response.data; }); }, login() { // 登录请求,携带租户 ID this.$axios.post('/api/login', { username: this.username, password: this.password, tenantId: this.tenantId }).then(response => { // 处理登录成功逻辑 }); } } }; </script> 请求拦截器 2.1 添加租户信息到请求头 在前端的请求拦截器中,将当前租户 ID 添加到请求头中,以便后端能够识别请求所属的租户。 import axios from 'axios'; // 创建 axios 实例 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 5000 }); // 请求拦截器 service.interceptors.request.use( config => { // 获取当前租户 ID const tenantId = localStorage.getItem('tenantId'); if (tenantId) { // 将租户 ID 添加到请求头 config.headers['X-Tenant-ID'] = tenantId; } return config; }, error => { console.log(error); Promise.reject(error); } ); export default service; 3. 菜单和权限处理 3.1 根据租户动态加载菜单不同租户可能有不同的菜单和权限,前端需要根据当前租户动态加载菜单。可以在登录成功后,根据租户 ID 请求后端获取该租户的菜单信息。
// 登录成功后,获取当前租户的菜单信息 this.$axios.get('/api/menu/list', { params: { tenantId: this.tenantId } }).then(response => { // 动态生成菜单 this.menuList = response.data; }); 4. 数据展示和操作 4.1 处理不同租户的数据在展示和操作数据时,前端需要确保只显示和操作当前租户的数据。可以在请求数据时,将租户 ID 作为参数传递给后端,后端根据租户 ID 进行数据过滤。
// 获取当前租户的用户列表 this.$axios.get('/api/user/list', { params: { tenantId: this.tenantId } }).then(response => { this.userList = response.data; }); 5. 全局状态管理 5.1 存储租户信息使用 Vuex 或其他状态管理工具,存储当前租户的信息,方便在整个应用中使用。
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: { tenantId: null, tenantName: '' }, mutations: { setTenantInfo(state, tenant) { state.tenantId = tenant.tenantId; state.tenantName = tenant.tenantName; // 存储到本地存储 localStorage.setItem('tenantId', tenant.tenantId); } }, actions: { login({ commit }, tenant) { commit('setTenantInfo', tenant); } } }); 后端部分 1. 多租户实现思路多租户(Multi - Tenancy)是一种软件架构技术,它允许多个租户(客户)共享同一套软件系统,同时又能保证各个租户的数据相互隔离。在若依框架中实现多租户,可以从以下几个方面入手: 数据库层面:采用租户标识区分不同租户的数据。 请求层面:在请求中携带租户标识,以便在处理请求时区分不同租户。 权限层面:不同租户的用户具有不同的权限。
2. 数据库设计修改 2.1 新增租户表在数据库中新增一个租户表,用于存储租户的基本信息。
CREATE TABLE `sys_tenant` ( `tenant_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '租户 ID', `tenant_name` varchar(100) NOT NULL COMMENT '租户名称', `tenant_code` varchar(50) NOT NULL COMMENT '租户编码', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户表'; 2.2 修改用户表在用户表中新增一个租户 ID 字段,用于关联租户。
ALTER TABLE `sys_user` ADD COLUMN `tenant_id` bigint(20) DEFAULT NULL COMMENT '租户 ID'; 3. 请求层面处理 3.1 拦截请求获取租户标识可以通过自定义拦截器或过滤器来获取请求中的租户标识,并将其存储在上下文中。
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class TenantFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String tenantId = httpRequest.getHeader("X-Tenant-ID"); if (tenantId != null) { // 将租户 ID 存储在上下文中 TenantContextHolder.setTenantId(Long.parseLong(tenantId)); } try { chain.doFilter(request, response); } finally { // 清除上下文 TenantContextHolder.clearTenantId(); } } } 3.2 租户上下文工具类创建一个租户上下文工具类,用于存储和获取当前请求的租户 ID。
public class TenantContextHolder { private static final ThreadLocal<Long> tenantIdThreadLocal = new ThreadLocal<>(); public static void setTenantId(Long tenantId) { tenantIdThreadLocal.set(tenantId); } public static Long getTenantId() { return tenantIdThreadLocal.get(); } public static void clearTenantId() { tenantIdThreadLocal.remove(); } } 4. 权限层面处理 4.1 修改 Spring Security 配置在 Spring Security 配置中,添加租户验证逻辑。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic() .and() .addFilterBefore(new TenantFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } } 4.2 修改权限验证逻辑在权限验证时,需要考虑租户的影响。例如,在查询用户权限时,需要根据租户 ID 进行过滤。
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; @Service public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Long tenantId = TenantContextHolder.getTenantId(); // 根据租户 ID 和用户名查询用户信息和权限 // ... return null; } } 5. 数据访问层面处理在数据访问层,需要在查询语句中添加租户 ID 条件,以确保不同租户的数据相互隔离。
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; @Mapper public interface UserMapper { @Select("SELECT * FROM sys_user WHERE username = #{username} AND tenant_id = #{tenantId}") List<User> findUsersByUsernameAndTenantId(String username, Long tenantId); } 6. 配置拦截器或过滤器在 Spring Boot 配置类中注册租户过滤器。
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FilterConfig { @Bean public FilterRegistrationBean<TenantFilter> tenantFilterRegistration() { FilterRegistrationBean<TenantFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new TenantFilter()); registration.addUrlPatterns("/*"); registration.setName("tenantFilter"); registration.setOrder(1); return registration; } }若依框架修改为多租户由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“若依框架修改为多租户”