0

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.

4
  • domain is not the issue, 405 means your server is not allowing you to do a POST request. The tutorial you're following sets some headers in a groovy file. Commented Sep 6, 2015 at 4:03
  • but when I delete the line "<csrf/>" in the spring-security xml, it works! What is going on? Commented Sep 6, 2015 at 4:04
  • I'm sorry, I don't know spring. All I can do is point out what the error means. Commented Sep 6, 2015 at 4:08
  • I did not notice the groovy file. Is there any other way to do this? since I do not have any knowledge about groovy file... Commented Sep 6, 2015 at 4:10

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you!! I followed the links you provided and figured out the way to do that.
0

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.

4 Comments

yes, I don't intend to let other domain to use my post urls. You mean I do not even need to add csrf filter for these urls? Actually I dont have any post for other domain, so I can just cancel the csrf protection?
No csrf is unrelated to domain, its to fight against forging a request by an attacker. Post your class in your question what it's doing com.tong.learn.service.security.CsrfHeaderFilter
After some investigations, I found I misunderstood CSRF with CORS (developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS). Protecting from CSRF attack is important.
thank you, but I found it is easier to change the angularjs side rather than change the spring security side.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.