Аутентификация на основе токена на основе Spring Security

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

@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-TOKEN (который является 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);
} 

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

Ответы

Ответ 1

Вот как я смог реализовать аутентификацию на основе токенов и базовую аутентификацию

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, прежде чем пытаться выполнить базовую проверку подлинности.

Ответ 2

Вы можете попробовать установить свой пользовательский токен 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 );
    }
}