我有一个 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 回答
首先是一个挑剔:Base64 是一种编码算法,而不是加密。但我不认为 base64 是问题,那部分看起来很好。
问题是您使用与基本身份验证相同的 URL 和表单登录(
/login
)。请求将首先命中UsernamePasswordAuthenticationFilter
,这将导致身份验证失败,因为没有表单数据。永远不会检查 Authorization 标头。通过针对另一个 URL 路径执行 Basic 身份验证将解决问题。另请注意,
AuthenticationFailureHandler
仅用于表单登录,正确使用 Basic auth 时不会调用它。