Struts 2 拦截器(Interceptor)意外触发问题求助
问题
我将Web应用划分为三个访问区域:
- 登录操作区:无需特殊拦截器;
- 安全区:仅允许已认证用户访问,
AuthenticationInterceptor会将未认证用户重定向至登出操作; - 管理员区:仅允许管理员用户访问,
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,该栈本不应包含此拦截器,导致普通认证用户访问安全区时被强制登出,只有管理员能正常访问。
原因分析
问题根源有两点:
- 同命名空间配置覆盖:
numeritos-secure和numeritos-admin都未指定namespace属性,默认使用空命名空间。Struts2中,同命名空间下后加载的包会覆盖之前包的默认拦截器栈配置,因此numeritos-admin设置的numeritosAdminStack会覆盖numeritos-secure原本的numeritosDefaultStack,导致安全区的Action也执行管理员拦截器。 - 不合理的继承关系:
numeritos-admin继承了numeritos-secure,而非直接继承numeritos-default,这不仅不符合“管理员区是独立特殊区域”的设计意图,还加剧了同命名空间下的配置冲突风险。
解决方法
- 为每个功能包指定独立命名空间,避免同命名空间下的配置覆盖;
- 修正
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
相关产品推荐
相关产品推荐

