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

Struts 2 拦截器(Interceptor)意外触发问题求助

问题

我将Web应用划分为三个访问区域:

  1. 登录操作区:无需特殊拦截器;
  2. 安全区:仅允许已认证用户访问,AuthenticationInterceptor会将未认证用户重定向至登出操作;
  3. 管理员区:仅允许管理员用户访问,AdministrationInterceptor会将非管理员用户重定向至登出操作。

对应地将Action划分到不同Struts包中:

  • numeritos-default:无Action,仅定义拦截器和拦截器栈,供其他包继承;
  • numeritos-login:直接继承struts-default,使用Struts默认拦截器栈;
  • numeritos-secure:继承numeritos-default,应使用其默认的numeritosDefaultStack;
  • numeritos-admin:继承numeritos-default,自定义默认拦截器栈,添加AdministrationInterceptor。

但实际运行时出现异常:numeritos-secure中的Action意外执行了AdministrationInterceptor,该栈本不应包含此拦截器,导致普通认证用户访问安全区时被强制登出,只有管理员能正常访问。

原因分析

问题根源有两点:

  1. 同命名空间配置覆盖:numeritos-secure和numeritos-admin都未指定namespace属性,默认使用空命名空间。Struts2中,同命名空间下后加载的包会覆盖之前包的默认拦截器栈配置,因此numeritos-admin设置的numeritosAdminStack会覆盖numeritos-secure原本的numeritosDefaultStack,导致安全区的Action也执行管理员拦截器。
  2. 不合理的继承关系:numeritos-admin继承了numeritos-secure,而非直接继承numeritos-default,这不仅不符合“管理员区是独立特殊区域”的设计意图,还加剧了同命名空间下的配置冲突风险。

解决方法

  1. 为每个功能包指定独立命名空间,避免同命名空间下的配置覆盖;
  2. 修正numeritos-admin的继承关系,让它直接继承numeritos-default,确保配置层级清晰。

修正后的完整配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">

<struts>

    <constant name="struts.custom.i18n.resources" value="global-messages" />
    
    <constant name="struts.devMode" value="true" />
    
    <constant name="struts.ui.theme" value="simple" />
    
    <package name="numeritos-default" extends="struts-default">
        <interceptors>
        
            <!-- Session authentication interceptor -->
            <interceptor name="authenticationInterceptor" class="authenticationInterceptor"/>

            <!-- Administration privileges interceptor -->
            <interceptor name="administrationInterceptor" class="administrationInterceptor"/>  
            
            <!-- Caching headers interceptor -->
            <interceptor name="cachingHeadersInterceptor" class="cachingHeadersInterceptor"/>
        
            <!-- Default interceptor stack for this package (default + authentication) -->  
            <interceptor-stack name="numeritosDefaultStack">
                <interceptor-ref name="authenticationInterceptor"/>
                <interceptor-ref name="cachingHeadersInterceptor"/>             
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>
            
            <!-- Default interceptor stack for administration packages (default + administration) -->   
            <interceptor-stack name="numeritosAdminStack">
                <interceptor-ref name="administrationInterceptor"/>
                <interceptor-ref name="numeritosDefaultStack"/>
            </interceptor-stack>            
                    
        </interceptors>
        
        <!-- Secure interceptor stack as default -->
        <default-interceptor-ref name="numeritosDefaultStack"/>
                
    </package>

    <package name="numeritos-login" extends="struts-default">
        
        <action name="welcome" class="welcomeAction">
            <result name="success" type="freemarker">ftl/login.ftl</result>
        </action>
    
        <action name="login" class="loginAction">
            <result name="success" type="redirectAction">/secure/exerciseLoad</result>
            <result name="input" type="freemarker">ftl/login.ftl</result>
        </action>
        
    </package>
    
    <!-- 添加独立命名空间 /secure -->
    <package name="numeritos-secure" extends="numeritos-default" namespace="/secure">
        
        <!-- Common login result -->        
        <global-results>
             <result name="login" type="redirectAction">/login/logout</result>
        </global-results>
        
        <action name="exerciseLoad" class="exerciseLoadAction">
            <result name="simple_op" type="freemarker">ftl/simple_operation_view.ftl</result>
            <result name="equation" type="freemarker">ftl/equation_view.ftl</result>
        </action>
        
        <action name="exerciseCheck" class="exerciseCheckAction">
            <result name="success" type="freemarker">ftl/show_exercise_result.ftl</result>
            <result name="input" type="freemarker">ftl/answer_format_error.ftl</result>
        </action>
        
        <action name="stats" class="statsAction">
            <result name="success" type="freemarker">ftl/stats.ftl</result>
        </action>
        
        <action name="modulesList" class="modulesListAction">
            <result name="success" type="freemarker">ftl/modules_list.ftl</result>
        </action>
        
        <action name="selectModule" class="selectModuleAction">
            <result name="input" type="freemarker">ftl/modules_list.ftl</result>
            <result name="success" type="redirectAction">exerciseLoad</result>
        </action>        
        
        <action name="userSettings" class="userSettingsAction">
            <result name="success" type="freemarker">ftl/user_settings.ftl</result>
        </action>
        
        <action name="changePassword" class="changePasswordAction">
            <result name="success" type="freemarker">ftl/user_settings.ftl</result>
            <result name="input" type="freemarker">ftl/user_settings.ftl</result>
        </action>
        
        <action name="logout" class="logoutAction">
            <result name="success" type="freemarker">ftl/login.ftl</result>        
        </action>        
          
    </package>
    
    <!-- 修改继承关系为numeritos-default,添加独立命名空间 /admin -->
    <package name="numeritos-admin" extends="numeritos-default" namespace="/admin">
    
        <!-- Admin interceptor stack as default -->
        <default-interceptor-ref name="numeritosAdminStack"/>
        
        <!-- Common login result -->        
        <global-results>
             <result name="login" type="redirectAction">/secure/logout</result>
        </global-results>
        
        <action name="adminConsole" class="adminConsoleAction">
            <result name="success" type="freemarker">ftl/admin_console_results.ftl</result>
        </action>   
        
        <action name="adminConsoleGroups" class="adminConsoleGroupsAction">
            <result name="success" type="freemarker">ftl/admin_console_groups.ftl</result>
        </action>
        
        <action name="adminCreateGroup" class="adminConsoleGroupsAction" method="create">
            <result name="success" type="redirectAction">adminConsoleGroups</result>
        </action> 
        
        <action name="adminDeleteGroup" class="adminConsoleGroupsAction" method="delete">
            <result name="success" type="redirectAction">adminConsoleGroups</result>
        </action>        
              
    </package>
    
    <package name="json-admin" extends="numeritos-default, json-default" namespace="/json-admin">
        <interceptors>
            <!-- Default interceptor stack for this package (default + administration + json) -->   
            <interceptor-stack name="numeritosJsonAdminStack">
                <interceptor-ref name="administrationInterceptor"/>
                <interceptor-ref name="json"/>
                <interceptor-ref name="numeritosDefaultStack"/>
            </interceptor-stack>        
        </interceptors>
        
        <default-interceptor-ref name="numeritosJsonAdminStack"/>     
                
        <action name="jsonTest" class="jsonTestAction">
            <result name="success" type="json">
                <param name="root">users</param>
            </result>
        </action>       
    </package> 

</struts>

关键修改说明

  • 为numeritos-secure添加namespace="/secure",numeritos-admin添加namespace="/admin",json-admin添加namespace="/json-admin",确保各包配置独立;
  • 将numeritos-admin的继承关系从numeritos-secure改为numeritos-default,符合设计意图;
  • 修正跨命名空间的跳转路径(如login的success跳转改为/secure/exerciseLoad),确保跳转正确。

内容的提问来源于stack exchange,提问作者Noé Otero Mateo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:06:23