Struts2登录注册系统:拦截器链与MVC闭环实践

发布时间:2026/10/29 4:50:26
Struts2登录注册系统:拦截器链与MVC闭环实践 简介这是一套基于Struts2框架实现的Java Web用户管理实战项目面向Java初学者与Web开发入门者聚焦MVC架构下登录注册、用户信息增删改查等核心功能的完整落地。资源包共74个文件涵盖13个Java源码含Action、Service、DAO层逻辑、9个XML配置文件struts.xml、web.xml等关键配置、6个JSP页面userinfo.jsp、regist.jsp等视图模板、13个Class编译文件及SQL Server数据库文件mdf/ldf辅以CSS、JS、GIF资源和配套说明文档整体4.27MB结构清晰便于分层理解与调试。已有844人学习下载项目完整呈现了Struts2拦截器校验、Action映射、表单验证、Session会话管理及JDBC数据库交互等关键实践环节附带MyEclipse工程配置.project、.classpath等开箱即用是掌握传统Java Web开发流程与框架集成的优质练手案例。1. 为什么还在用 Struts2 做登录注册不是守旧是真能闭环交付某高校课程设计要求“实现带权限控制的用户信息管理”学生交上来一堆 Spring Boot 模板——跑得快但一问「登录失败时如何统一拦截并返回友好提示」就卡壳一查「注册成功后怎么把用户 ID 写进 session 并跳转到个人页」代码里全是Controller硬跳没做 Action 转发链路设计。而一份用 Struts2 实现的完整登录注册系统恰恰卡在「流程可追溯、异常可拦截、权限可插拔」这三个课设答辩最常被追问的点上。它不追求新潮但能把Action的生命周期、Interceptor的执行顺序、Result的类型选择、ModelDriven的数据绑定这些概念全部落到一个真实可运行的.war包里。适合需要交一份「看得见请求流转、摸得着拦截逻辑、改得了表单校验」的毕设/课设同学——尤其当你导师说「别光贴配置讲讲你这个LoginAction从接收参数到返回 JSON 中间走了哪几层」时这份 Struts2 方案就是你的标准答案。2. 总体架构设计MVC 三层结构与五个核心模块的职责划分Struts2 不是黑匣子它是一套有明确分层契约的 Web 框架。本方案采用经典三层结构表现层JSP Struts 标签、控制层Action Interceptor、模型层POJO Service DAO。所有模块均按「单一职责可替换」原则组织避免把数据库操作写进 Action也禁止在 JSP 里写 Java 脚本。整个系统围绕用户生命周期展开划分为五大模块用户注册、登录鉴权、个人信息维护、密码修改、退出清理。每个模块对应一个独立 Action 类共用一套BaseAction提供通用方法如getUsername()、getErrorMessage()同时通过struts.xml显式声明包继承关系确保拦截器栈可复用、结果类型可覆盖。2.1 核心配置文件struts.xml的模块化组织方式struts.xml是整个系统的路由中枢。本方案不采用单文件堆砌写法而是按功能域拆分为struts-user.xml用户模块、struts-common.xml公共拦截器与结果类型再由主配置文件include引入。关键设计点在于包package的继承链package namebase abstracttrue interceptors interceptor nameloginCheck classcom.example.interceptor.LoginCheckInterceptor/ interceptor-stack nameuserStack interceptor-ref namedefaultStack/ interceptor-ref nameloginCheck/ /interceptor-stack /interceptors /package package nameuser extendsbase namespace/user action nameregister classcom.example.action.RegisterAction methodexecute result namesuccess typeredirectActionprofile/result result nameinput typedispatcher/WEB-INF/jsp/register.jsp/result result nameerror typedispatcher/WEB-INF/jsp/error.jsp/result /action /package提示abstracttrue的包不能定义 Action只用于定义拦截器和结果类型namespace/user决定了 URL 前缀所有该包下 Action 的访问路径自动补/user/避免硬编码路径。redirectAction类型用于重定向到另一个 Action如注册成功后跳转到profile而非直接跳转 JSP保证 Controller 层完整参与流程。2.2 用户注册模块从表单提交到数据库落库的四步闭环注册不是简单存一条记录它包含「前端校验 → 后端校验 → 密码加密 → 事务提交」四个不可跳过的环节。本方案中RegisterAction继承ActionSupport并实现ModelDrivenUser使 Struts2 自动将表单字段绑定到User对象属性。关键代码如下public class RegisterAction extends ActionSupport implements ModelDrivenUser { private User user new User(); Override public User getModel() { return user; } public String execute() throws Exception { // 1. 业务校验用户名是否已存在 if (userService.findByUsername(user.getUsername()) ! null) { this.addFieldError(username, 用户名已被注册); return INPUT; } // 2. 密码加密使用 BCrypt非明文存储 user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword())); // 3. 设置默认状态与时间戳 user.setStatus(1); // 1启用 user.setCreateTime(new Date()); // 4. 调用 Service 层完成插入Service 方法加 Transactional userService.save(user); return SUCCESS; } }这段代码背后有三层设计意图第一addFieldError()触发 Struts2 的字段级错误收集机制配合 JSP 中s:fielderror fieldNameusername/可精准定位报错字段第二密码加密不在 DAO 层做而在 Action 层完成确保敏感逻辑不泄露到数据访问层第三userService.save()是接口调用实际实现类可随时替换为 MyBatis 或 JDBC Template符合依赖倒置原则。2.3 登录鉴权模块基于 Session 的状态管理与拦截器联动登录不是if(username.equals(x) pwd.equals(y))就完事。本方案采用「登录 Action 写 Session → 全局拦截器读 Session → 非登录态强制跳转」的三段式设计。LoginAction成功后执行public String execute() throws Exception { User user userService.login(username, password); if (user ! null) { // 将用户基本信息存入 HttpSession非全量对象避免序列化问题 MapString, Object userInfo new HashMap(); userInfo.put(id, user.getId()); userInfo.put(username, user.getUsername()); userInfo.put(role, user.getRole()); ServletActionContext.getRequest().getSession().setAttribute(LOGIN_USER, userInfo); return SUCCESS; } else { this.addActionError(用户名或密码错误); return INPUT; } }而LoginCheckInterceptor拦截器则负责在每次请求前检查public String intercept(ActionInvocation invocation) throws Exception { HttpServletRequest request ServletActionContext.getRequest(); HttpSession session request.getSession(false); if (session null || session.getAttribute(LOGIN_USER) null) { // 未登录跳转至登录页并保留原请求地址用于登录后回跳 String originalUrl request.getRequestURL().toString(); session.setAttribute(ORIGINAL_URL, originalUrl); return login; // 对应 struts.xml 中定义的 login result } return invocation.invoke(); // 放行 }注意session.setAttribute(LOGIN_USER, userInfo)存的是精简 Map而非整个User实体。因为User若含 Hibernate 关联对象或懒加载代理在 Session 序列化时极易抛NotSerializableException——这是 Struts2 项目最典型的翻车点之一后面避坑章节会重点讲。3. 关键技术实现拦截器链、表单校验与 JSON 数据交互Struts2 的能力边界不在“能不能做”而在“怎么做才不踩坑”。本章聚焦三个高频使用场景多拦截器协同、声明式编程式双重校验、前后端分离下的 JSON 交互。每一处都给出可直接粘贴的代码并说明其在课设答辩中如何解释设计动机。3.1 拦截器链设计登录检查、权限控制、日志记录的执行顺序一个请求可能同时触发多个拦截器顺序错了就会出大问题。比如「先记日志再检查登录」那未登录用户的行为也会被记录「先权限控制再登录检查」则权限判断永远拿不到用户信息。本方案定义了严格顺序的拦截器栈interceptor-stack namesecureStack interceptor-ref nameparams/ !-- 1. 解析参数 -- interceptor-ref namevalidation/ !-- 2. 执行校验XML/注解 -- interceptor-ref nameworkflow/ !-- 3. 校验失败跳 input -- interceptor-ref nameloginCheck/ !-- 4. 检查登录态 -- interceptor-ref namepermissionCheck/ !-- 5. 检查角色权限 -- interceptor-ref namelogging/ !-- 6. 记录访问日志 -- /interceptor-stack其中permissionCheck拦截器从 Session 中取出role字段比对当前 Action 的RequiresRole(ADMIN)注解通过自定义注解实现不匹配则返回forbidden结果。而logging拦截器使用StopWatch统计 Action 执行耗时并写入 log4j 日志public String intercept(ActionInvocation invocation) throws Exception { StopWatch stopWatch new StopWatch(); stopWatch.start(); String result invocation.invoke(); stopWatch.stop(); logger.info(Action [{}] executed in {}ms, result{}, invocation.getAction().getClass().getSimpleName(), stopWatch.getTotalTimeMillis(), result); return result; }这种设计让答辩时能清晰回答“为什么日志拦截器放在最后因为要统计整个 Action 生命周期包括数据库操作和视图渲染”。3.2 表单校验XML 声明式校验与 Action 内编程式校验的分工Struts2 支持两种校验方式但新手常混淆使用场景。本方案明确规定格式类校验长度、邮箱、数字用 XML业务类校验用户名是否存在、旧密码是否正确用 Action 内代码。以注册为例RegisterAction-validation.xml文件内容如下!DOCTYPE validators PUBLIC -//Apache Struts//XWork Validator 1.0.3//EN http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd validators field nameusername field-validator typerequiredstring message用户名不能为空/message /field-validator field-validator typeregex param nameexpression![CDATA[^[a-zA-Z0-9_]{3,16}$]]/param message用户名只能是3-16位字母、数字或下划线/message /field-validator /field field nameemail field-validator typeemail message邮箱格式不正确/message /field-validator /field /validators而「用户名是否已存在」这类需查库的逻辑必须放在execute()方法内原因有二一是 XML 校验器无法访问 Service 层二是若放 XML校验失败时用户看到的是“用户名已存在”但此时数据库还没写入属于逻辑矛盾。答辩时可强调“XML 校验是前置过滤器编程式校验是业务闸门二者不可替代”。3.3 JSON 数据交互json-default包与JSONResult的定制化使用当课设要求“用 AJAX 提交登录表单并局部刷新”时Struts2 默认的dispatcher结果类型就不够用了。本方案启用json-default包并为登录 Action 单独配置 JSON 结果package nameajax extendsjson-default namespace/ajax action namedoLogin classcom.example.action.LoginAction methodajaxLogin result typejson param namerootresponse/param param nameexcludePropertiespassword,confirmPassword/param /result /action /package对应的ajaxLogin()方法返回一个MapString, Objectpublic String ajaxLogin() { MapString, Object response new HashMap(); try { User user userService.login(username, password); if (user ! null) { ServletActionContext.getRequest().getSession() .setAttribute(LOGIN_USER, buildUserInfo(user)); response.put(success, true); response.put(message, 登录成功); response.put(redirectUrl, /user/profile.action); } else { response.put(success, false); response.put(message, 用户名或密码错误); } } catch (Exception e) { response.put(success, false); response.put(message, 系统繁忙请稍后再试); } return SUCCESS; }注意param nameexcludeProperties用于防止敏感字段如密码被序列化到 JSON 响应中这是 Struts2 JSON 插件的安全基线配置答辩时若被问及“如何防信息泄露”这就是标准答案。4. 常见问题排查五类高频翻车点与现场修复方案Struts2 项目部署后打不开、表单提交没反应、中文乱码、拦截器不生效……这些问题不是玄学而是有固定模式。以下是我在带三个模拟项目 X 过程中学生最常遇到的五类问题每条按「现象 → 原因 → 解决」给出可立即执行的修复步骤。4.1 现象页面打开空白浏览器控制台无报错Tomcat 日志显示FilterDispatcher已加载原因Struts2 2.5 版本已废弃FilterDispatcher但部分教程仍沿用旧版 web.xml 配置导致 Filter 初始化失败后续所有请求被静默丢弃。解决检查web.xml将filter-classorg.apache.struts2.dispatcher.FilterDispatcher/filter-class替换为filter-classorg.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter/filter-class并确认struts2-core-x.x.x.jar版本与struts.xml中 DTD 声明匹配如xwork-validator-1.0.3.dtd对应 Struts2 2.3.x。4.2 现象表单提交后页面跳转到input结果但fielderror标签不显示任何错误原因s:fielderror/标签未指定fieldName且 Action 中addFieldError()使用的字段名与 JSP 表单name属性不一致。例如表单写s:textfield nameuser.username/但addFieldError(username, ...)—— Struts2 无法关联。解决统一命名规则。推荐两种方案① 表单用s:textfield nameusername/Action 中addFieldError(username, ...)② 若必须用对象属性表单写s:textfield nameuser.username/Action 中addFieldError(user.username, ...)。切勿混用。4.3 现象中文参数提交后 Action 中获取为乱码如“张三”变“å¼ ä¸‰”原因Struts2 的params拦截器默认使用ISO-8859-1解码而浏览器发送的是UTF-8。解决在struts.xml中全局设置constant namestruts.i18n.encoding valueUTF-8/ constant namestruts.locale valuezh_CN/并确保 JSP 页面顶部声明% page contentTypetext/html;charsetUTF-8 languagejava %血泪经验此问题在 Eclipse 中新建 JSP 时默认编码常为 GBK务必右键文件 → Properties → Text file encoding → 改为 UTF-8。4.4 现象LoginCheckInterceptor拦截器不生效未登录也能访问/user/profile.action原因拦截器栈未在 Action 所属 package 中引用或引用了错误的栈名。常见错误是struts.xml中写了interceptor-ref namesecureStack/但该栈定义在另一个未被extends的 package 中。解决检查 package 继承链。确保目标 Action 所在 package 的extends属性指向定义了该拦截器栈的父包。例如package nameuser extendsbase !-- 必须 extends 定义了 secureStack 的包 -- action nameprofile interceptor-ref namesecureStack/ !-- 此处引用才有效 -- ... /action /package4.5 现象s:iterator遍历 List 报ognl.MethodFailedException提示getXXX()不存在原因OGNL 表达式中调用的方法在对象中确实不存在或方法签名不匹配如getList()返回ArrayList但 OGNL 写成list[0].name而ArrayList没有name属性。解决用#attr或#request显式指定作用域。例如s:iterator value#request.userList varu s:property value#u.username/ /s:iterator并在 Action 中HttpServletRequest request ServletActionContext.getRequest(); request.setAttribute(userList, userService.findAll());避免依赖 ValueStack 的隐式查找降低 OGNL 解析失败概率。5. 课设答辩验收指南从演示脚本到答辩话术的全流程准备答辩不是代码朗诵会而是用 8 分钟证明你“真的懂这个系统怎么跑起来”。我带过的某跨平台系统项目学生第一次答辩被连问 7 个“为什么”第二次就过了——差别就在是否提前设计好演示路径和应答逻辑。以下是我给学生定的硬性验收清单照着做答辩官挑不出硬伤。5.1 演示脚本三分钟闭环演示必须覆盖的五个动作不要从首页开始答辩演示必须从“最能体现你掌控力”的环节切入。我要求学生固定走以下五步总时长控制在 180 秒内步骤操作目的答辩官关注点1清空浏览器 Cookie 和 Session访问/user/login.action证明登录入口干净是否理解会话状态初始化2输入错误密码点击登录 → 页面显示“用户名或密码错误”展示addActionError()与input结果联动校验机制是否真落地3正确登录后手动在地址栏输入/user/profile.action验证LoginCheckInterceptor是否拦截未登录请求拦截器是否真正生效4在 profile 页面点击“修改密码”提交新密码 → 成功后跳转到登录页展示logoutAction 清理 Session状态销毁是否彻底5重新登录查看数据库user表password字段是否为 BCrypt 加密串证明密码未明文存储安全实践是否到位提示第 5 步必须现场打开数据库工具如 DBeaver查询不能只说“我查过了”。答辩官信数据不信口头承诺。5.2 答辩话术针对三类高频问题的标准应答模板答辩官最爱问三类问题技术选型、设计取舍、扩展可能。以下是经过验证的应答结构每条控制在 90 秒内说完Q为什么不用 Spring Security 做权限A“Spring Security 功能强大但课设要求聚焦‘用户生命周期管理’这一核心流程。Struts2 的拦截器机制足够支撑登录检查、角色判断、日志记录三层需求且所有逻辑都在interceptor包下目录结构清晰便于讲解。如果未来升级为微服务我会把权限模块抽成独立 Auth Service用 JWT 替代 Session。”QModelDriven和ServletActionContext哪个更推荐A“ModelDriven用于表单数据绑定它让 Struts2 自动把请求参数注入到 Model 对象减少样板代码ServletActionContext用于获取 Servlet API 对象比如我要在 Action 里重定向到外部系统就必须用它获取HttpServletResponse。两者不是互斥关系而是分工明确前者管数据流入后者管环境交互。”Q如果用户量涨到 10 万这个架构怎么优化A“第一把LoginCheckInterceptor中的 Session 存储换成 Redis解决单机 Session 共享问题第二userService.login()方法增加缓存层用Cacheable缓存用户凭证第三静态资源CSS/JS剥离到 NginxStruts2 只处理动态请求。这三点不改变现有代码结构只需调整配置和引入新组件。”5.3 文档交付物答辩材料包必须包含的四类文件学生常以为交个 WAR 包就行其实答辩材料包才是加分项。我要求必须包含以下四类文件缺一不可README.md用表格列出系统功能点、技术栈版本、部署步骤含 Tomcat 版本要求、默认账号密码如 admin/123456ER-Diagram.png用 draw.io 画的用户表 E-R 图标注主外键、字段类型、索引如username加唯一索引struts-structure.txt文本版包结构树标出关键类位置例如com.example.action/ ├── LoginAction.java # 登录逻辑含 ajaxLogin() ├── RegisterAction.java # 注册逻辑含密码加密 com.example.interceptor/ ├── LoginCheckInterceptor.java # 检查 session 中 LOGIN_USERtest-cases.xlsx测试用例表含用例编号、操作步骤、预期结果、实际结果手填、备注如“需清空 session 后执行”。从那以后我每次指导学生做课设都强制他们第一天就建好这四个文件边开发边填内容。不是为了应付检查而是逼自己在写第一行代码前就想清楚这个功能到底要解决什么问题、谁来用、怎么验证。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询