java - 无法从 Base64 身份验证、spring security 获取登录名和密码

标签 java angularjs spring spring-mvc spring-security

我有一个 angularjs 前端和 spring security 后端。

我的登录 Controller 通过 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 加密的有效登录名和密码,则我的 UserDetailsS​​ervice 无法通过客户的登录名找到客户,从而导致 403 错误。

问题是:Spring 如何从 Authorization header 中解码登录名和密码?

换句话说,当我使用以下代码(不使用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 加密时 - 它无法做到这一点。

最佳答案

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

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

另请注意,AuthenticationFailureHandler 仅用于表单登录,正确使用基本身份验证时不会调用它。

关于java - 无法从 Base64 身份验证、spring security 获取登录名和密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33389430/

相关文章:

spring - Openshift Pod自动缩放

java - 在 JOOQ 中,如果我直接使用底层连接,我的事务状态是否保持?

java - 如何绕中心旋转三角形?

java - 制作合适的 Java 数独生成器的问题

angularjs - Angular (1.5.8) 动态组件

java - 在@RestController 上使用 Spring Caching 注解

java - JPS : HQL query to check null attribute

javascript - 在 AngularJS 中使用 UI-Router 将状态重定向到默认子状态

javascript - mongodb 搜索查询 - 返回所有结果(也包括不匹配的结果)

java - 使用Spring Security中的PasswordEncoder