如何基于令牌管理请求授权?Struts 1开发场景技术咨询
基于Struts 1的动态URL令牌授权实现方案
这个思路完全可行,结合Struts 1的请求处理流程,我们可以把这个方案落地得很顺畅。我帮你细化一下具体的实现步骤和关键代码,你可以参考着来做:
1. 先搞定数据库配置表
首先得建个表来动态管理哪些页面/功能需要令牌授权,字段可以这么设计:
id:主键url_pattern:匹配Struts的Action路径(比如/userManage.do)或者具体URL,支持模糊匹配(比如/admin/*)require_token:布尔值,标记是否需要令牌验证token_expire_minutes:令牌过期时间(分钟)description:备注(可选)
这样后续要调整授权规则,直接改数据库就行,不用动代码。
2. 自定义Filter做核心拦截
Struts 1的请求会先经过Filter链,所以我们写个TokenAuthFilter来处理所有请求的授权判断。核心逻辑分成这几步:
- 读取当前请求的URL,匹配数据库里的配置,判断是否需要令牌
- 如果不需要,直接放行到后续的Struts处理流程
- 如果需要,先检查Session里有没有已验证通过的标记(比如
token_validated),有就直接放行 - 没有的话,判断是不是用户提交令牌的请求:
- 是就验证输入的令牌和Session里存的是否一致、有没有过期,验证过了就标记Session,跳回用户原来要访问的页面
- 不是就生成随机令牌,存到Session(带过期时间),发邮件给用户,然后跳转到令牌输入页面
给你个核心代码示例:
public class TokenAuthFilter implements Filter { // 这里可以用Spring注入或者初始化时获取DAO实例 private TokenConfigDao tokenConfigDao; private EmailService emailService; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String requestUrl = req.getRequestURI(); // 匹配数据库中的授权配置 TokenConfig config = tokenConfigDao.matchUrl(requestUrl); if (config == null || !config.isRequireToken()) { chain.doFilter(request, response); return; } // 检查是否已经通过令牌验证 Boolean isValidated = (Boolean) req.getSession().getAttribute("token_validated"); if (Boolean.TRUE.equals(isValidated)) { chain.doFilter(request, response); return; } // 处理令牌提交请求 if ("/tokenSubmit.do".equals(requestUrl)) { String inputToken = req.getParameter("token"); String storedToken = (String) req.getSession().getAttribute("auth_token"); Long expireTime = (Long) req.getSession().getAttribute("token_expire_time"); if (inputToken != null && inputToken.equals(storedToken) && System.currentTimeMillis() < expireTime) { // 验证通过,标记Session req.getSession().setAttribute("token_validated", true); // 跳转回用户原本要访问的页面 String originalUrl = (String) req.getSession().getAttribute("original_request_url"); resp.sendRedirect(originalUrl); } else { // 验证失败,返回输入页并提示错误 req.setAttribute("errorMsg", "令牌无效或已过期,请重新获取"); req.getRequestDispatcher("/tokenInput.jsp").forward(req, resp); } return; } // 首次访问需要授权的页面,生成并发送令牌 String token = UUID.randomUUID().toString().replace("-", "").substring(0, 6); // 生成6位短令牌 long expireTime = System.currentTimeMillis() + config.getTokenExpireMinutes() * 60 * 1000; // 把令牌、过期时间、原请求URL存到Session req.getSession().setAttribute("auth_token", token); req.getSession().setAttribute("token_expire_time", expireTime); req.getSession().setAttribute("original_request_url", requestUrl); // 发送令牌到用户邮箱(这里替换成你的邮件服务调用) String userEmail = (String) req.getSession().getAttribute("current_user_email"); emailService.sendAuthToken(userEmail, token); // 跳转令牌输入页面 req.getRequestDispatcher("/tokenInput.jsp").forward(req, resp); } // Filter的init和destroy方法,根据需要初始化DAO和邮件服务 @Override public void init(FilterConfig filterConfig) throws ServletException { // 示例:从Spring上下文获取Bean ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext()); tokenConfigDao = ctx.getBean(TokenConfigDao.class); emailService = ctx.getBean(EmailService.class); } @Override public void destroy() {} }
3. 配置Web.xml和Struts-config.xml
- 在
web.xml里把我们的Filter放到Struts的核心Filter前面,确保先拦截请求:
<filter> <filter-name>TokenAuthFilter</filter-name> <filter-class>com.yourcompany.filter.TokenAuthFilter</filter-class> </filter> <filter-mapping> <filter-name>TokenAuthFilter</filter-name> <url-pattern>*.do</url-pattern> <!-- 匹配所有Struts Action请求 --> </filter-mapping> <!-- Struts核心Servlet配置放在后面 --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <!-- 其他配置省略 --> </servlet>
- 在
struts-config.xml里配置令牌提交的Action(其实Filter里已经处理了核心逻辑,这里主要是让Struts能识别这个请求路径):
<action path="/tokenSubmit" type="com.yourcompany.action.TokenSubmitAction" name="tokenForm" scope="request"> <forward name="success" path="/tokenInput.jsp"/> <forward name="error" path="/tokenInput.jsp"/> </action>
当然,如果你觉得把验证逻辑放到Action里更符合Struts的习惯,也可以把Filter里的提交验证部分移到Action里,Filter只负责拦截和跳转。
4. 写个简单的令牌输入页面
tokenInput.jsp就做个表单让用户输入令牌,还要能显示错误提示:
<%@ page contentType="text/html;charset=UTF-8" %> <html> <head> <title>授权令牌验证</title> <style> .error { color: #dc3545; margin-bottom: 15px; } .form-group { margin-bottom: 20px; } </style> </head> <body> <div style="width: 400px; margin: 100px auto;"> <h3>请输入授权令牌</h3> <% String errorMsg = (String) request.getAttribute("errorMsg"); %> <% if (errorMsg != null && !errorMsg.isEmpty()) { %> <p class="error"><%= errorMsg %></p> <% } %> <form action="${pageContext.request.contextPath}/tokenSubmit.do" method="post"> <div class="form-group"> <input type="text" name="token" placeholder="6位令牌" required style="width: 100%; padding: 8px;"> </div> <button type="submit" style="width: 100%; padding: 8px; background: #007bff; color: white; border: none; border-radius: 4px;">验证授权</button> </form> <p style="margin-top: 15px; font-size: 14px;">令牌已发送至您的注册邮箱,有效期为10分钟</p> </div> </body> </html>
5. 几个要注意的细节
- Session有效期:Session的过期时间要设置得比令牌过期时间长,不然Session失效了,存的令牌和原URL都丢了,用户验证不了
- 避免重复发令牌:如果用户反复刷新需要授权的页面,要判断Session里有没有未过期的令牌,有就直接跳输入页,别重复发邮件
- 令牌安全性:虽然是一次性令牌,也建议用随机强度高的生成方式,比如UUID截取或者SecureRandom生成
- 邮件可靠性:邮件发送可能有延迟,页面上要给用户明确提示,比如“若未收到邮件,请检查垃圾邮箱或点击重新获取”(可以加个重新获取的按钮,调用Filter里的重新生成逻辑)
内容的提问来源于stack exchange,提问作者KESO
相关产品推荐
相关产品推荐

