4

I'm developing an application using Extjs-6 with Spring 4. My Application is Restful.
I enable CORS Origin as follow:

public class CorsFilter extends OncePerRequestFilter {

    private static final String ORIGIN = "Origin";


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        response.addHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.addHeader("Access-Control-Max-Age", "10");

        String reqHead = request.getHeader("Access-Control-Request-Headers");

        if (!StringUtils.isEmpty(reqHead)) {
            response.addHeader("Access-Control-Allow-Headers", reqHead);
        }

        if (request.getMethod().equals("OPTIONS")) {
            try {
                response.getWriter().print("OK");
                response.getWriter().flush();
            } catch (IOException e) {
            e.printStackTrace();
            }
        } else{
            filterChain.doFilter(request, response);
        }
    }
 }

Filter config:

<security:http use-expressions="true">
    ...
    <sec:custom-filter ref="CorsFilter" before="HEADERS_FILTER"/>
</security:http>

And the Bean:

<bean id="CorsFilter" class="..." />

I want to users loging with an AJAX request. I test ajax request using Advanced REST client and http requester. Results of extensions are as follow:

enter image description here

enter image description here

Ext Ajax request code is as follow:

Ext.Ajax.request({
    url: "http://localhost/Calk/j_spring_security_check",
//      params: {
//          "j_username": "ali",
//          "j_password": "123456"
//      },
    params: "j_username=ali&j_password=123456",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    method: "POST",
    success: function(){...},
    failure: function(){...}
});

When I send the request, it get 200 OK, and I init the application in client side, And send some requests to get some data. But server for all this requests get 401 Unauthorized.

Whats the problem?

Important Update:

Logining request is as follow:

enter image description here

Server set cookie in response, and getting authorized data request is as follow:

enter image description here

Why?

Morteza Malvandi
  • 1,656
  • 7
  • 30
  • 73
  • Did you try with `params: form.getValues()`? – qmateub Apr 13 '16 at 10:29
  • yes, result is as above. – Morteza Malvandi Apr 13 '16 at 10:36
  • The problem is that the Request Method "OPTIONS" is used, which is always sent without the parameters you require for authentication. [Check here why that method is used](https://www.sencha.com/forum/showthread.php?95656-how-to-disable-Ajax.request-method-quot-OPTIONS-quot&p=452480&viewfull=1#post452480). – Alexander Apr 13 '16 at 12:05
  • Read the linked forum thread to the end. You can't have finished it in about 2 minutes. – Alexander Apr 13 '16 at 12:08
  • The problem is not answered – Morteza Malvandi Apr 13 '16 at 12:16
  • The session cookie is restricted to `/Calk/` path, is your page with ajax located under this path? Inspect your ajax call, see what cookie is being set the first time in the response header, and whether it is being sent back in the second request header. – serg Apr 17 '16 at 06:35
  • My page and my application are in different path. Page: `localhost/Calk` and Application: `localhost:8084/Calk` – Morteza Malvandi Apr 17 '16 at 07:00

3 Answers3

2

The reason for your error is you are making a cross domain request. I think your javascript application is running in different port while your application server is running on 8084.

For this to work you need to enable CORS support for your application. Here is how you can do that.

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }
}

Follow this link for detailed explanation. For the most part this should work for you, if spring-security is still complaning then you may need to add filter for spring security. You can do that by following this answer

public class CorsFilter extends OncePerRequestFilter {

    private static final String ORIGIN = "Origin";


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        if (request.getHeader(ORIGIN) == null || request.getHeader(ORIGIN).equals("null")) {
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.addHeader("Access-Control-Max-Age", "10");

            String reqHead = request.getHeader("Access-Control-Request-Headers");

            if (!StringUtils.isEmpty(reqHead)) {
                response.addHeader("Access-Control-Allow-Headers", reqHead);
            }
        }
        if (request.getMethod().equals("OPTIONS")) {
            try {
                response.getWriter().print("OK");
                response.getWriter().flush();
            } catch (IOException e) {
            e.printStackTrace();
            }
        } else{
            filterChain.doFilter(request, response);
        }
    }
 }

Configure the Filter for Security

<security:http use-expressions="true" .... >
    ...
    //your other configs
    <sec:custom-filter ref="corsFilter" before="HEADERS_FILTER"/>
</security:http>

Expose the Filter as Bean

<bean id="corsFilter" class="<<location of the CORS filter class>>" />
Community
  • 1
  • 1
JChap
  • 1,421
  • 9
  • 23
  • I have some stores that all of them get data from same web server I set CORS config and it get all data correctly. But only Login request don't work correctly – Morteza Malvandi Apr 14 '16 at 04:56
  • Then try the Security Filter, Updated the Answer. – JChap Apr 14 '16 at 05:03
  • I updated my code according to your response. When I send Login Ajax Request, Server get `200 OK`(when I send incorrect user/pass, it get `401 Authorized`). When user Login successfully, page get some data usign ajax, But in next request server get `401 Authorized`. – Morteza Malvandi Apr 16 '16 at 12:45
  • Hmm it may be because it is creating a new session. Can you confirm.. if the session is same for both the login request and when you are sending data request ? – JChap Apr 16 '16 at 22:59
  • I don't know. But when I use http requester application work correctly. – Morteza Malvandi Apr 17 '16 at 03:26
1

Spring Security works as follows:
If any request coming from a client have no JSessionID cookie, Spring will reset that client's session and set an invalid session for it.Then the next request will be sent with that invalid session.

So you need to set cookie in all of the Ext ajax requests. I guess some concurrent ajax requests do not send cookie. In order to set cookie in all of ajax requests, you can add this event:

Ext.Ajax.on("beforerequest",function(con){
  con.setUseDefaultXhrHeader(false);
  con.setWithCredentials(true);
}

This function runs before sending all your ajax requests.

Dariush Jafari
  • 5,223
  • 7
  • 42
  • 71
0

I think the problem is that the session cookie is being set for /Calk/ path while the ajax call is made from /Workspace/Calk/. So you need to find where session cookie path is configured in Spring and change it to be just /.

I haven't used Spring security, but from a quick search this and this questions seem to suggest that you can change the path in web.xml:

<web-app>
    <session-config>
        <cookie-config>
            <path>/</path>
        </cookie-config>
    </session-config>
</web-app>
Community
  • 1
  • 1
serg
  • 109,619
  • 77
  • 317
  • 330