2017-02-20 4 views
9

У меня есть отдых api, где я аутентифицируюсь с использованием весенней безопасности. Основная авторизация, при которой клиент отправляет имя пользователя и пароль для каждого запроса. Теперь я хотел реализовать аутентификацию на основе токенов, где я пошлю токен в заголовке ответа, когда пользователь будет аутентифицирован сначала. Для дальнейших запросов клиент может включить этот токен в заголовок, который будет использоваться для аутентификации пользователя для ресурсов. У меня есть два провайдера аутентификации tokenAuthenticationProvider и daoAuthenticationProviderАутентификация на основе токена на основе Spring безопасности

@Component 
public class TokenAuthenticationProvider implements AuthenticationProvider { 

    @Autowired 
    private TokenAuthentcationService service; 

    @Override 
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException { 

     final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); 
     final HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); 
     final String token = request.getHeader(Constants.AUTH_HEADER_NAME); 
     final Token tokenObj = this.service.getToken(token); 
     final AuthenticationToken authToken = new AuthenticationToken(tokenObj); 
     return authToken; 
    } 

    @Override 
     public boolean supports(final Class<?> authentication) { 
      return AuthenticationToken.class.isAssignableFrom(authentication); 
     } 
} 

И в daoAuthenticationProvider я устанавливаю пользовательские UserDetailsService и аутентификация с пользовательской информацией авторизации, запрашивая его из базы данных (который работает отлично до тех пор, как имя пользователя и пароль, которые передаются с помощью Авторизация: Basic bGllQXBpVXNlcjogN21wXidMQjRdTURtR04pag == в качестве заголовка)

Но когда я включаю маркер в заголовке, используя X-AUTH-лексеме (что Constants.AUTH_HEADER_NAME), tokenAuthenticationProvider не вызывается. Я получаю ошибку как

{"timestamp":1487626368308,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/find"} 

И вот как я добавляю поставщиков аутентификации.

@Override 
    public void configure(final AuthenticationManagerBuilder auth) throws Exception { 

     final UsernamePasswordAuthenticationProvider daoProvider = new 

UsernamePasswordAuthenticationProvider(this.service, this.passwordEncoder()); 
    auth.authenticationProvider(this.tokenAuthenticationProvider); 
    auth.authenticationProvider(daoProvider); 
} 

Просьба предложить, как я могу реализовать аутентификацию на основе токена, не нарушая при этом текущего поведения весенней безопасности.

+0

У вас есть разные способы сделать это. Вы можете @Autowired провайдера непосредственно на каждом фильтре или установить это Провайдеры в одном Authentication Manager и работать с ним в обоих FIlters. Конечно, вы должны установить оба фильтра в Spring Security FilterChain. – Dani

ответ

11

Вот как я был в состоянии реализовать маркер проверки подлинности на основе и базовой аутентификации

SpringSecurityConfig.java

@Configuration 
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter 
{ 

    @Override 
    public void configure(final AuthenticationManagerBuilder auth) throws Exception 
    { 
     auth.userDetailsService(this.participantService).passwordEncoder(this.passwordEncoder()); 
    } 

    @Override 
    protected void configure(final HttpSecurity http) throws Exception 
    { 

     //Implementing Token based authentication in this filter 
     final TokenAuthenticationFilter tokenFilter = new TokenAuthenticationFilter(); 
     http.addFilterBefore(tokenFilter, BasicAuthenticationFilter.class); 

     //Creating token when basic authentication is successful and the same token can be used to authenticate for further requests 
     final CustomBasicAuthenticationFilter customBasicAuthFilter = new CustomBasicAuthenticationFilter(this.authenticationManager()); 
     http.addFilter(customBasicAuthFilter); 

    } 
} 

TokenAuthenticationFilter.java

public class TokenAuthenticationFilter extends GenericFilterBean 
    { 


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

      //extract token from header 
      final String accessToken = httpRequest.getHeader("header-name"); 
      if (null != accessToken) { 
      //get and check whether token is valid (from DB or file wherever you are storing the token) 

      //Populate SecurityContextHolder by fetching relevant information using token 
       final User user = new User(
          "username", 
          "password", 
          true, 
          true, 
          true, 
          true, 
          authorities); 
        final UsernamePasswordAuthenticationToken authentication = 
          new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); 
        SecurityContextHolder.getContext().setAuthentication(authentication); 

      } 

      chain.doFilter(request, response); 
     } 

     } 

CustomBasicAuthenticationFilter.java

@Component 
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter { 


    @Autowired 
    public CustomBasicAuthenticationFilter(final AuthenticationManager authenticationManager) { 
     super(authenticationManager); 
    } 

    @Override 
    protected void onSuccessfulAuthentication(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response, final Authentication authResult) { 
     //Generate Token 
     //Save the token for the logged in user 
     //send token in the response 
     response.setHeader("header-name" , "token"); 


    } 

} 

Поскольку наш CustomBasicAuthenticationFilter был настроен и добавлен в качестве фильтра к пружинной безопасности,

Всякий раз, когда базовая аутентификация успешна запрос будет перенаправлен на onSuccessfulAuthentication, где мы устанавливаем маркер и отправить его в ответ с некоторым заголовком «имя-заголовок».

Если для последующего запроса отправлено «имя-заголовок», запрос будет проходить через TokenAuthenticationFilter, прежде чем пытаться выполнить базовую проверку подлинности.

+0

Итак, даже если аутентификация Token прошла успешно, она также попытается выполнить базовую аутентификацию? Потому что это еще один фильтр. В чем смысл? Ему нужно каждый раз отправлять пользователю/паролю? – selman

+0

@selman Он не должен каждый раз отправлять пользователю/паролю.['BasicAuthenticationFilter'] (https://github.com/spring-projects/spring-security/blob/5.0.1.RELEASE/web/src/main/java/org/springframework/security/web/authentication/www /BasicAuthenticationFilter.java#L157) реализовано таким образом, что если в запросе нет базового заголовка проверки подлинности, он просто переходит к следующему фильтру. Таким образом, на самом деле оба фильтра будут работать для одного и того же запроса только в том случае, если этот запрос имеет как заголовок токена, так и основной заголовок auth. И в этом случае основной заголовок auth выигрывает (потому что он последний). – djxak

+0

@selman, если есть действительный токен, прикрепленный к запросу с заголовком маркера. Он не проходит через базовый фильтр auth. Существует два случая, когда оба фильтра будут выполнены. 1. Если запрос не содержит заголовка токена 2. Если токен недействителен/истек , который инициирует базовый фильтр проверки подлинности, и вы можете установить токен в базовом фильтре аутентификации, который может использоваться для дальнейших запросов. – Raghavendra

1

Вы можете попробовать установить свой маркер пользовательских AuthenticationToken в фильтре аутентификации, например:

public class AuthenticationFilter extends GenericFilterBean { 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
     final String authTokenHeader = ((HttpServletRequest)request).getHeader(Constants.AUTH_HEADER_NAME); 

     if (authTokenHeader != null) { 
      SecurityContextHolder.getContext().setAuthentication(createAuthenticationToken(authTokenHeader)); 
     } 

     chain.doFilter(request, response); 
    } 
} 
+0

Привет, Спасибо за ответ. Я достиг решения, аналогичного тому, как вы упомянули. Я скоро отправлю решение. – Raghavendra

Смежные вопросы