I have an angularjs front end and spring security at the back end.
My login controller sends via POST request the customer credentials that are encrypted using Base64 algorithm. The code is the following :
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) {
});
};
}]);
At the back end I have the following configuration of the 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());
}
If authentication is successful then I send back a 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);
}
}
If something is wrong then I simply send back an error message:
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();
}
}
However, if I send a valid login and password that are encrypted with base64 then my UserDetailsService cannot find a customer by his/her login thus causing me a 403 error.
The question is: how does the Spring decode the login and password from the Authorization header?
Put it in another way, when I use the following code (without 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 successfully finds a user with his/her login, but when I use a btoa encryption at front end - it fails to do so.