Я пытаюсь создать 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
Надеюсь, это поможет некоторым другим.