Spring Security и шаблон Synchronizer Token J2EE, проблема при сбое аутентификации - PullRequest
0 голосов
/ 26 апреля 2010

мы используем 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>

Ответы [ 2 ]

1 голос
/ 28 апреля 2010

Хотите принять первый запрос POST или нет (поскольку вы говорите, что токен предназначен для целей безопасности, а также для предотвращения отправки дубликатов форм)? Обычно это не тот случай, когда вы захотите принять POST из предыдущего сеанса при использовании токенов синхронизатора, так почему бы просто не запустить пользователя по четко определенному URL-адресу при входе в систему (который поддерживает Spring Security)

Если вы действительно хотите продолжить предыдущую транзакцию, вы можете расширить onSuccessfulAuthentication метод AuthenticationProcessingFilter Spring Security и проанализировать SavedRequest (сохраненный в сеансе), чтобы определить предыдущее значение токена. Затем вы могли бы инициализировать ваш TransactionTokenBean с этим значением, чтобы оно было принято в следующем запросе.

Код кэширования запросов в Spring Security 3 гораздо более гибок, поэтому, если вы можете выполнить обновление, это было бы целесообразно.

0 голосов
/ 27 апреля 2010

Зачем ограничивать сессию бобов? Похоже, вы хотите токен, который будет существовать вечно - даже через новый логин. Это больше похоже на работу для печенья без тайм-аута.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...