I'm about to implement an AngularJS application backed by RESTful services served by an Java EE container (JBoss). I managed to automate the build so that the angular app gets packaged (including running Compass, usecss, etc) with Grunt.js and is then included into the war file build with maven.
The access to the application itself and the REST services are protected by a form based authentication configured with the web.xml, e.g.:
<security-constraint>
<web-resource-collection>
<web-resource-name>AngularApplication</web-resource-name>
<url-pattern>/index.html</url-pattern>
<url-pattern>/services/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>HEAD</http-method>
<http-method>PUT</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>User</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<description>User has to be authenticated.</description>
<role-name>User</role-name>
</security-role>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>
When I run the appliction from within the deployed application, everything works fine - after authenticating with the login form, the angular app loads and is able to access the services.
But for development this is a bit too heavy-weight as the loop to see changes in the app (rebuild and redeploy everything) would take long. So for the server-side I can use hot swapping of the java classes, fine. For angular, I thought I could use a locally served application wired together with livereload by the "grunt serve" task.
But here my problem starts: As the app is not loaded from the java server, my service calls are not successful as I'm not authenticated. Trying to authenticate with an $http.post() seems to be successful (response has HTTP-Status 200 and contains a header set-cookie JSESSIONIDSSO=.....; Path=/), but this cookie is not applied - if I got it right because of XSRF security mechanisms. The Problem seems to be, that in this setup I'm requesting the authentication via a page that is served by another host (localhost:8080 vs. localhost:9000). I tries to set the HTTP-headers on server side
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
servletResponse.setHeader("Access-Control-Allow-Credentials", "true");
servletResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
as well as tried to convince Angular to accept the call, with eg.
$httpProvider.defaults.useXDomain = true;
or
delete $httpProvider.defaults.headers.common['X-Requested-With'];
But this didn't help me to proceed.
Also, trying to switch from a cookie to URL parameter with
<session-config>
<tracking-mode>URL</tracking-mode>
</session-config>
didn't help. That prevents me from logging in at all and show an wierd error message:
"HTTP Status 408 - The time allowed for the login process has been exceeded. If you wish to continue you must either click back twice and re-click the link you requested or close and re-open your browser"
So I'm stuck here. I'm looking for a easy to handle solution to use "grunt server" during development. Do you have any suggestions what would be the best practice here? Do I need to establish an token-based authentication instead of the FORM based? What would be the best practice here?
Yes, I have read the thread at How to proper authenticate an AngularJS client to the server but that basic auth approach seems like I would have the switch the whole this to Basic auth just b/c of the development mode with "grunt serve". What would you guys suggest?
We had exactly the same issue in our development. Just add this to your Angular app:
$httpProvider.defaults.withCredentials = true;
And in your server you have to specify the origin but not wild card since allow-credentials set to true.
servletResponse.setHeader("Access-Control-Allow-Origin", "http://localhost:9000");
servletResponse.setHeader("Access-Control-Allow-Credentials", "true");
if you are running grunt in port 9000.
With this the JSESSIONID should be set properly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With