Сконфигурируйте несколько типов аутентификации с помощью весенней безопасности для Basic Auth & JWT - PullRequest
2 голосов
/ 24 сентября 2019

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

1) Использование JWT для всех URL-адресов запроса, кроме 1, который должен быть защищен с помощью Basic Auth

2)Базовая аутентификация для одного URL.

У меня есть настройки безопасности как для JWT, так и для базовой аутентификации.Моя проблема в том, что когда я делаю запрос к URL-адресу Basic Authenticated, используя действительные имя пользователя и пароль, он успешно аутентифицирует меня и выполняет свою работу по хранению данных в cassandra.

Затем я собираюсь сгенерировать токен для ВСЕХ URL-адресов других запросов через / api / login и добавить его в заголовок Authorization: Bearer {Token}.

Однако, если бы я былаутентифицированный с помощью обычной аутентификации, я могу получить доступ к другим URL-адресам (защищенным аутентификацией JWT), даже не имея токена в запросе.

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

Стоит ли ожидать этого?Как я полагаю, несмотря на то, что я прошел проверку подлинности с помощью базовой аутентификации для одной конечной точки, мне все равно придется отправлять токены в запросе для всех других защищенных конечных точек JWT.

Я нашел такой ответ: множественная аутентификация SpringBootАдаптер

, а также эту статью: https://docs.spring.io/spring-security/site/docs/4.2.x/reference/htmlsingle/#multiple-httpsecurity

и попытался реализовать решения, но проблема, как объяснено, все еще возникает.

Класс конфигурации безопасности имеет видследует:

@Configuration
@EnableWebSecurity
public class SecurityHttpConfig extends WebSecurityConfigurerAdapter {

    @Configuration
    @Order(1)
    public static class BasicAuthSecurityConfig extends WebSecurityConfigurerAdapter {

        @Value("${basic.auth.user}")
        private String basicAuthUsername;

        @Value("${basic.auth.password}")
        private String basicAuthPassword;

        @Value("${crashboxx.consume.endpoint}")
        private String crashBoxxConsumeEndpoint;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable().authorizeRequests().antMatchers("/v1/crash/consumeCrashBoxxEvent").hasRole("ADMIN").and()
                    .httpBasic().authenticationEntryPoint(getBasicAuthEntryPoint()).and().sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);// We don't need sessions to be created.
        }

        @Bean
        public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint() {
            return new CustomBasicAuthenticationEntryPoint();
        }

        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
            auth.inMemoryAuthentication().withUser(basicAuthUsername).password(encoder.encode(basicAuthPassword))
                    .roles("ADMIN");
        }

        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }

    @Configuration
    @Order(2)
    public static class JwtWebSecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        private JwtAuthenticationEntryPoint unauthorizedHandler;

        @Autowired
        private JwtAuthenticationProvider jwtAuthenticationProvider;

        // Any endpoints that require no authorization should be added here..
        @Value("${api.login.endpoint}")
        private String loginEndpoint;

        @Autowired
        public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) {
            authenticationManagerBuilder.authenticationProvider(jwtAuthenticationProvider);
        }

        @Bean
        public JwtAuthenticationTokenFilter authenticationTokenFilterBean() {
            return new JwtAuthenticationTokenFilter();
        }

        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                    .authorizeRequests().antMatchers("/api/login").permitAll().anyRequest().authenticated();

            httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
            httpSecurity.headers().cacheControl();
        }
    }

С классом BasicAuthEntryPoint:

public class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {

    private static final Gson gson = new Gson();

    @Override
    public void commence(final HttpServletRequest request, final HttpServletResponse response,
            final AuthenticationException authException) throws IOException, ServletException {
        // Authentication failed, send error response.
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        PrintWriter writer = response.getWriter();
        writer.println(gson.toJson("HTTP Status 401 : " + authException.getMessage()));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        setRealmName("Realm");
        super.afterPropertiesSet();
    }

Также JWT impl:

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Value("${jwt.header}")
    private String tokenHeader;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        final String requestHeader = request.getHeader(tokenHeader);
        // Ensure Auth Header contains 'Bearer'
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            String authToken = requestHeader.substring(7);
            JwtAuthentication authentication = new JwtAuthentication(authToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        chain.doFilter(request, response);
    }

Я надеюсь, что это имеет смысл .. Если естьеще вопросы, пожалуйста, дайте мне знать, но, кажется, не могу обойти этот вопрос.

Сначала я добавил «особый случай», который является URL-адресом для базовой аутентификации, но все равно не имеет никаких различий.

Спасибо

Ответы [ 2 ]

1 голос
/ 25 сентября 2019

Это было решено с помощью информации, предоставленной в ответе выше от Правина Кумара Лаласанги.

Небольшое изменение в методе configure помогло. Обновления:

@Override
    protected void configure(HttpSecurity http) throws Exception {
      http.csrf().disable()
      .antMatcher(crashBoxxConsumeEndpoint).authorizeRequests().anyRequest()
      .hasRole("ADMIN")
      .and().httpBasic().authenticationEntryPoint(getBasicAuthEntryPoint())
      .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
1 голос
/ 25 сентября 2019

Код, который вы разместили в конфигурации безопасности @Order(1)

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable().authorizeRequests().antMatchers("/v1/crash/consumeCrashBoxxEvent").hasRole("ADMIN").and()
            .httpBasic().authenticationEntryPoint(getBasicAuthEntryPoint()).and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

Если это именно тот код, который вы используете, то с вашей конфигурацией @Order(2) не будут обращаться.Это будет мертвая конфигурация.
Позвольте мне объяснить!
http.authorizeRequests() == http.antMatcher("/**").authorizeRequests()

В вашей первой конфигурации вы используете подстановочный знак, и ваш результат конфигурации равен

  • /v1/crash/consumeCrashBoxxEvent доступный, если пользователь аутентифицирован и имеет роль ADMIN
  • Rest of URL's доступный, если пользователь аутентифицирован

Позвольте мне угадатьчто происходит!
1. Вы нажимаете URL /v1/crash/consumeCrashBoxxEvent или any URL. Вам будет предложено ввести базовую аутентификацию.
2. После успешной аутентификации вы можете получить доступ к любому URL, потому что вы аутентифицированный пользователь.

Однако, если я прошел аутентификацию через Basic Auth, я могу получить доступ к другим URL-адресам (защищенным JWT-аутентификацией), даже не имея токена в запросе.

Потому что какЯ сказал, что вы можете получить доступ к любому URL, потому что вы являетесь аутентифицированным пользователем

Когда я получаю доступ к защищенным JWT URL без аутентификации с помощью Basic Auth, я должен отправить токен в заголовке иработаетОжидается

Проверка без токена вы можете получить доступ или нет.Потому что однажды, если вы войдете в систему с помощью обычной аутентификации, не будет выхода со стороны сервера (даже если вы перезапустите сервер).Вы можете выйти из системы, только если закроете браузер.Таким образом, вы проверяете это, закрывая и снова запуская браузер.И протестируйте его, не посылая токен JWT.
Также убедитесь, что ваш запрос достигает JwtAuthenticationTokenFilter, Поставьте журналы отладки для проверки.

Поскольку в вашем вопросе много абстракций, очень трудно предсказать, что именнопроизойдет, если вы не отправите свой полный код.

Дайте мне знать в комментариях, если мои прогнозы отклонились от реальных.

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