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

如何基于令牌管理请求授权?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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:06:38