Подтвердить что ты не робот

Spring Безопасность - аутентификация аутентификации на основе токена и пользователя/пароля на основе токена

Я пытаюсь создать webapp, который будет в основном предоставлять REST API с помощью Spring, и пытаюсь настроить защиту.

Я пытаюсь реализовать такой шаблон: https://developers.google.com/accounts/docs/MobileApps (Google полностью изменил эту страницу, поэтому больше не имеет смысла - см. страницу, на которую я ссылался здесь: http://web.archive.org/web/20130822184827/https://developers.google.com/accounts/docs/MobileApps)

Вот что мне нужно:

  • У веб-приложения есть простые формы входа/регистрации, которые работают с обычной аутентификацией пользователя/пароля spring (ранее делали этот тип с помощью dao/authenticationmanager/userdetailsservice и т.д.).
  • Конечные точки REST api, которые являются сессиями без состояния и каждый запрос, прошедший проверку подлинности на основе маркера, предоставленного с запросом

(например, пользователь регистрируется/регистрируется с использованием обычных форм, webapp обеспечивает безопасный файл cookie с токеном, который затем может использоваться в следующих запросах API)

У меня была обычная настройка проверки подлинности, как показано ниже:

@Override protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf()
            .disable()
        .authorizeRequests()
            .antMatchers("/resources/**").permitAll()
            .antMatchers("/mobile/app/sign-up").permitAll()
            .antMatchers("/v1/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/")
            .loginProcessingUrl("/loginprocess")
            .failureUrl("/?loginFailure=true")
            .permitAll();
}

Я думал о добавлении фильтра pre-auth, который проверяет токен в запросе и затем устанавливает контекст безопасности (это означает, что обычная следующая проверка подлинности будет пропущена?), однако, за пределами обычного пользователя/пароль Я не слишком много сделал с защитой на основе токенов, но на основании некоторых других примеров я пришел к следующему:

Конфигурация безопасности:

@Override protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .disable()
            .addFilter(restAuthenticationFilter())
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and()
                .antMatcher("/v1/**")
            .authorizeRequests()
                .antMatchers("/resources/**").permitAll()
                .antMatchers("/mobile/app/sign-up").permitAll()
                .antMatchers("/v1/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/")
                .loginProcessingUrl("/loginprocess")
                .failureUrl("/?loginFailure=true")
                .permitAll();
    }

Мой пользовательский фильтр отдыха:

public class RestAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public RestAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    private final String HEADER_SECURITY_TOKEN = "X-Token"; 
    private String token = "";


    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        this.token = request.getHeader(HEADER_SECURITY_TOKEN);

        //If we have already applied this filter - not sure how that would happen? - then just continue chain
        if (request.getAttribute(FILTER_APPLIED) != null) {
            chain.doFilter(request, response);
            return;
        }

        //Now mark request as completing this filter
        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

        //Attempt to authenticate
        Authentication authResult;
        authResult = attemptAuthentication(request, response);
        if (authResult == null) {
            unsuccessfulAuthentication(request, response, new LockedException("Forbidden"));
        } else {
            successfulAuthentication(request, response, chain, authResult);
        }
    }

    /**
     * Attempt to authenticate request - basically just pass over to another method to authenticate request headers 
     */
    @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        AbstractAuthenticationToken userAuthenticationToken = authUserByToken();
        if(userAuthenticationToken == null) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
        return userAuthenticationToken;
    }


    /**
     * authenticate the user based on token, mobile app secret & user agent
     * @return
     */
    private AbstractAuthenticationToken authUserByToken() {
        AbstractAuthenticationToken authToken = null;
        try {
            // TODO - just return null - always fail auth just to test spring setup ok
            return null;
        } catch (Exception e) {
            logger.error("Authenticate user by token error: ", e);
        }
        return authToken;
    }

Приведенное выше приводит к ошибке при запуске приложения: authenticationManager must be specified Может ли кто-нибудь сказать мне, как лучше всего это сделать - фильтр pre_auth - лучший способ сделать это?


ИЗМЕНИТЬ

Я написал, что я нашел, и как я это сделал с помощью Spring -security (включая код), реализующего стандартную реализацию токена (не OAuth)

Обзор проблемы и подхода/решения

Внедрение решения с Spring -security

Надеюсь, это поможет некоторым другим.

4b9b3361

Ответ 1

Я считаю, что ошибка, о которой вы говорите, просто потому, что для базового класса AbstractAuthenticationProcessingFilter, который вы используете, требуется AuthenticationManager. Если вы не собираетесь использовать его, вы можете установить его в no-op или просто реализовать Filter напрямую. Если ваш Filter может аутентифицировать запрос и устанавливает SecurityContext, то обычно обработка вниз по потоку будет пропущена (это зависит от реализации нисходящих фильтров, но я не вижу ничего странного в вашем приложении, поэтому они, вероятно, все ведут себя так).

Если бы я был вами, я мог бы рассмотреть возможность размещения конечных точек API в полностью отдельной цепочке фильтров (другой WebSecurityConfigurerAdapter bean). Но это только упрощает чтение, не обязательно важно.

Вы можете найти (как предложено в комментариях), что вы в конечном итоге изобретаете колесо, но не получаете вреда при попытке, и вы, вероятно, узнаете больше о Spring и безопасности в процессе.

ДОПОЛНЕНИЕ: подход github довольно интересен: пользователи просто используют токен в качестве пароля в базовом auth, а серверу не нужен специальный фильтр (BasicAuthenticationFilter отлично).