мы используем Spring Security 2.0.4. У нас есть TransactionTokenBean, который генерирует уникальный токен каждый POST, бин имеет сессионную область. Маркер используется для проблемы с отправкой дубликата формы (и безопасности). TransactionTokenBean вызывается из фильтра сервлетов.
Наша проблема заключается в следующем, после истечения времени ожидания сеанса, когда вы выполняете POST в приложении, Spring Security перенаправляет на страницу входа, сохраняя исходный запрос. После повторного входа в систему TransactionTokenBean создается снова, поскольку он имеет область действия сеанса, но затем Spring переходит к первоначально доступному URL, также отправляя маркер, созданный в то время. Поскольку TransactionTokenBean создается снова, токены не совпадают, и наш фильтр создает исключение. Я не совсем знаю, как справиться с этим элегантно (или, если на то пошло, я даже не могу исправить это с помощью взлома), какие-нибудь идеи?
Это код 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;
}
}
и это фильтр сервлетов (игнорируйте 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>