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

如何将自定义Rest Shiro过滤器迁移至shiro.ini并配置API会话超时

技术问题与配置需求

初始问题

  • 能否将旧的Rest过滤器移植至shiro.ini中配置?
  • 在shiro.ini中初始化过滤器后,能否通过Java代码扩展其doFilter()方法?

注:当前遇到错误处理异常,疑似Shiro Bug,无需协助编写代码,仅需方向指引。

现有代码与配置

MyRestFilter的doFilter()实现

@Override
@SneakyThrows(Throwable.class)
public void doFilter(final HttpServletRequest request, final HttpServletResponse response, 
        HttpSession httpSession, final FilterChain chain) throws ServletException, IOException
{
    debugRequest(request);
    List<String> sessionIds = Servlets.getRequestQueryStringMap(request).get(SESSION_ID);
    String sessionId = sessionIds == null? null : Iterables.getOnlyElement(sessionIds);
    String sessionIdHeader = request.getHeader(SESSION_ID_HEADER);
    if(sessionId == null)
    {
        sessionId = sessionIdHeader;
    }
    ThreadContext.unbindSubject();
    ThreadContext.bind(securityManager);
    if(sessionId != null)
    {
        // strip out host / instance ID from the session
        sessionId = sessionId.replaceFirst("\\.[\\w_\\-+]+$", "");
        Subject subject = new Subject.Builder().sessionId(sessionId).buildSubject();
        Session session = subject.getSession(false);
        if(session != null)
        {
            session.touch();   // this should not be necessary, but due to Shiro bug, it is
            StatefulUtil.pingStateful(session);
        }
        if(subject.getPrincipal() == null)
        {
            response.sendError(HttpStatus.SC_UNAUTHORIZED, "Unauthorized API Call");
        }
        else
        {
            try
            {
                subject.execute(() ->
                {
                    SharedWebstats webStats = Beans.getReference(SharedWebstats.class);
                    webStats.webStats("mobile");

                    if (chain != null)
                    {
                        chain.doFilter(request, response);
                    }
                    return null;
                });
            }
            catch(ExecutionException e)
            {
                // unwrap Shiro's ExecutionException, interferes
                // with global exceptino handling mechanisms
                Throwable cause = e.getCause();
                if (cause != null)
                {
                    throw cause;
                } 
                else
                {
                    throw e;
                }
            }
        }
    }
    else
    {            
        chain.doFilter(request, response);
    }
    ThreadContext.unbindSecurityManager();
}

MyRestFilter的init()实现(存在会话超时配置问题)

@Override
public void init() throws ServletException
{
    
    //securityManager = new WebIniSecurityManagerFactory("classpath:META-INF/shiro.ini").createInstance();
    securityManager = WebIniSecurityManagerFactory("classpath:META-INF/shiro.ini").createInstance();
    int sessionTimeout = WebXml.INSTANCE.getSessionTimeout();
    DefaultSecurityManager dsm = (DefaultSecurityManager)securityManager;
    DefaultSessionManager sm = (DefaultSessionManager)dsm.getSessionManager();
    sm.setGlobalSessionTimeout(sessionTimeout * 60 * 1000);
    realmCache.putAll(securityManager);
    ssse.addSessionListeners(sm.getSessionListeners());
}
            
private org.apache.shiro.mgt.SecurityManager securityManager;
private @Inject RealmCacheHolder realmCache;
private @Inject ShiroSessionScopeExtension ssse;

当前Web.xml配置

<filter>
        <filter-name>ShiroFilter</filter-name>
        <filter-class>com.dependencyfromhell.shiro.ee.filters.ShiroFilter</filter-class>
        <async-supported>true</async-supported>
    </filter>
    <context-param>
        <param-name>shiroConfigLocations</param-name>
        <param-value>classpath:META-INF/shiro.ini</param-value>
    </context-param>
        
    <!-- Security - Enforce Filter Ordering -->
    <filter-mapping>
        <filter-name>ShiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher> 
        <dispatcher>FORWARD</dispatcher> 
        <dispatcher>INCLUDE</dispatcher> 
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>
    <filter-mapping>
        <filter-name>MyRestFilter</filter-name>
        <url-pattern/>
    </filter-mapping>

期望的shiro.ini配置

# rest support
rest = MyRestFilter

#Rest api
/api/* = MyRestFilter

核心疑问

能否在shiro.ini中配置MyRestFilter的会话超时,同时保留Java类中的doFilter()逻辑?


方向指引

1. 过滤器移植至shiro.ini的可行性

完全可行。Shiro支持在ini文件中配置自定义过滤器:

  • 确保MyRestFilter继承自Shiro的AccessControlFilter/PathMatchingFilter,或实现javax.servlet.Filter并适配Shiro过滤器规范。
  • 按你期望的ini格式配置即可,还能通过rest.paramName=paramValue的方式给过滤器注入配置参数。

2. 保留doFilter()逻辑并扩展

可以完全保留原有逻辑:

  • 配置到shiro.ini后,Java类的doFilter()逻辑会正常执行,无需修改。
  • 若要扩展,直接修改Java类的doFilter()方法即可,ini配置仅负责实例化过滤器和绑定路径,不影响代码逻辑。

3. 会话超时的配置方案(针对/api/路径)

Shiro全局会话超时无法按路径区分,推荐以下两种方案:

  • 方案一:在过滤器中动态设置
    删除init()中设置全局会话超时的代码,在doFilter()中判断请求路径,匹配/api/*时单独设置会话超时:
    if(session != null && request.getRequestURI().startsWith("/api/")){
        session.setTimeout(customApiTimeout * 60 * 1000);
    }
    
    其中customApiTimeout可通过ini注入:给MyRestFilter添加setter方法,在ini中配置rest.customApiTimeout=30。
  • 方案二:多SessionManager配置
    为Rest路径单独配置SessionManager,通过Shiro路径规则绑定,但实现复杂度较高,优先推荐方案一。

4. 错误处理异常排查方向

  • 检查Shiro版本是否存在已知的ExecutionException相关Bug,尝试升级到最新稳定版。
  • 确认全局异常处理器能否正确捕获过滤器抛出的异常,重点排查异常抛出后是否被其他拦截器拦截或丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 06:05:23