Reputation: 179
I have a angularjs application. This step is for registering a user, so it is a POST method. The angularjs service for submitting the registration form is as follow:
homeApp.factory('mainService', ['$http', function($http) {
var mainService = {};
mainService.signupArtist = function(data){
var promise = $http.post('../user/register/artist', data) .then(function(response) {
return response.data;
});
return promise;
}
return mainService;
}]);
I have my spring security set up as this:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
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.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<http auto-config="false" use-expressions="true" entry-point-ref="customAuthenticationEntryPoint">
<intercept-url pattern="/artist/**" access="hasRole('ROLE_ARTIST')" />
<intercept-url pattern="customer/**" access="hasRole('ROLE_CUSTOMER')" />
<!-- access denied page -->
<access-denied-handler error-page="/user/403" />
<logout invalidate-session="true" logout-success-url="/user/login" />
<custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
<!-- enable csrf protection -->
<csrf/>
<custom-filter after="CSRF_FILTER" ref="csrfHeaderFilter" />
<csrf token-repository-ref="csrfTokenRepository" />
</http>
<!-- Select users and user_roles from database -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="authenticationProvider" >
</authentication-provider>
</authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<beans:bean id="customAuthenticationEntryPoint"
class="com.tong.learn.service.security.CustomAuthenticationEntryPoint">
<beans:property name="loginPageUrl" value="/user/login" />
<beans:property name="returnParameterEnabled" value="true" />
<beans:property name="returnParameterName" value="r" />
</beans:bean>
<beans:bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
<!-- change here if customize form action
handler are for login with ajax POST -->
<beans:property name="authenticationFailureHandler"
ref="securityLoginFailureHandler" />
<beans:property name="authenticationSuccessHandler"
ref="securityLoginSuccessHandler" />
<beans:property name="passwordParameter" value="password" />
<beans:property name="usernameParameter" value="username" />
</beans:bean>
<beans:bean id="securityLoginSuccessHandler"
class="com.tong.learn.service.security.SecurityLoginSuccessHandler">
</beans:bean>
<beans:bean id="securityLoginFailureHandler"
class="com.tong.learn.service.security.SecurityLoginFailureHandler">
<beans:property name="defaultFailureUrl" value="/user/login" />
</beans:bean>
<beans:bean id="csrfHeaderFilter"
class="com.tong.learn.service.security.CsrfHeaderFilter" >
</beans:bean>
<beans:bean id="csrfTokenRepository" class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository">
<beans:property name="headerName" value="X-XSRF-TOKEN" />
</beans:bean>
</beans:beans>
These are from a link of Spring website.
When I submit the form, the server gives me a 405 error, which is unexpected since I have always been on the localhost, rather than requesting something from different domain. The cookie should be correct.
The request and response are as follow:
General
Remote Address:[::1]:8080
Request URL:http://localhost:8080/learn/user/register/artist
Request Method:POST
Status Code:405 Method Not Allowed
Response Headers
view source
Allow:GET
Content-Language:en
Content-Length:1090
Content-Type:text/html;charset=ISO-8859-1
Date:Sun, 06 Sep 2015 03:48:16 GMT
Server:Apache-Coyote/1.1
Request Headers
view source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4
Connection:keep-alive
Content-Length:2
Content-Type:application/json;charset=UTF-8
Cookie:JSESSIONID=2E77A6690C8F56BFA8F51AF37974BA1B; XSRF-TOKEN=2dfa09e4-9201-4b14-b2d3-fea8a9117be3
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/learn/home/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
X-Requested-With:XMLHttpRequest
X-XSRF-TOKEN:2dfa09e4-9201-4b14-b2d3-fea8a9117be3
I dont know what is wrong. Can you help me? Thanks.
Upvotes: 0
Views: 1343
Reputation: 882
Update:
After some investigations, I found I misunderstood CSRF with CORS (https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). Protecting from CSRF attack is important. I think only when CSRF can be prevented by other ways, you could bypass the CsrfFilter.
=====================================================
I met similar issues before. According to this post , once SCRF is enabled, all http requests will be intercepted by CsrfFilter
, who contains a private class DefaultRequiresCsrfMatcher
.
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
/* (non-Javadoc)
* @see org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.servlet.http.HttpServletRequest)
*/
public boolean matches(HttpServletRequest request) {
return !allowedMethods.matcher(request.getMethod()).matches();
}
}
For GET|HEAD|TRACE|OPTIONS
methods it will just pass through, while POST
method will be checked against its CSRF token.
So if you don't intend to expose your POST URLs to other domains by using CSRF, just implement another Matcher for CSRFFilter to bypass POST method or some URL patterns.
Upvotes: 0
Reputation: 1395
UPDATE:
Check this similar SO question which has example angularjs http interceptor getting directly csrf params and adding in the headers in all requests.
REASON for default angularJS doesn't work as mentioned in the link
By default AngularJS provides a mechanism to implement Cross Site Request Forgery, however this mechanism works with cookies only. Since Spring Security works by setting a token as an HTTP parameter, the out of the box solution AngularJS provides wouldn’t work.
As mentioned in the documentation of spring-security-csrf-token-interceptor project - "An AngularJS interceptor that sets the Spring Security CSRF token information in all HTTP requests" - it works by making a head call to receive the X-CSRF-TOKEN, it then stores this token and sends it out with every http request.
Summary : Add angular interceptor mentioned in the above project in yours to fix your case. Check the sample mentioned in that github project.
Upvotes: 1