I have a problem with AngularJS. When I call a Rest Services from another domain, the authorization Header is not sending on the Request, so the Spring Security is not recognizing the authentication credentials. Attached the configuration files.
web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<filter>
<filter-name>cors</filter-name>
<filter-class>com.axcessfinancial.web.filter.CorsFilter</filter-class>
<filter-mapping><filter-name>cors</filter-name><url-pattern>/*</url-pattern></filter-mapping>
Context-Security.xml
<http use-expressions="true">
<intercept-url pattern="/**" access="isAuthenticated()" />
<http-basic/>
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="admin" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
CorsFilter
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "Authorization, Accept, Content-Type, X-PINGOTHER");
response.addHeader("Access-Control-Max-Age", "1728000");
}
filterChain.doFilter(request, response);
}
app.js
var app = angular.module('app', ['app.controller','app.services']);
app.config(function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
/* $httpProvider.defaults.headers.common['Authorization'] = 'Basic YWRtaW46YWRtaW4='; */
});
service.js
angular.module('app.services',[]).service('Service', function ($http,$q,UtilHttp) {
$http.defaults.headers.common = {"Access-Control-Request-Headers": "accept, origin, authorization"};
$http.defaults.headers.common['Authorization'] = 'Basic YWRtaW46YWRtaW4=';
return {
listCutomer: function(){
var defer=$q.defer();
$http.post('http://localhost:8088/rest-template/soa/listCustomer',{withCredentials: true})
.success(function(data){
defer.resolve(data);
})
.error(function(data){
defer.reject(data);
});
return defer.promise;
}
};
});
Problem:
Response Headersview source
Content-Length 1134
Content-Type text/html;charset=utf-8
Date Wed, 21 May 2014 14:39:44 GMT
Server Apache-Coyote/1.1
Set-Cookie JSESSIONID=5CD90453C2CD57CE111F45B0FBCB0301; Path=/rest-template
WWW-Authenticate Basic realm="Spring Security Application"
Request Headers
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Access-Control-Request-He... authorization,content-type
Access-Control-Request-Me... POST
Cache-Control no-cache
Connection keep-alive
Host localhost:8088
Origin null
Pragma no-cache
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
I think your problem is the following:
when
your browser (following CORS specs) adds an extra step to the request:
it first sends a particular request with method "OPTIONS" to the URL, if the server respond approving the actual request you want your real request will start.
Unfortunately in your scenario spring returns 401 (unauthorized) to the OPTIONS request because auth token is not present in this request, consequently your real request will never start
Solution:
you can put your cors filter before the spring security filter in your web.xml and avoid calling next filter in the chain (spring security) if request method is OPTIONS
this exaple filter works for me:
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}
remember to declare your cors filter before the spring security filter in your web.xml
Add your filter to the front of the Spring Security filter chain...
<http>
<custom-filter before="CHANNEL_FILTER" ref="myFilter" />
</http>
<beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter"/>
see http://docs.spring.io/spring-security/site/docs/3.0.x/reference/ns-config.html