Невозможно получить логин и пароль от Base64 Authentication, Spring Security

0

У меня есть угловая передняя часть и пружинная защита на заднем конце.

Мой контроллер входа отправляет через POST запрос учетных данных клиента, которые шифруются с использованием алгоритма Base64. Код следующий:

gasStation.controller('LoginController', ['$rootScope', '$scope', '$http', '$window', 'customerInformation',
    function ($rootScope, $scope, $http, $window, customerInformation) {
        $rootScope.Login = function () {
            var encodedData = btoa($scope.username+':'+$scope.password);
            $http.defaults.headers.common['Authorization'] = 'Basic ' + encodedData;

            $http({
                method: 'POST',
                url: '/login',
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                    "X-Ajax-call": 'true'
                }
            })
                .success(function (response) {
                })
                .error(function (response) {

                });
        };
    }]);

На заднем конце у меня есть следующая конфигурация безопасности Spring:

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        // declare all public resources and URIs
        http.authorizeRequests()
                .antMatchers("/pages/index.html", "/pages/public/**", "/resources/css/**", "/resources/img/**", "/resources/js/**").permitAll();
        http.authorizeRequests().antMatchers("/login", "logout").permitAll();
        http.authorizeRequests().antMatchers(HttpMethod.POST, "/register").permitAll();
        http.authorizeRequests().antMatchers(HttpMethod.GET, "/customer_types").permitAll();

        // any other resources and URIs must pass authentication procedure.
        http.httpBasic().and().authorizeRequests().anyRequest().authenticated();
        http.formLogin()
                .successHandler(new AjaxAuthenticationSuccessHandler(customerRepository))
                .failureHandler(new AjaxAuthenticationFailureHandler())
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/pages/index.html");

        http.exceptionHandling().authenticationEntryPoint(new AjaxAuthorizationPoint());
    }

Если аутентификация прошла успешно, я отправляю обратно файл cookie:

public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private CustomerRepository customerRepository;

    public AjaxAuthenticationSuccessHandler(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        int numberOfEntries;
        ObjectMapper objectMapper = new ObjectMapper();
        CustomerInformationDto customerInformationDto = new CustomerInformationDto();

        Customer customer = customerRepository.getByLogin(authentication.getName());
        String customerType = customer.getCustomerType().getTypeName();
        if ("REGULAR".equals(customerType)) {
            numberOfEntries = customer.getVehicles().size();
        } else {
            numberOfEntries = customer.getGasstations().size();
        }

        // create here a cookie and send it back to a client.

        customerInformationDto.setStatus("ok");
        customerInformationDto.setCustomerType(customer.getCustomerType().getTypeName());
        customerInformationDto.setNumberOfEntries(numberOfEntries);

        response.getWriter().print(objectMapper.writeValueAsString(customerInformationDto));
        saveCookie("my god damn cookie","my god damn cookie",response);

        response.getWriter().flush();
    }
    private void saveCookie(String cookieName, String value, HttpServletResponse response) {
        Cookie cookie = new Cookie(cookieName, value);
        //maxAge is one month: 30*24*60*60
        cookie.setMaxAge(2592000);
        response.addCookie(cookie);
    }
}

Если что-то не так, я просто отправлю сообщение об ошибке:

public class AjaxAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
        ObjectMapper objectMapper = new ObjectMapper();
        CustomerInformationDto customerInformationDto = new CustomerInformationDto();
        customerInformationDto.setStatus("Invalid login or password.");
        response.setStatus(403);
        response.getWriter().print(objectMapper.writeValueAsString(customerInformationDto));
        response.getWriter().flush();
    }
}

Однако, если я отправлю действительный логин и пароль, которые зашифрованы с помощью base64, то мой UserDetailsService не сможет найти клиента по его/ее логину, что приведет к ошибке 403.

Возникает вопрос: как Spring декодирует логин и пароль из заголовка авторизации?

Поместите его по-другому, когда я использую следующий код (без base64):

gasStation.controller('LoginController', ['$rootScope', '$scope', '$http', '$window', 'customerInformation',
    function ($rootScope, $scope, $http, $window, customerInformation) {
        $rootScope.Login = function () {
            var postData = 'username=' + $scope.username + '&password=' + $scope.password;
            var url = "http://" + $window.location.host + '/pages/index.html#';


            $http({
                method: 'POST',
                url: '/login',
                data:postData,
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                    "X-Ajax-call": 'true'
                }
            })
                .success(function (response) {

                })
                .error(function (response) {
                    $scope.errorMessage = response.status;
                });
        };
    }]);

Spring успешно находит пользователя с его/ее именем, но когда я использую шифрование btoa на передней панели, он не делает этого.

  • 0
    Вы не должны отправлять зашифрованный пароль Base64 при входе в систему. Вставьте bean-компонент encoder в менеджер аутентификации и отправьте пароль без Base64, тогда он будет работать. Это причина, почему https требуется на страницах входа в систему.
  • 0
    Не могли бы вы уточнить, что вы имеете в виду, говоря, inject the encoder bean in your authentication-manager ? Вы имеете в виду, что я должен автоматически подключать любой кодировщик паролей в моей весенней конфигурации контекста безопасности?
Показать ещё 5 комментариев
Теги:
spring-security
spring
spring-mvc

1 ответ

1
Лучший ответ

Сначала nitpick: Base64 - это алгоритм кодирования, а не шифрование. Но я не думаю, что base64 - проблема, эта часть выглядит отлично.

Проблема в том, что вы используете один и тот же URL-адрес для Basic auth как для входа в форму (/login). Сначала запрос попадет в UsernamePasswordAuthenticationFilter, что приведет к сбою аутентификации, поскольку нет данных формы. Заголовок авторизации никогда не проверяется. При выполнении Basic auth против другого пути URL-адреса будет исправлена проблема.

Также обратите внимание, что AuthenticationFailureHandler предназначен только для входа в форму, он не будет вызываться при правильном использовании Basic auth.

  • 0
    Если проблема в URL "/ login", то где я должен объявить другой URL (предположим, "/ ajax_login") в моей конфигурации контекста безопасности?
  • 0
    @ mr.M Вам не нужно объявлять URL-адрес для базовой аутентификации, он доступен для всех URL-адресов, которые не зарезервированы фильтрами Spring Security (вход, выход из системы).
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню