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

Glassfish 7.0.16中并发Ajax请求出现403 Forbidden问题排查

并发请求下Jakarta Security出现403 Forbidden的排查与解决

核心原因分析

单请求正常但并发场景出现随机403,且请求的Principal和会话属性完全一致,大概率是以下两类问题导致:

  1. 过滤器注入SecurityContext的线程安全隐患
    Servlet Filter默认是单例实例,若直接@Inject SecurityContext且未指定正确作用域,会导致多个并发请求共享同一个SecurityContext实例。Glassfish中的SecurityContext是绑定到当前请求线程的,单例过滤器里的注入实例无法随请求动态切换,并发时会拿到其他请求的上下文,进而导致角色判断错误。

  2. Glassfish角色缓存的并发同步漏洞
    Glassfish默认会缓存会话中的角色信息以提升性能,但并发场景下,会话的角色缓存可能存在线程同步问题,导致部分请求读取到未正确初始化的角色数据——即便Principal正确,角色校验仍会失败。

  3. 过滤器执行顺序问题
    如果自定义过滤器在Jakarta Security的内置安全过滤器之前执行,此时SecurityContext可能还未完成初始化,并发下不同请求的初始化时机差异会引发随机403。


解决方案

1. 修正SecurityContext的获取方式(优先推荐)

不要在过滤器中直接注入SecurityContext,改为从当前请求中动态获取:

public class AuthorizationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        // 从请求属性中获取当前请求绑定的SecurityContext
        SecurityContext sc = (SecurityContext) req.getAttribute("jakarta.security.auth.message.SecurityContext");
        
        boolean hasRole = req.isUserInRole("ROLE_A");
        // 双重校验,兼容不同场景
        if (sc != null) {
            hasRole = hasRole || sc.isCallerInRole("ROLE_A");
        }

        if (hasRole) {
            chain.doFilter(request, response);
        } else {
            // 日志打印调试信息
            System.out.printf("Forbidden - Principal: %s, Session ID: %s%n", 
                    req.getUserPrincipal(), req.getSession().getId());
            res.sendError(HttpServletResponse.SC_FORBIDDEN);
        }
    }

    // init、destroy方法按需实现
}

2. 调整过滤器执行顺序

确保自定义过滤器在Jakarta Security的内置过滤器之后执行:

  • 若使用web.xml配置,显式定义过滤器映射,保证顺序在安全约束之后:
<filter>
    <filter-name>AuthorizationFilter</filter-name>
    <filter-class>com.yourpackage.AuthorizationFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>AuthorizationFilter</filter-name>
    <url-pattern>/your-protected-path/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>
  • 若使用@WebFilter注解,添加@Order(100)(数值越大执行越晚,确保在安全过滤器之后):
@WebFilter(urlPatterns = "/your-protected-path/*")
@Order(100)
public class AuthorizationFilter implements Filter {
    // ...
}

3. 禁用Glassfish角色缓存

修改Glassfish的domain.xml配置,禁用角色缓存以规避并发同步问题:

<security-service>
    <auth-realm name="your-jdbc-realm" classname="com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm">
        <!-- 你的JDBC Realm其他配置 -->
        <property name="roleCacheEnabled" value="false"></property>
    </auth-realm>
</security-service>

注意:禁用缓存会增加数据库查询次数,需评估性能影响。

4. 升级Glassfish版本

Glassfish 7.0.16可能存在Jakarta Security的并发处理bug,尝试升级到最新稳定版本(如7.0.17及以上),官方可能已修复此类问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:27:46