RESTful authentication using Spring Security on Spring Boot, and jQuery as a web client

In a previous article, I started touching on some very basic Spring Security-based authentication on top of Spring Boot. That should have given you something to start with… as long as you stuck with Spring MVC, that is.

Indeed, Spring Security gives you all of the tools almost out-of-the-box when securing against Java Server Pages: redirection to the login page, redirection to the right controller after user has authenticated, JSP taglibs, etc.

The fact is, there are a lot of modern web applications that rely on accessing back-end web services while implementing a rich and responsive user experience on the front-end, such as AngularJS-based web applications. And things can get a little trickier when authentication is done using web services: no redirection after login, the returned HTTP status codes are not necessarily the right ones… the web services’ client inherits a lot of responsibilities that were previously handled by the back-end.

In this post, I will do my best to describe one possible solution to implement RESTful authentication on the back-end, accessed a jQuery-based web client on the front-end. I will examine both the back-end and front-end, so that by the end of this tutorial you will hopefully have a starting point for implementing your own solution.

The full code discussed here below is available at https://github.com/codesandnotes/restsecurity for your convenience.

But first, the basics of securing Spring-based REST

Let’s start with a few links and resources I found useful to understand the topic.

To begin with, the Spring Framework has a very convenient way to turn controllers into REST-enabled controllers. Since Spring 4 it’s even easier thanks to the @RestController annotation. Feel free to browse their excellent reference manual.

I haven’t found that much information about securing RESTful services in Spring Security’s reference manual. Instead, I have found a great deal on the topic at Baeldung. He has a full section about REST services with Spring, including securing those services. Check this and this for instance. In fact, <biased advice warning> do yourself a favor and subscribe to his newsletter. I can’t count how many times I dug up one of his articles when I was stuck. </biased advice warning>

Okay, let’s set it up!

So let’s get out hands dirty. We start by configuring a Maven project. We will rely on Spring Boot to provide us with a default, working configuration which we can then bend according to our needs.

As you guessed, we’ll run the application in an embedded Jetty. Note how easy it is to include Spring Security:Β  just add that spring-boot-starter-security starter POM, and off you go!

Spring Boot recommends using Java to configure the application. You can of course use XML files instead, but I like the idea of configuring an application with Java: I find it kind of intuitive.

The class acts as the main entry point for our Spring Boot-based application. You can of course define whatever bean is needed for your application here, but the one that we will focus on is our custom implementation of the WebSecurityConfigurerAdapter, which will allow us to configure Spring Security.

Configuring the back-end security

Here’s what a the implementation of theΒ WebSecurityConfigurerAdapter might look like:

We first configure the authentication aspects by overriding the configure(AuthenticationManagerBuilder builder) method. For our tutorial we set up a simple in-memory authentication context like we did in this post, so we can log in using some test user profiles.

The interesting part comes as we override theΒ configure(HttpSecurity http) method in order to customize our security handling.

  • The 1st statement tells Spring Security that we will intercept all requests matching the specified Ant matcher and make sure that they come from an authenticated user. Any request starting with “/rest/” in the URL will only be accessible to authenticated users. Easy.
  • The 2nd statement disables CSRF (Cross Site Request Forgery). This is understandably enabled by default by Spring Security, but we’ll disable it for simplicity sakes (and because I haven’t had time to figure out how to implement this in a REST context, sorry!). It’s an important aspect of REST security so, by all means, read about it here and here. Also check the solution kindly provided by Allan Ditzel here!
  • The 3rd statement tells Spring Security what happens when an unauthenticated client tries to access a restricted resource. In an MVC application we would be redirected to a login page… But remember: this is a web service! The service should return a 401 HTTP status code and let the client direct its users to some login page. A custom authentication entry point is used to implement that behavior (see below for an implementation example).
  • The 4th statement indicates that we will request the client to submit a user’s login credentials as a form. How convenient, my web client is great at submitting forms! Of course you can also chose to implement Basic HTTP Authentication or Digest Authentication (have a peek here for more information on this). We also specify our custom authentication success handler in case the user successfully authenticates (more on this below).
  • Finally, the 5th and last statement allows to specify a custom authentication failure handler.

Fine, you say, but what’s inside those custom entry point and handlers classes? Why do we need to override them?

Let’s examine each custom class one by one…

The custom authentication entry point

By implementing our own authentication entry point, we can tell Spring Security exactly what to do if someone tries to access a protected resource without being authenticated. But we are dealing with web services here, so what we actually want our system to do is: “if the client accesses a resource but is not authenticated, we respond with a 401 Unauthorized status“.

The above code simply responds with a 401 Unauthorized status code as soon as there’s an authentication problem.

The custom authentication success handler

So let’s say that the client responds by sending some login credentials, and that those credentials are valid: Spring Security then invokes our specified authentication success handler. This is where Spring Security would clear the authentication attributes, apply some redirection strategy to a defined target URL and even retrieve a cached request to submit it (so that after a successful login the system re-submits the request that triggered the redirection to an authentication page; in other words, it allows resuming the flow that was interrupted by a login request).

But, once again, we’re dealing with web services here so we only want the server to respond with a 200 HTTP status that the login was successful. The client will be responsible on what to do next. So here we go then:

There’s nothing more to it, really. We make sure we clean up any authentication attributes left in the HttpServletRequest instance, and by default the method will trigger a 200 status instead of a 301 normally sent by a Spring Security form login.

Okay then, one more handler to go.

The custom authentication failure handler

This is an example of a custom authentication failure handler:

As you can see, there’s not much customization going on: everything is delegated to SimpleUrlAuthenticationFailureHandler. If you don’t need to customize the failure behavior (such as logging the failure or triggering a special action), you can in fact directly use a SimpleUrlAuthenticationFailureHandler instance in your ApplicationSecurity class. Easy!

The REST web service

We need a simple web service that will allow us to test our security setup. Since I’m writing this part of the article on the night of Halloween, I propose you a “HelloweenWebService“. That will change us from the usual “HelloWorld” stuff πŸ˜‰

Nothing exceptional here: the services class is annotated with @RestController, which is a useful Spring 4 shortcut for annotating the class with @Controller and @ResponseBody. We map our hello() method to the “/rest/hello” path and we send a HelloweenResponse as a response entity, which will be automatically converted to JSON courtesy of Jackson and Spring’s HttpMessageConverter.

The important thing to note here is the “/rest” bit in the path: remember we told Spring Security that everything under “/rest/**” is only accessible to authenticated users. This means that if a user tries to access this service without being authenticated, Spring Security will fire our custom authentication entry point and therefore return a 401. Good, that’s exactly the behavior we need!

Also note that we are able to access a Principal instance, automatically filled by Spring Security for authenticated users. Cool!

Okay, let’s move on to the web client now…

The web client

The tricky thing with the web client is that it’ll have much more responsibilities that what we were used to. Mainly, the web client must:

  • detect the 401 response and redirect to a login page
  • submit the login credentials to Spring Security and handle Spring Security’s response
  • redirect the user once he has successfully logged in

So let’s get started!

The secured main page

This simple HTML page and its associated JavaScript code displays a “hello” message that embeds the logged user’s username. This means of course that the user must be logged for this to work, which is exactly what the JavaScript code will make sure of:

The main page’s JavaScript uses jQuery’s ajax() method to send a GET request to our “/rest/hello” service and uses the service’s returned information to display the resulting message inside the #helloweenMessage div tags.

Of course the first time this code runs Spring Security will send back a 401 response. That’s because we protected that resource from unauthorized access. So the fail() method must handle this case and redirect the user to the login.html page.

But even before we redirect the user, we are going to store the details of the request that provoked the login into a cookie. Why? Because we can then use those details to continue where we left of before being asked to log in. In other words, after the client is successfully logged, we can redirect him to this page (or any other page we store in the cookie).

So how do we implement the login page then?

The login page

The login page is a simple form, really. Remember, we are implementing form-based authentication!

However note that instead of specifying an action attribute inside the form tag, we will handle the submission process with our own JavaScript code. This will allow us to control the flow depending on the login service’s response: 200 if the user successfully logged in or 401 if he provided the wrong credentials, according to our implemented custom authentication success or failure handlers.

By default Spring Security exposes a “/login” service which accepts form data to submit credentials. The form parameters must be named “username” and “password” for this to work out-of-the box. Don’t worry, Spring Security allows you to specify different parameter names if you need to.

If the user successfully logs in, we retrieve the pre-login request from the cookie and redirect the user accordingly. Neat, isn’t it? Of course the redirection logic could be improved, for example to handle situations where the user was posting a form: we could store the form and post URL into the cookie and re-submit the form at successful login, before we redirect the user to the next page.

Running our application

So go ahead, start the application by running Application.java’s main() method.

Head to “http://localhost:8080“: you’ll see that you are automatically redirected to the login page. So far so good!

Now type some valid credentials, such as “user/user” or “admin/admin“: you should be redirected to the main “index.html” page, with your username inserted in the “Happy Halloween” message. Great!!!

Now reload the page: you are still logged in, so your Halloween message is still displayed to you and you are not required to log in again. Which is what we need, really. Unless you want to force your user to login at each request, of course, but then be ready to run for your life…

…wait.

How come our application remembers that we’re logged in?

Not quite session-less…

Well, when Spring Security took in the valid credentials, it sent back a cookie that holds a JSESSIONID parameter. The cookie got automatically stored by the browser, which sends it back at every request.

A session is effectively established between our web application and our server, allowing Spring Security to know if the client sending the request is already authenticated or not.

Whether this behavior is acceptable or not depends on what you need.

From here, the world’s your oyster…

This was a very simple proposal on how to implement REST-based authentication for rich, web applications, using Spring Security on the back-end. The example is simplistic of course, but you should be able to build your way from here.

The important points to remember are:

  • For REST-based authentication, some of Spring Security’s behavior and responses must be adapted. This is quite easily done by implementing our own entry points and authentication handlers.
  • The client is responsible of handling the flow, depending on the HTTP responses it gets. We leverage jQuery’s ajax() function for that purpose: it allows is to easily provide the logic for successful and failed authentication.
  • The client is also responsible for redirecting the user after a successful login.

So are we safe then?

…but there’s room for improvement!

This authentication setup (form-based) works fine when clients are web applications. But what about other, non web-based systems? They probably won’t know what to do of a session cookie! So for non-web clients we might need to add Basic HTTP authentication or Digest authentication.

Furthermore, there’s possibly a lot of security aspects to be enforced.This setup would work good for an enterprise application, where clients access the services within an intranet.

For a publicly-available service, there’s still some work to do: use HTTPS for a start, implement CSRF security, etc. A good start would be to have a look at OWASP’s REST Security Cheat Sheet and browse through all topics of that page. They’re professionals: they know what they’re talking about!

Maybe I’ll post another security article in a forthcoming future, where I would try to cover these security aspects… But for the moment we’ll stop here: this tutorial is long enough as it is!

So I hope this post got you started on RESTful authentication. Should you find a bug in the code or if you just want to propose a better way of implementing this, please feel free to drop me a line!

Cheers!

 

BTW: the full code is available here for you to download. Go ahead and feel free to improve it if you want to!

BTW-2: Helloween. Yes, I used to listen to that πŸ˜‰

 

Updated (Nov. 3rd): Allan Ditzel has provided a very neat solution to expose the CSRF tokens generated by Spring Security in the response’s header (see comments below). This way the web client is able to include the CSRF token in, for example, the header of the response sent back to the server. I have updated the tutorial’s code consequently. Kudos to Allan for coming up with that one!

 

26 comments

    • Hi Harold,
      Indeed, I’ve stopped short of the “logout” process for this post. Not really sure why… So here’s the deal: I’m incredibly busy these days, but give me a week and I’ll find some time to write a follow-up post for the logout process AND update the Git code accordingly.
      Stay tuned, and thanks for pointing that out!

      • Abhishek says:

        Hi,
        Thanks a lot for the great tutorial.
        However, I am not able to start up the application, may be the spring configuration files etc. are missing?

        • Hello Abhishek,

          I’ve tried deleting and checking out the project using SourceTree, then I opened the project in IntelliJ (make sure the external libraries are downloaded: in IntelliJ you can use the Maven Projects tab…).

          I’ve then right-clicked on codesandnotes.restsecurity.Application and requested IntelliJ to run it as a simple Java application. The application started without problems. From there I was able to access is by going to http://localhost:8080 (use “user/user” as the username/password.

          Feel free to post more details to see if I can guess where the issue lies (full error message, IDE you use, etc).

          Cheers!

  1. Matt says:

    This code doesn’t work for me in the slightest and I’ve copied and pasted all of it. Every request gets a 401 response even when i send the user and password.

    I’m using curl to test the service and sending this:

    localhost:8080/rest/hello -u user:user

    • Hello Matt,

      It’s hard for me to understand the issue without seeing the code as a whole… I have not tested my code using curl requests. Remember though that the authentication must be done by Spring Security too, because it’ll keep track of your authentication success. This means that a POST has to be sent to the right URL (/login) with the username and passwords in the body for Spring to authenticate you.

      But before you try all the above, you might want to download the full code at https://github.com/codesandnotes/restsecurity . I know that code works for having made the whole process from the GitHub checkout up to making it the whole thing run. It as also been tested by a colleague of mine, so I’m pretty sure about it.

      Maybe you can start from that code base and adapt it step by step to obtain what you need?

      Spring Security and security in general are very touchy: change one line of code and all hell breaks lose. So my advice is don’t give up, start with the code on GitHub and let me know if there’s anything else I can help you with.

      Cheers!

    • Tarmo Kalling says:

      This usually means that for whatever reason your webSecurityConfigurerAdapter bean is not registered. This kind of problem might come up if you try to refactor the webSecurityConfigurerAdapter bean out from the Spring boot main runner class for example and forget to register the new class as a Component. This is what actually happened to me but if I added the Component annotation to the new class, it started working. And it also works with cURL (tested it myself).

  2. Sridhar says:

    I have used the same approach mentioned here. I have Angular js client code running on node js and Spring boot application with the security component mentioned in this article. The authentication was success full from a login page and I see a log, but its session id is null inPrincipal:
    Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@442b7c85: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_USER

    The subsequent requests are getting 401, as some how session is not maintained also the JsessionId cookie was not appearing in the request headers of these subsequent requests. What could be he issue. Thanks

    • Hello Sridhar,
      I’m not sure what you mean by running AngularJS client code on nodeJS… Isn’t Node supposed to run JS for backend?
      Anyways, if the session is not maintained it is because the JSESSIONID is missing in your subsequent requests. When running a client in a browser, the browser automatically gets the cookie with the JSESSIONID, stores it and sends it back at each request. This won’t happen if cookies are disabled in the browser, or if your code is not really running in a browser…

  3. Peter says:

    Great tutorial… works excellent.
    I am dealing with cookie “helloween”. Where is that name set, and how can I change it?

  4. Marc Collin says:

    strangely when i load your apps, i see a web page who display

    Here’s what our Helloween service is saying to you:

    In the console debug mode, i see

    GET http://localhost:8084/rest/hello 404 (Not Found)

    >>>>> When GET /rest/hello fails jqXHR X-CSRF-TOKEN = null
    utils.js:8 >>>>> When GET /rest/hello fails no JSESSIONID cookie was found
    utils.js:16 >>>>> When GET /rest/hello fails no restsecurity cookie was found

    I click on Post something…

    and that fail because

    cookie is null… so can’t read csrf property

    • Hi Marc,
      Say, why is the URL pointing to port 8084? Are you running the application on a Tomcat by any chance?
      The example code is based around Spring Boot. The idea of Spring Boot is that it provides us with a default, ready-to-use environment so we don’t have to take a bunch of details into account. So to run the code, one is meant to run the Application.class main() method: this will start an embedded Jetty server on 8080, load the Spring environment along with some default settings and expose the web application (still on 8080). You will have a server running the Spring web services and the web application on the same port.
      Try to run the app using that Application.class (just create a “standard” Java run configuration in your IDE): I’m quite sure it will solve your problem.
      If not, drop me a line πŸ˜‰

        • Hi Marc,
          This is the strangest thing. Looking at the pom.xml you can see that we enforce Jetty:

          And at startup my console displays:
          2015-10-07 09:16:35.899 INFO 7940 — [ main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port: 8080
          2015-10-07 09:16:35.901 INFO 7940 — [ main] codesandnotes.restsecurity.Application : Started Application in 3.476 seconds (JVM running for 4.245)

          I DID notice however that spring-boot-starter-web is adding a dependency to three Tomcat libraries…
          So I’ve updated the pom to exclude them from the web starter, just in case:

          I’m not sure those extra dependencies are the culprit. Maybe NetBeans insisted on giving priority to Tomcat?
          Check out the latest revisions from GitHub and try again: make sure tomcat is not listed as a dependency anymore. If NetBeans insists on running it with Tomcat, then it means NetBeans is not executing Application.class but rather running the project inside its own Tomcat.
          Cheers!

  5. Marc Collin says:

    is there a way to know if we are connected?

    is ask for this use case:

    a user connect to the site, go to multiple page, return to login without logout. maybe could be a good idea to don’t allow the user to go to login page.

  6. Hi,

    First of all, thank you for the well explained and excellent article on implementing spring security at the backend in the restful web service. There are lot of websites that has either very outdated code or complex to understand. I am a fan of bloggers who keep it simple and also writes to serve the purpose of the topic. I am a blogger myself writing on spring security with spring boot. I wanted to first of all, learn and implement the same approach what you have explained here.

    I have one question though, hope you would not mind to share with me your thoughts on this. This whole approach will work for a public facing application, with the client just using html and javascript (say a mobile app) and everything including authentication is wrapped up in the rest service. My only concern here is, if I go about using this solution, I am sending user credentials in javascript to be verified by the rest service, which I feel is more vulnerable to attacks. I am not a security expert and I don’t even know what type of attack it is or if it is truly a vulnerable one. So can you please share some insights on this? If in case, this is not a good way to send credentials to the rest service, then what would be the best way to send user credentials to rest service using javascript, some kind of encrypting or hashing instead of passing plain username and password?

    Thanks,
    Priya

    • Hello Priya,

      It’s actually a very important point you’re making! I believe the attack you are describing is a MITM (man in the middle) attack: “an attack where the attacker secretly relays and possibly alters the communication between two parties who believe they are directly communicating with each other” (from Wikipedia).

      Sending credentials in JavaScript (or any other means!) comes with risks which one can mitigate by adding layers of security: you can always imagine someone intercepting your data, but we can make it very hard for him/her to do so. But then we have to go further than a simple REST authentication.

      We know we have to send something to the REST service, either credentials to open a session or access tokens if we use OAuth. In any case, there’s an exchange of data!
      So can we encrypt that data? We could use an AES key (symmetrical. Shared between the client and the server): the key is both known by the client and the REST service and is used to encrypt data. That helps preventing MITM attacks… as long as the attacker does not go and find the key in the JavaScript code! Shall we try to avoid writing the key in the JavaScript code? Okay, let’s make a service that sends back a unique AES for that client, which the client can use to encrypt the credentials… Okay but then we need to send its username. Oh and the attacker still could read the AES key that is sent back!
      Let’s move to RSA keys then, which are at the core of PGP! The client uses the REST service’s public key to encrypt the data, and the REST service uses the client public key to reply. It’s slower, but it could work right? Well… the client needs to know the REST service’s public key for that. So we must make sure that the server’s public key obtained by the client is the genuine one, not a fake one that the attacker is serving you… That’s the SSL certification system! Okay but what if the attacker is injecting JS code to steal your credentials? It’s not a MITM attack anymore, it’s actually easier than that…

      Okay, so my point is: the web is hard in terms of protection! The fact that the code of your client application is actually served through the net means that your client application itself can be compromised at any point! We must apply a stack of techniques to prevent an array of attacks. But cheer up, security passionates have found this series of techniques which, combined together, provide a fairly safe set of protection layers!

      First, you could definitely implement CORS (Cross Origin Resources Sharing). This technique basically ensures that the requests received by the server come from the right place: “okay, the request comes from https://my.amazing-app.com, so it should be fine”.
      Second, you can implement CSRF tokens. These tokens are exchanged between the server and the client to ensure that requests are coming from the right client (and not from an attacker). It’s quite tricky to explain in a few lines, but I’ve blogged about this (start here).
      Third, you can actually protect your login page so that, thanks to CSRF tokens, you make sure it’s the client, not the attacker, that’s trying to login (see http://docs.spring.io/spring-security/site/docs/3.2.8.RELEASE/reference/htmlsingle/#csrf-login).
      Finally, there’s SSL certificates. Ideally everything you send through the Internet (credentials included) would be better off on HTTPS.

      This setup should already give you a pretty good protection stack. Unless you’re victim of a the Poodle attack (how cute!). See, one security approach in itself is not safe enough, but once they are combined they do a pretty good job.

      Feel free to read my series of posts explaining CORS and CSRF (). The code is available on GitHub, and it adds these layers of security to a real client-server structure.
      Adding SSL certificates on top should keep you on the safe side. If not, it probably means you’ve become bigger than Facebook or Google πŸ˜‰

  7. Thank you very much for the detailed reply. I have already implemented csrf in spring security (nothing pretty much to implement, spring does everything and we just need to enable it) and now I am going to look at CORS now. As far as passing credentials in javascript is concerned, what I understand from your reply is “it can be considered safe with SSL certificate”. All these combined, reduce potential risks of our application being compromised.

    I am going to write a detailed article on this, with all the knowledge I gathered here and elsewhere on the web. Thank you once again πŸ™‚

Leave a Reply

Your email address will not be published. Required fields are marked *