You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过@PathVariable在Spring Security中实现多企业独立会话管理?

实现Spring Security按企业路径独立会话的方案

核心思路

浏览器对同域名下的Cookie默认是全局共享的,Spring Security默认的JSESSIONID Cookie路径为根路径/,导致不同企业路径会共用同一个会话。要实现会话隔离,核心是让每个/{company}/路径下的会话Cookie仅对当前企业路径生效,同时让Spring Security能识别不同路径的会话,互不干扰。

具体实现步骤

1. 自定义会话Cookie的路径

通过CookieSerializer动态设置Cookie的路径,根据当前请求的company路径变量生成专属的Cookie路径,确保不同企业的Cookie仅在自身路径下有效。

@Bean
public CookieSerializer cookieSerializer() {
    DefaultCookieSerializer serializer = new DefaultCookieSerializer();
    // 从请求URL中提取company,设置对应的Cookie路径
    serializer.setCookiePath(request -> {
        String requestUri = request.getRequestURI();
        Matcher matcher = Pattern.compile("^/([^/]+)/").matcher(requestUri);
        if (matcher.find()) {
            return "/" + matcher.group(1) + "/";
        }
        // 非企业路径默认用根路径
        return "/";
    });
    return serializer;
}

2. 配置Spring Security会话管理

在SecurityFilterChain中配置会话策略,允许同一用户在不同企业路径下创建多个独立会话,同时注入自定义的CookieSerializer。

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/{company}/**").authenticated()
            .anyRequest().permitAll()
        )
        .formLogin(form -> form
            .loginPage("/login")
            .permitAll()
        )
        .sessionManagement(session -> session
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            // 允许同一用户创建无限多会话(适配多企业场景)
            .maximumSessions(Integer.MAX_VALUE)
            .sessionRegistry(sessionRegistry())
        )
        .csrf(csrf -> csrf.disable()); // 实际场景按需开启CSRF

    // 注入自定义Cookie序列化器
    http.setSharedObject(CookieSerializer.class, cookieSerializer());
    return http.build();
}

@Bean
public SessionRegistry sessionRegistry() {
    return new SessionRegistryImpl();
}

3. 企业数据与会话的绑定

每个企业拥有独立数据集,需要确保用户登录后的权限、数据访问与当前企业绑定:

  • 自定义UserDetails,添加company字段存储用户所属企业
  • 登录时从请求路径提取company,查询对应企业的用户信息
  • 请求拦截时校验当前路径的company与用户会话中的company是否一致,防止越权

示例自定义UserDetails:

public class CustomUserDetails implements UserDetails {
    private String username;
    private String password;
    private String company;

    public CustomUserDetails(String username, String password, String company) {
        this.username = username;
        this.password = password;
        this.company = company;
    }

    public String getCompany() {
        return company;
    }

    // 实现UserDetails的其他必填方法(省略)
}

示例UserDetailsService实现:

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String company = extractCompanyFromRequest(request);
        // 根据username+company查询对应企业的用户数据(模拟数据库查询)
        return new CustomUserDetails(username, "{encodedPassword}", company);
    }

    private String extractCompanyFromRequest(HttpServletRequest request) {
        String requestUri = request.getRequestURI();
        Matcher matcher = Pattern.compile("^/([^/]+)/").matcher(requestUri);
        if (matcher.find()) {
            return matcher.group(1);
        }
        throw new IllegalArgumentException("无效的企业路径");
    }
}

4. 验证效果

  • 打开两个标签页,分别访问http://www.example.com/company1/home和http://www.example.com/company2/home
  • 用不同企业的用户分别登录,查看浏览器Cookie:两个标签页的JSESSIONID Cookie路径分别为/company1/和/company2/,互不共享
  • 操作其中一个标签页的会话,另一个标签页的会话不受影响,各自保持活跃

关键注意事项

  • Cookie路径匹配规则:浏览器只会将Cookie发送到匹配路径的请求中,确保不同企业的会话Cookie不会跨路径干扰
  • 会话注册表作用:SessionRegistry会跟踪所有活跃会话,避免同一用户在不同企业路径下的会话被误踢下线
  • 越权防护:必须校验用户会话中的company与当前请求路径的company一致,防止用户跨企业访问数据

内容的提问来源于stack exchange,提问作者Mehdi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:17:44