首页 文章

无法从 Base64 身份验证获得登录名和密码,弹出安全性

提问于
浏览
1

我有一个 angularjs 前端和后端的弹簧安全。

我的登录控制器通过 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 无法通过 his/her 登录找到客户,从而导致 403 错误。

问题是:Spring 如何解码 Authorization 标头中的登录名和密码?

换句话说,当我使用以下代码(没有 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 成功找到了 his/her 登录的用户,但是当我在前端使用 btoa 加密时 - 它没有这样做。

1 回答

  • 1

    首先是一个挑剔:Base64 是一种编码算法,而不是加密。但我不认为 base64 是问题,那部分看起来很好。

    问题是您使用与基本身份验证相同的 URL 和表单登录(/login)。请求将首先命中UsernamePasswordAuthenticationFilter,这将导致身份验证失败,因为没有表单数据。永远不会检查 Authorization 标头。通过针对另一个 URL 路径执行 Basic 身份验证将解决问题。

    另请注意,AuthenticationFailureHandler仅用于表单登录,正确使用 Basic auth 时不会调用它。

相关问题