I got an issue related to the HTTP response header "Access-Control-Allow-Origin" when using basic authetication with Spring. When I authenticate manually, like the code bellow (I'm using REST):
@RequestMapping(value = "/login", method = RequestMethod.POST, consumes = "application/json") @ResponseStatus(value = HttpStatus.OK) public void login(@RequestBody String body, HttpServletResponse response) throws IOException { try { User user = gson.fromJson(body, User.class); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( usuario.getUsername(), usuario.getPassword()); authenticationManager.authenticate(token); } catch (BadCredentialsException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } catch (Exception e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } everything works fine, I receive the following HTTP response:
HTTP/1.1 401 Unauthorized Server: Apache-Coyote/1.1 Access-Control-Allow-Origin: null Access-Control-Allow-Credentials: true Content-Type: text/html;charset=utf-8 Content-Length: 951 Date: Fri, 17 May 2013 19:14:36 GMT as you can see, "Access-Control-Allow-Origin" is present on the response. Everything is fine here. I can catch a 401 error in my ajax call.
But when the authentication is performed automatically, like the code bellow:
@RequestMapping(value = "/name", method = RequestMethod.POST, consumes = "application/json") @PreAuthorize("hasRole('ROLE_CUSTOMER')") public @ResponseBody String getName(HttpServletResponse response) throws IOException { String json = null; try { User userSession = (User) SecurityContextHolder.getContext() .getAuthentication().getPrincipal(); Customer customer = customerDao.getNameByUsername(userSession.getUsername()); json = gson.toJson(customer); } catch (Exception e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } return json; } the HTTP response is:
HTTP/1.1 401 Unauthorized Server: Apache-Coyote/1.1 WWW-Authenticate: Basic realm="Spring Security Application" Content-Type: text/html;charset=utf-8 Content-Length: 981 Date: Fri, 17 May 2013 19:41:08 GMT There is no "Access-Control-Allow-Origin" in the response
Google Chrome console show the following error:
Origin null is not allowed by Access-Control-Allow-Origin My ajax call does not return a 401 Unauthorized error, even though the HTTP response return it (response above), I receive an unknow error.
I figured out that for all browsers, I need a "Access-Control-Allow-Origin" in the HTTP response, otherwise they will generate some kind of silent error and my ajax call will fail (can't catch the 401 error). Actually, javascript will fail silently. XMLHttpRequest does not accept an HTTP response without "Access-Control-Allow-Origin".
How can I make Spring inject this "Access-Control-Allow-Origin" in HTTP responses for basic authentication?
this is my Spring Security xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <security:http create-session="stateless" entry-point-ref="authenticationEntryPoint"> <security:intercept-url pattern="/customer/**" /> <security:http-basic /> <security:custom-filter ref="basicAuthenticationFilter" after="BASIC_AUTH_FILTER" /> </security:http> <bean id="basicAuthenticationFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter"> <property name="authenticationManager" ref="authenticationManager" /> <property name="authenticationEntryPoint" ref="authenticationEntryPoint" /> </bean> <bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint"> <property name="realmName" value="teste.com" /> </bean> <!-- It is responsible for validating the user's credentials --> <security:authentication-manager alias="authenticationManager"> <!-- It is responsible for providing credential validation to the AuthenticationManager --> <security:authentication-provider> <security:password-encoder ref="passwordEncoder" /> <security:jdbc-user-service data-source-ref="mySQLdataSource" users-by-username-query="select username, password, enabled from usuario where username = ?" authorities-by-username-query="select username, papel from autoridade where username = ?" /> </security:authentication-provider> </security:authentication-manager> <bean class="org.springframework.security.crypto.password.StandardPasswordEncoder" id="passwordEncoder" /> </beans>