新闻详情

新闻详情

首页 / 资讯中心 / 详情

Struts2实战:从登录注册到用户信息管理系统的完整开发解析

发布时间:2026/9/25 8:15:00来源:尧图网络
Struts2实战:从登录注册到用户信息管理系统的完整开发解析
简介一套基于Struts2实现的、可运行的登录注册及用户信息管理系统项目包面向Java Web初学者及需要快速搭建经典MVC模块的开发者。项目完整演示了用户登录、注册、信息维护等基础且重要的业务场景涵盖Struts2的Action与Result映射、Interceptor拦截器、struts.xml全局配置、表单验证、异常处理、SQL Server数据库交互以及权限检查、数据校验、用户认证与授权、Session状态保持等核心知识点有助于理解从前端JSP、控制器到后端业务逻辑与数据库的完整调用链路。压缩包共74个文件整体仅4.27MB包含Java源码、JSP页面、XML配置、class编译产物、JAR依赖库、gif/jpg图片、SQL Server数据库文件mdf/ldf及一份便于辅助学习的图解文档目录按源码、数据库、WebRoot等模块清晰划分便于导入MyEclipse等IDE直接运行学习。目前已有843人学习下载尤其适合作为课程设计、毕业设计或Struts2框架入门进阶的参考资料也适合用来巩固MVC分层与Web开发基础。1. 老框架还能做新项目吗一个Struts2认证管理系统的真实价值提起 Struts2很多人的第一反应是“古董”“漏洞多”“早该被 Spring MVC 淘汰”。但在课程设计、毕业设计、老系统维护和部分国企内部项目里Struts2 依然占着不小的存量。我就是从 2016 年开始接触这类项目的那时候最头疼的不是功能写不出来而是网上教程全是 2012 年左右的古董配置照着抄常常在版本兼容性上翻车。更要命的是Struts2 每隔一阵就爆一个远程代码执行漏洞S2-029、S2-045、S2-046 这些编号听起来就像定时炸弹。本文要讲的登录注册及用户信息管理系统恰好是 Struts2 最典型的使用场景——表单处理、请求参数绑定、拦截器做登录校验、OGNL 取值。把这些跑明白你不仅能交出一份能答辩的项目还能真正看懂 Struts2 的运作机制和漏洞成因。适合正在做课设、接手老项目、或者想补 Struts2 原理坑的人。2. 先把 Struts2 的运行机制讲透Action 映射、拦截器栈与 OGNL 的实际用途2.1 为什么这个项目还值得用 Struts2 做从 MVC 演进看选型理由很多新人问既然 Spring MVC 那么成熟为什么还要折腾 Struts2答案在于“存量”。大量 2010 到 2015 年之间上线的企业系统前端用的是 JSP后端就是 Struts2 Spring Hibernate 这套 SSH 组合。这些系统至今仍在运行需要有人能维护、改功能。所以我说学 Struts2 不是学“最新的技术”而是学“怎么跟老系统打交道”。从 MVC 演进的角度看Struts2 的核心思路其实很清晰浏览器请求先经过一个总入口过滤器 StrutsPrepareAndExecuteFilter然后根据 URL 找到对应的 Action 类Action 执行完返回一个字符串比如 success、error框架再根据这个字符串决定跳转到哪个 JSP。这个模型比原始的 Servlet 写 out.println 要优雅得多也奠定了后来所有 MVC 框架的基础。理解这一层后面配置 struts.xml 就不会觉得是死记硬背了。再说选型理由的更直接层面Struts2 的拦截器机制在登录校验、权限控制、参数自动封装这些场景下确实做到了“约定优于配置”。就拿参数绑定来说表单里提交上来Struts2 会自动调用 Action 的 setUser() 方法把 username 值填进 user 对象的属性里全程不需要手动 request.getParameter。这种能力在 2010 年前后是非常惊艳的。今天的登录注册系统用 Spring MVC 写本质上干的也是同一件事但如果你要维护的是老代码就必须得理解 Struts2 这一套写法。2.2 最小可运行配置web.xml、struts.xml 与 Action 映射的关系先搭一个最简骨架。Struts2 应用本质是个 Web 应用所以 web.xml 里必须声明那个核心过滤器它的路径映射决定了哪些 URL 会进入 Struts2 框架。下面是最常见的配置!-- web.xml 关键片段 -- filter filter-namestruts2/filter-name filter-classorg.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter/filter-class /filter filter-mapping filter-namestruts2/filter-name url-pattern/*/url-pattern /filter-mapping这里要特别注意实现类在不同版本的 Struts2 里位置不一样2.5 之前是 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter2.5 之后把 ng 去掉了变成 org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter。配置报 ClassNotFound 时先查这里。url-pattern 用 /*意味着所有请求都会过过滤器但静态资源比如 CSS、JS 也会被拦所以 struts.xml 里要配一个忽略静态资源的常量。下面的 struts.xml 是最小可用版本?xml version1.0 encodingUTF-8? !DOCTYPE struts PUBLIC -//Apache Software Foundation//DTD Struts Configuration 2.5//EN http://struts.apache.org/dtds/struts-2.5.dtd struts constant namestruts.devMode valuetrue / constant namestruts.action.extension valueaction / package namedefault namespace/ extendsstruts-default action nameuser_* classcom.demo.action.UserAction method{1} result namesuccess/WEB-INF/pages/success.jsp/result result nameerror/WEB-INF/pages/error.jsp/result /action /package /strutsstruts.devMode 设为 true 后修改配置文件会立即生效方便开发调试但生产环境必须关掉否则等于把系统信息全暴露出去。struts.action.extension 定义 action 后缀访问 /user_login.action 才会匹配到 user_*注意这个通配符映射很常用——一个 UserAction 里写了 login、register、list 多个方法URL 里下划线后面的单词会通过 {1} 传给 method。class 指向 Action 类result 里的 name 对应方法返回的字符串。2.3 OGNL 表达式在表单绑定里的两个常用场景OGNLObject-Graph Navigation Language是 Struts2 的最大特色也是后来出漏洞最多的点。日常开发里OGNL 主要体现在两个场景。第一个是 JSP 页面取值在 success.jsp 里写 ${user.username}看着像 EL 表达式实际 Struts2 会把 OGNL 的 ValueStack 顶部的值对象按属性名去取这就是为什么 Action 里写了 setUsername 之后页面直接能显示。第二个是表单回显用户注册失败返回到注册页时输入框里写之类的写法用的是 Struts2 标签库% taglib prefixs uri/struts-tags % s:form actionuser_register methodpost s:textfield nameuser.username label用户名 / s:password nameuser.password label密码 / s:submit value注册 / /s:form这里一个常见误区是新手以为 s:textfield 里的 name 只是普通的 HTML name 属性其实 Struts2 会用 OGNL 表达式把它解析成属性访问user.username 等价于 get 到 Action 的 user 对象后调 getUser().setUsername()前提是 Action 里有个 user 属性且已实例化。页面上 s:form 的 action 写的是不带后缀的逻辑名框架自动补 .action。OGNL 表达式如果写错最常见的报错是 Unable to load configuration 或者 property not found排查方向就是检查 user 对象有没有 getter/setter、有没有初始化。3. 用 Struts2 实现登录注册从表单校验到 Session 管理的完整链路3.1 用户表设计与密码存储加盐 MD5 而不是裸 MD5正式动手写 Action 之前先把数据库这块定下来。用户信息管理系统最核心的表就是用户表字段一般包括用户 ID、用户名、密码、姓名、邮箱、手机号、创建时间。密码存储是第一个坑很多老教程直接明文存储或者只做一次 MD5这在当年是常态但现在这么做等于把用户数据裸奔。正确做法是加盐哈希每个用户生成一个随机盐值存盐值和哈希结果两个字段。用 MD5 做加盐虽然谈不上最安全但作为课设和内部系统足够而且不会引入额外的加密库。用户表结构如下CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, salt VARCHAR(32) NOT NULL, real_name VARCHAR(50) DEFAULT , email VARCHAR(100) DEFAULT , mobile VARCHAR(20) DEFAULT , create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;username 加 UNIQUE 约束是为了让数据库层兜底防止重复注册。password 字段长度设 64因为“盐 明文密码”做 MD5 后是 32 位十六进制再存一份盐值 32 位分开存。create_time 用默认值省得手动填。需要注意的是MySQL 5.7 之前不支持 DATETIME 的 DEFAULT CURRENT_TIMESTAMP如果用的老版本就得在插入时显式设置时间。字符集用 utf8mb4 而不是 utf8因为 utf8 在 MySQL 里存不了 emoji 和个别生僻字后期改表很痛苦。3.2 注册 Action字段校验、重复用户名检查与重定向注册功能的逻辑链路是接收表单参数 → 基本非空校验 → 查重 → 加盐哈希 → 插入用户 → 返回成功页。Struts2 的 Action 类一般不直接写 JDBC但课设项目用原生 JDBC 反而更好讲清楚链路。我把注册方法写成下面这样注意看参数是怎么进来的public class UserAction extends ActionSupport { private User user; private String tip; private UserService userService new UserService(); public String register() throws Exception { if (user null) { tip 提交数据不能为空; return ERROR; } if (!validateRegister(user)) { tip getFieldErrors().toString(); return ERROR; } boolean exists userService.isUsernameExists(user.getUsername()); if (exists) { tip 用户名已被注册; return ERROR; } // 生成随机盐并计算加盐MD5 String salt UUID.randomUUID().toString().replace(-, ).substring(0, 8); String hashed DigestUtils.md5Hex(salt user.getPassword()); user.setSalt(salt); user.setPassword(hashed); userService.insertUser(user); return SUCCESS; } private boolean validateRegister(User u) { if (u.getUsername() null || u.getUsername().length() 3) return false; if (u.getPassword() null || u.getPassword().length() 6) return false; return true; } }逻辑说明User 对象是 Action 的属性表单里的 user.username、user.password 会通过 OGNL 自动填充所以 register() 里直接判断 user 是否为 null 和字段长度。DigestUtils 是 commons-codec 提供的工具类比手写 MessageDigest 少很多样板代码。isUsernameExists 对应 SQL 是 SELECT COUNT(*) FROM t_user WHERE username?。insertUser 用 PreparedStatement 防止 SQL 注入这个细节在答辩时也是加分项。返回值 SUCCESS 对应 struts.xml 里的 success result这里有个重要原则注册成功后要“重定向”而不是“转发”否则用户按 F5 刷新会重复提交表单。所以 struts.xml 里 success 的 result 应该写 redirect 到登录页action nameuser_register classcom.demo.action.UserAction methodregister result namesuccess typeredirect/user_login.action/result result nameerror/WEB-INF/pages/register.jsp/result /actiontyperedirect 会让浏览器发起一次新的 GET 请求到 /user_login.action地址栏变成登录页 URL刷新不会重新提交 POST 数据。这个方法新手最容易漏结果答辩演示时一按 F5 数据库里就多出一堆重复用户。3.3 登录 Action查询验证、Session 写入与登录拦截器登录的逻辑和注册对称接收用户名密码 → 按用户名查出用户 → 用盐值重新计算哈希 → 比对 → 写入 Session → 跳转用户列表页。这里最关键的是 Session 的写入方式。Struts2 里有两种常见做法一种是让 Action 实现 SessionAware 接口由框架注入一个 Map 类型的 session另一种是直接用 ServletActionContext.getRequest().getSession()。我更推荐前者因为它让 Action 不直接依赖 Servlet API单元测试更方便。下面用第一种public class UserAction extends ActionSupport implements SessionAware { private User user; private MapString, Object session; private UserService userService new UserService(); public void setSession(MapString, Object session) { this.session session; } public String login() throws Exception { User dbUser userService.findByUsername(user.getUsername()); if (dbUser null) { tip 用户不存在; return ERROR; } String hashed DigestUtils.md5Hex(dbUser.getSalt() user.getPassword()); if (!hashed.equals(dbUser.getPassword())) { tip 密码错误; return ERROR; } session.put(loginUser, dbUser); return SUCCESS; } }setSession 是框架在 Action 执行前自动调用的参数 map 的 key 是字符串、value 是任意对象底层对应 HTTP Session。session.put(loginUser, dbUser) 之后后续 Action 和 JSP 页面里都能通过 #session.loginUser 取到这个用户对象。需要注意的是从数据库查出来后dbUser 的 password 字段存的是加盐哈希值把它塞进 Session 其实有一点信息暴露风险虽然哈希值不能直接反推出明文但在内部系统里稳妥起见还是应该手动把 password 和 salt 置空再存 Session。登录成功后拦截器要配合使用这部分我在第 6 章单独展开。4. 用户信息管理模块CRUD 操作、分页与列表页渲染4.1 DAO 层设计JDBC 还是 MyBatis用户信息管理系统的核心操作就是增删改查四件事加上一个列表分页。DAO 层选型上我的建议是课设和教学项目用原生 JDBC入职接老项目大概率会遇到 MyBatis 的 XML 配置。原生 JDBC 能让你看清每一次数据库交互的成本——每次 new Connection 都真的走一次网络和数据库认证握手。当然实际项目里没人这么干会配一个连接池比如 2010 年代常用的 dbcp 或 c3p0。写 JDBC 的 DAO 时有一个这么多年都没变的血泪经验所有资源都要在 finally 里关闭顺序是先关闭 ResultSet、再关闭 Statement、最后关闭 Connection反了会直接内存泄漏。下面是一个查询全部用户并分页的方法用的是 MySQL 的 LIMIT 语法public ListUser findPage(int pageNum, int pageSize) { String sql SELECT id, username, real_name, email, mobile, create_time FROM t_user ORDER BY id DESC LIMIT ?, ?; ListUser list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (pageNum - 1) * pageSize); ps.setInt(2, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { User u new User(); u.setId(rs.getInt(id)); u.setUsername(rs.getString(username)); u.setRealName(rs.getString(real_name)); u.setEmail(rs.getString(email)); u.setMobile(rs.getString(mobile)); u.setCreateTime(rs.getTimestamp(create_time)); list.add(u); } } } catch (Exception e) { e.printStackTrace(); } return list; }好多新手写 LIMIT 分页会跳过第 1 页原因是 LIMIT 0, 5 才是第一页而 pageNum 默认从 1 开始所以 (pageNum - 1) * pageSize 这个偏移量计算是必须的。另外注意我查询时没有 SELECT password 和 salt这两个字段不属于列表页展示数据查出来纯属浪费内存。try-with-resources 语法是 Java 7 之后推荐的写法它自动帮你关闭实现了 AutoCloseable 的类代码干净很多老项目如果还在 JDK 6 就得改成 finally 手动关闭。4.2 用户列表分页Limit 参数与 PageBean 的实现分页不只是 SQL 那条 LIMIT还需要知道总记录数来算总页数所以 DAO 里配套一个 count 查询。我通常会做一个 PageBean 来封装这些避免 JSP 页面里写一堆裸变量。PageBean 的字段很固定当前页、每页条数、总条数、总页数、当前页的数据列表。总条数用 SELECT COUNT(*) FROM t_user 查询注意这条语句不要带 ORDER BY带了反而增加数据库排序开销。总页数的计算公式是总页数 (总条数 每页条数 - 1) / 每页条数用整除避免小数。public class PageBeanT { private int pageNum; private int pageSize; private int totalCount; private int totalPages; private ListT list; public PageBean(int pageNum, int pageSize, int totalCount, ListT list) { this.pageNum pageNum; this.pageSize pageSize; this.totalCount totalCount; this.totalPages (totalCount pageSize - 1) / pageSize; this.list list; } }这里有一个边界坑当用户手动修改 URL 里的 pageNum 为一个超大值比如 ?pageNum99999此时查询结果为空但页面应该显示“没有更多数据”而不是直接报错。处理办法是在 Action 里对 pageNum 做一次校正超出总页数时回退到最后一页小于 1 时回到第一页。类似地pageSize 不要放开给前端传固定一个值或者校验最大值否则有人把 pageSize 传成 100000 会把你数据库拖垮。分页的 JSP 渲染老项目里用的是 JSTL 的 c:forEach 配合循环显示页码。这里有个 Struts2 的老毛病Action 暴露给 JSP 的属性默认都在 ValueStack 顶部所以 JSP 里可以直接写 ${pageBean.list} 取列表但是如果要遍历并访问 user.username 这样的嵌套属性必须保证 User 类有对应的 getter这个不用我多说了吧我见过太多 NoSuchProperty 的报错都是因为字段没写 getter。4.3 编辑与删除操作请求参数传递和 Struts2 的 setter 注入编辑和删除相对简单核心是按 ID 定位数据。删除的 URL 一般长这样user_delete.action?id12Struts2 会自动把 id 参数注入到 Action 的 setId 方法里所以 Action 里准备一个 int id 字段和 setId(int id) 就行了。但这里有个经典翻车点如果 id 参数在请求里丢失或者转成了非法数字Struts2 的默认类型转换会直接报错误页面而不是给个友好的提示。所以生产环境代码会在 Action 里批量注册一个类型转换拦截器的错误处理器或者干脆用 String 接收 id再手动 Integer.parseInt 并捕获异常。删除方法里我通常会做一个关联判断如果用户下存在业务数据比如订单、文章不给删而要提示“存在关联数据不能删除”。这属于业务逻辑的完整性不是数据库外键约束因为很多老系统设计时根本没有把外键建起来。更新操作和编辑共用一个页面edit 方法负责查出当前记录回显到表单update 方法接收表单提交。注意 Struts2 回显时有个坑如果你把 user 对象整个塞到 request 域JSP 里用 ognl 取值可能要写 s:textfield nameuser.username value%{user.username} /这里的 %{} 是 OGNL 的强制表达式语法不是普通字符串。这个细节在 3.2 我提过现在是真的要用了s:textfield nameuser.username value%{user.username} label用户名 readonlytrue / s:textfield nameuser.realName value%{user.realName} label姓名 / s:textfield nameuser.email value%{user.email} label邮箱 /注意一个细节编辑回显时 username 我加了 readonly因为用户名一般是登录标识不允许随便改。如果业务上确实允许改那 update 时要重新检查唯一性否则两个用户可能改成同一个名字数据库层的 UNIQUE 约束会直接抛异常。5. 安全防范与踩坑记录S2-029 漏洞原理和框架的常见问题5.1 S2-029 远程代码执行漏洞到底怎么发生的讲 Struts2 的安全一定绕不开连续爆雷的远程代码执行漏洞最典型的就是 S2-029。作为维护者你得先搞清楚这类漏洞的本质才不会看到漏洞编号就慌。S2-029 的根子在 Struts2 的 OGNL 表达式执行机制框架在处理某些特定标签的属性时会对用户输入的内容进行二次 OGNL 解析。如果攻击者把一段精心构造的 OGNL 表达式放在请求参数或者某个特定字段里框架就会以服务器权限执行任意代码。说白了问题不在 OGNL 本身而在“用户的输入被当成代码执行了”这是一个典型的表达式注入问题。S2-029 主要影响 Struts 2.0.x 到 2.3.28 等版本官方在 2.3.28.1 里做了修复。所以你看网上很多老代码教程带你配一个 2.3.24 的依赖还觉得“稳定”那放在今天就是拿着一把上了膛的枪在写代码。除了 S2-029还有 S2-045 用 Content-Type 头触发、S2-046 用文件名触发、S2-057 用 URL 路径触发都是同一家族的问题——OGNL 表达式在某个入口被二次解析。理解了这一点你就明白为什么官方一直在加强 OGNL 的沙箱机制为什么后来这些漏洞的利用条件越来越苛刻。我们日常维护老系统能做的有三层升级到已修复版本、关闭不必要的动态方法调用、给服务器加 WAF 拦截带 ognl 特征的请求。5.2 给老项目打补丁升级 Struts2 版本与加固 OGNL如果你手上的项目还是 Struts 2.3 老版本起步动作是把 Maven 里的 struts2-core 升到一个还在维护的 2.5.x 版本。别害怕升级Struts2 从 2.3 到 2.5 的 API 大部分兼容真正要改的是两件事一是 web.xml 里过滤器的类名要换成不带 ng 的全限定名二是 2.5 默认不允许通过 URL 直接调用 Action 的任意方法必须在 struts.xml 里显式声明 method。前者我在 2.2 提过后者很多教程没讲比如 2.5 里这样配置是可以的package namedefault namespace/ extendsstruts-default action nameuser_register classcom.demo.action.UserAction methodregister / action nameuser_login classcom.demo.action.UserAction methodlogin / action nameuser_list classcom.demo.action.UserAction methodlist / /package但如果你写的是 action nameuser_* ... 配合 {1} 传 method2.5 里默认会被拦掉报 Method xxx is not allowed。解决方式是在 struts.xml 里加 或包级别配置 。这个限制其实是好事等于把可调用的 Action 方法名单列清楚天然减少了攻击面。升级完依赖后用 mvn clean package 重打包然后对照启动日志看有没有报错。记住先从 devMode 调试模式下跑一遍登录注册全流程确认没有异常再关 devMode 发到生产。生产环境还有两个 OGNL 加固点。第一在 struts.xml 里设置 禁止 OGNL 访问静态方法这是很多攻击链的第一步。第二别把 devMode 开在生产环境devMode 下框架会输出大量日志而且部分安全检查会被跳过。这些都属于保命操作一台不做就可能变成别人的挖矿肉鸡。5.3 三个高频踩坑配置翻车、中文乱码、404 ActionNotFound最终我把这个项目反复做了三遍遇到的坑可以浓缩成三条按出现频率排踩坑一404 页面提示 There is no Action mapped for namespace / and action name user_login。现象是明明注册了 Action 却访问不到。原因通常有两种struts.xml 文件名写错比如叫 struts.xml 但放在了 WEB-INF 下而不是类路径 src/main/resources或者包名、命名空间对不上。解决方式确认 struts.xml 在 target/classes 目录下打开编译后的路径检查命名空间严格区分大小写/ 和 /user 是两个 namespace。我之前犯过把 namespace 写成空字符串结果请求路径里多带了一个斜杠的错这种问题要养成看启动时日志的习惯里面通常会打印加载了哪些配置文件能省大量排查时间。踩坑二注册成功跳转登录页后中文全部变成问号。现象是 JSP 里显示不出中文代码里明明写了中文。原因是 Struts2 默认的编码不是 UTF-8如果 web.xml 没配置 CharacterEncodingFilterPOST 请求的中文就会乱。解决方式在 web.xml 的最前面加一个编码过滤器注意它必须放在 Struts2 过滤器之前。filter filter-nameencoding/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencoding/filter-name url-pattern/*/url-pattern /filter-mapping另外 JSP 页面头部要写 % page contentTypetext/html;charsetUTF-8 %两者都做了才彻底不乱码。如果你的项目没引入 Spring那也可以自己写一个简单的 Filter或者用 Struts2 的 struts.i18n.encoding 常量但这招对 GET 请求的 queryString 无效最省心的还是 Spring 那个过滤器。踩坑三登录成功后点“退出”无效。现象是点了退出按钮、关了浏览器再打开还是登录状态。原因是只在前端隐藏了菜单没有真正销毁 Session。解决方式退出 Action 里要调 session.clear() 或 session.remove(loginUser)并返回一个重定向到登录页的 result。另外一定要设置 Session 过期时间在 web.xml 里加session-config session-timeout30/session-timeout /session-config30 表示 30 分钟不操作自动失效。这个配置很多课设项目根本没有导致用户挂一晚上都还能操作答辩时评委一问就露怯。6. 进阶技巧把登录状态校验做成通用拦截器并验证安全边界Struts2 的拦截器机制是这个框架最值得学的部分。把登录校验放到每个 Action 方法里来写会导致大量重复代码正确做法是写一个拦截器在 Action 执行之前统一做校验。核心思路是放行登录、注册等免登录接口拦截其它所有请求如果 Session 里没有 loginUser 就直接返回 loginResult 跳回登录页。下面这个拦截器是我一直沿用的模板public class LoginInterceptor extends AbstractInterceptor { Override public String intercept(ActionInvocation invocation) throws Exception { MapString, Object session invocation.getInvocationContext().getSession(); String actionName invocation.getProxy().getActionName(); if (actionName.startsWith(user_login) || actionName.startsWith(user_register)) { return invocation.invoke(); } if (session.get(loginUser) null) { return loginResult; } return invocation.invoke(); } }在 struts.xml 里把这个拦截器挂到 default 包上我这个项目的配置大致是package namedefault namespace/ extendsstruts-default interceptors interceptor namelogin classcom.demo.interceptor.LoginInterceptor / interceptor-stack nameloginStack interceptor-ref namedefaultStack / interceptor-ref namelogin / /interceptor-stack /interceptors default-interceptor-ref nameloginStack / !-- 其他 action 配置与 result 配置 -- /package注意把自定义拦截器放在 defaultStack 之后这样先执行框架自带的参数封装、类型转换再执行登录检查Session 里才有完整的参数可用。default-interceptor-ref 一旦覆盖默认的拦截器栈就失效了所以必须把 defaultStack 手动 ref 进来这是我见过最常见的拦截器不生效的原因。配置完成后用 Postman 或 curl 模拟未登录请求验证拦截器是否生效。比如 curl -i http://localhost:8080/user_list.action预期返回 302 跳转到登录页再带一个 JSESSIONID cookie 访问同样的 URL预期返回 200 和列表页面。这一步一定要做因为 JSP 页面上隐藏链接只能防君子不能防小人真正的安全边界在服务端。做完这些剩下的就是调 OGNL 取值性能、给 Action 方法补充 try-catch 统一异常处理这些细活了。我自己的习惯是每写完一个 Action 方法就在本地用一个独立的 Tomcat 端口起一份实例测试完再部署到开发环境不混着跑。这些年维护老项目最深的教训就是别因为 Struts2 老就轻敌漏洞爆出来的时候只有先理解它的人不慌。希望这篇笔记能帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

fs-extra removeSync() 深度指南:一行代码递归删除文件与目录的同步实现原理 2026/9/25 9:32:39

fs-extra removeSync() 深度指南:一行代码递归删除文件与目录的同步实现原理

开发工具 【免费下载链接】node-fs-extra Node.js: extra methods for the fs object like copy(), remove(), mkdirs() 项目地址: https://gitcode.com/gh_mirrors/no/node-fs-extra 点击查看 免费下载 导读 removeSync(path) 是 fs-extra 提供的同步删除 API&am…

阅读更多 →
Excel文件合并终极指南:5种实用方法从手动到自动化 2026/9/25 9:32:39

Excel文件合并终极指南:5种实用方法从手动到自动化

多个 Excel 文件合并成一个表,这个话题我帮周围的同事处理过无数次。每次一听说要“合并报表”,大家第一反应要么是复制粘贴到手指抽筋,要么是急着找人写代码。其实合并 Excel 文件的思路并不复杂,关键看你的文件数量、表头是否规…

阅读更多 →
S3 Browser:Windows原生S3管理工具深度解析 2026/9/25 9:32:20

S3 Browser:Windows原生S3管理工具深度解析

1. 为什么是 S3 Browser?——Windows 用户管理 AWS S3 的真实痛点与替代方案对比在 Windows 桌面环境里直连 AWS S3,你大概率经历过这几种“卡点”:用 AWS CLI 命令行敲半天aws s3 cp却搞不定路径斜杠方向、权限报错堆成山;用 VS …

阅读更多 →
Atlas 300V 24G 上部署 YOLO:从模型转换到调优的完整实战 2026/9/25 9:32:14

Atlas 300V 24G 上部署 YOLO:从模型转换到调优的完整实战

最近不止一个人来问我同一个问题:手里有张 Atlas 300V 24G 卡,到底能不能拿来跑 YOLO,是不是真像网上说的那样“插上就能用”。还有人干脆搞混了它的定位,把它当成普通显卡去跑训练,结果一跑就懵。今天这篇就把我这段时…

阅读更多 →
Xred木马深度剖析:PHP WebShell的隐蔽驻留与实战排查防御 2026/9/25 9:31:35

Xred木马深度剖析:PHP WebShell的隐蔽驻留与实战排查防御

1. 从一次应急响应说起:Xred木马到底是什么我第一次接触到Xred木马,是在一次内部安全巡检中。当时一台测试服务器的CPU占用率长期飘红,排查了半天也没找到明显的异常进程,直到用netstat看到一条非常可疑的外连连接,顺藤…

阅读更多 →
SQL Server PIVOT 行转列实战:静态与动态写法及避坑指南 2026/9/25 9:31:28

SQL Server PIVOT 行转列实战:静态与动态写法及避坑指南

简介:这份PDF资料聚焦SQL Server中行转列的核心技术PIVOT,面向需要处理报表数据转换的数据库开发人员与数据分析初学者。内容以WEEK_INCOME收入表为例,从传统CASE配合SUM的写法切入,逐步过渡到PIVOT操作符的语法结构,并…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉