Java 无法从Base64身份验证获取登录名和密码,spring security
我有一个angularjs前端和后端的spring security 我的登录控制器通过POST请求发送使用Base64算法加密的客户凭据。代码如下:Java 无法从Base64身份验证获取登录名和密码,spring security,java,angularjs,spring,spring-mvc,spring-security,Java,Angularjs,Spring,Spring Mvc,Spring Security,我有一个angularjs前端和后端的spring security 我的登录控制器通过POST请求发送使用Base64算法加密的客户凭据。代码如下: gasStation.controller('LoginController', ['$rootScope', '$scope', '$http', '$window', 'customerInformation', function ($rootScope, $scope, $http, $window, customerInforma
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 security配置:
@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加密的有效登录名和密码,则我的UserDetails服务无法通过其登录名找到客户,从而导致403错误
问题是:Spring如何解码授权头中的登录名和密码?
换言之,当我使用以下代码(不带base64)时:
Spring成功地找到了一个登录的用户,但当我在前端使用btoa加密时,它没有这样做 首先是一个挑剔的问题:Base64是一种编码算法,而不是加密算法。但我不认为base64是问题所在,那部分看起来不错 问题是基本身份验证和表单登录使用相同的URL(
/login
)。请求将首先点击UsernamePasswordAuthenticationFilter
,这将导致身份验证失败,因为没有表单数据。从未检查授权标头。通过对另一个URL路径进行基本身份验证,可以解决此问题
还请注意,
AuthenticationFailureHandler
仅用于表单登录,正确使用基本身份验证时不会调用它。登录时不应发送Base64加密密码。在身份验证管理器中插入编码器bean并发送不带Base64的密码,然后它就可以工作了。这就是为什么登录页面需要https的原因。您能详细说明一下您所说的在您的身份验证管理器中插入编码器bean是什么意思吗?你的意思是我应该在我的spring安全上下文配置中自动连接任何密码编码器吗?不是任何,是具有Base64编码的。我只有XML配置的示例,而没有Java配置的示例。因此,如果我注入此Base64编码器,则密码将从原始编码为Base64编码。是的,没错,而且在数据库中保存密码时,您必须对其进行Base64编码/加密。注入它们告诉Spring我正在使用Base64编码。明白了吗?如果url“/login”中存在问题,那么在我的安全上下文配置中,我应该在哪里声明另一个url(假设“/ajax\u login”)@mr.M您不需要为基本身份验证声明url,它适用于所有未被Spring安全筛选器保留的url:s(登录,注销)。好的。关于URL命名,您是对的。现在,我的UserDetailsService从数据库中查找用户名。然而,我想发回一些200响应状态的数据。我可以在哪里定义一个过滤器并将其放入安全过滤器链以完成此任务?@M先生您可以在控制器中添加映射吗?或者看看这个:谢谢你的建议。每件事都很有魅力!
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;
});
};
}]);