gpt4 book ai didi

java - Spring Security 和 Synchronizer Token J2EE 模式,身份验证失败时出现问题

转载 作者:行者123 更新时间:2023-12-01 16:05:32 24 4
gpt4 key购买 nike

我们正在使用 Spring Security 2.0.4。我们有一个 TransactionTokenBean,它在每次 POST 时生成一个唯一的 token ,该 bean 是 session 范围的。该 token 用于解决重复表单提交问题(和安全性)。 TransactionTokenBean 是从 Servlet 过滤器调用的。我们的问题如下,发生 session 超时后,当您在应用程序中执行 POST 时,Spring Security 会重定向到登录页面,并保存原始请求。再次登录后,TransactionTokenBean 会再次创建,因为它是 session 范围的,但 Spring 会转发到最初访问的 url,同时发送当时生成的 token 。由于再次创建 TransactionTokenBean, token 不匹配,我们的过滤器会抛出异常。我不太知道如何优雅地处理这个问题,(或者就此而言,我什至无法通过黑客修复它),有什么想法吗?

这是 TransactionTokenBean 的代码:

public class TransactionTokenBean implements Serializable {

public static final int TOKEN_LENGTH = 8;

private RandomizerBean randomizer;

private transient Logger logger;

private String expectedToken;

public String getUniqueToken() {
return expectedToken;
}

public void init() {
resetUniqueToken();
}

public final void verifyAndResetUniqueToken(String actualToken) {
verifyUniqueToken(actualToken);
resetUniqueToken();
}

public void resetUniqueToken() {
expectedToken = randomizer.getRandomString(TOKEN_LENGTH, RandomizerBean.ALPHANUMERICS);
getLogger().debug("reset token to: " + expectedToken);
}

public void verifyUniqueToken(String actualToken) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("verifying token. expected=" + expectedToken + ", actual=" + actualToken);
}

if (expectedToken == null || actualToken == null || !isValidToken(actualToken)) {
throw new IllegalArgumentException("missing or invalid transaction token");
}

if (!expectedToken.equals(actualToken)) {
throw new InvalidTokenException();
}
}

private boolean isValidToken(String actualToken) {
return StringUtils.isAlphanumeric(actualToken);
}

public void setRandomizer(RandomizerBean randomizer) {
this.randomizer = randomizer;
}

private Logger getLogger() {
if (logger == null) {
logger = Logger.getLogger(TransactionTokenBean.class);
}
return logger;
}

}

这是 Servlet 过滤器(忽略 Ajax 内容):

public class SecurityFilter implements Filter {

static final String AJAX_TOKEN_PARAM = "ATXTOKEN";
static final String TOKEN_PARAM = "TXTOKEN";

private WebApplicationContext webApplicationContext;

private Logger logger = Logger.getLogger(SecurityFilter.class);

public void init(FilterConfig config) {
setWebApplicationContext(WebApplicationContextUtils.getWebApplicationContext(config.getServletContext()));
}

public void destroy() {
}

public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException,
ServletException {

HttpServletRequest request = (HttpServletRequest) req;


if (isPostRequest(request)) {
if (isAjaxRequest(request)) {
log("verifying token for AJAX request " + request.getRequestURI());
getTransactionTokenBean(true).verifyUniqueToken(request.getParameter(AJAX_TOKEN_PARAM));
} else {
log("verifying and resetting token for non-AJAX request " + request.getRequestURI());
getTransactionTokenBean(false).verifyAndResetUniqueToken(request.getParameter(TOKEN_PARAM));
}
}

chain.doFilter(request, response);
}

private void log(String line) {
if (logger.isDebugEnabled()) {
logger.debug(line);
}
}

private boolean isPostRequest(HttpServletRequest request) {
return "POST".equals(request.getMethod().toUpperCase());
}

private boolean isAjaxRequest(HttpServletRequest request) {
return request.getParameter("AJAXREQUEST") != null;
}

private TransactionTokenBean getTransactionTokenBean(boolean ajax) {
return (TransactionTokenBean) webApplicationContext.getBean(ajax ? "ajaxTransactionTokenBean"
: "transactionTokenBean");
}

void setWebApplicationContext(WebApplicationContext context) {
this.webApplicationContext = context;
}

}

web.xml的相关部分:

<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>
xxx.common.web.security.SecurityFilter
</filter-class>
</filter>

<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<servlet-name>SpringServlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

TransactionTokenBean:

<bean id="transactionTokenBean" class="xxx.common.web.bean.support.TransactionTokenBean"
init-method="init" scope="session">
<property name="randomizer" ref="randomizer" />
</bean>

最佳答案

您是否要接受第一个 POST 请求(因为您说 token 是出于安全目的以及防止重复表单提交)?当您使用同步器 token 时,通常不会出现这样的情况:您希望接受来自先前 session 的 POST,那么为什么不在用户登录时从明确定义的 URL 启动用户(Spring Security 支持) ?

如果确实想继续之前的事务,可以扩展 Spring Security 的 AuthenticationProcessingFilter 的 onSuccessfulAuthentication方法并内省(introspection) SavedRequest(存储在 session 中)以确定先前的 token 值。然后,您可以使用该值初始化 TransactionTokenBean,以便在后续请求中接受它。

Spring Security 3 中的请求缓存代码更加灵活,因此如果您可以升级,那将是可取的。

关于java - Spring Security 和 Synchronizer Token J2EE 模式,身份验证失败时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2715499/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com