From stateful to stateless RESTful security using Spring and JWTs – Part 2 (session-based authentication)

We’re going to set up a RESTful API which we will secure using Spring Security and session-based (stateful) authentication. We will also write some JUnit tests to verify our application behavior. By the end of this chapter you will have a working, secured RESTful API which we will modify later to bring to its stateless incarnation.

So crack your joints and let’s start coding!

(or point your browser to this chapter’s code on GitHub. Whatever rocks your boat!)

Related posts

Booting up!

This being a Maven project, let’s start with our pom.xml:

In essence, dependency management is used to import Spring Boot’s default dependencies. We then explicitly specify which parts (Spring Boot starters, libraries) we want to use.
For this project we will get everything we need by using five starters:

  • jetty: brings Jetty instead of Tomcat as an embedded server. This is purely a preference thing. Feel free to keep Tomcat if you want!
  • web: includes Spring MVC and all that’s needed to implement our RESTful API using Spring. Please note that the web starter also imports the Tomcat starter! You might want to exclude it, since we are working with Jetty anyway…
  • security: brings in Spring Security on-board
  • logging: sets Logback as the default logger.
  • test: yes, we will test our code. Because we’re professionals, hey!

Let’s not forget to set Java to 8, both in Maven’s properties and in Maven’s compiler plug-in. We also specify Spring Boot’s Maven plug-in so one can start the application from command-line should he need to:

Being a Spring Boot application, we need to define our point-of-entry class, which we’ll name “Application“:

That neat @SpringBootApplication annotation enables Spring Boot’s auto-configuration, component scanning and makes this class a configuration class in case you want to start defining @Beans in there.

Because I’m API…

(OMG that was lame, sorry!)

Yeah… eeeh, let’s work on our RESTful API, shall we?

Nothing extraordinarily fancy in there:

  • The @RestController is another one of those very useful annotations which considers the class as @Controller and every @RequestMapping method as binding the method’s return object to the response’s body, exactly as if the methods were annotated with @ResponseBody.
  • We separate our two GET mappings by defining two paths: “secure” and “unsecure”. Every end point under the “unsecure” path will be made freely accessible, as opposed to every other path.
  • We return ResponseEntity objects carrying a GreetingWebObject. This will be automatically converted to an adequate JSON structure thanks to Jackson, which is imported by Spring boot’s web starter. Nothing else needs to be done, except specifying that we return a JSON value.
  • The secured greetings will be able to leverage the Principal object (aka the logged user’s details) to customize our greetings’ message. Obviously this only works when the user is authenticated.
  • Heads up! Don’t do the same mistake I regularly do: don’t specify a “consumes” property in @RequestMapping. These are GET methods, so we don’t need to have them consume anything. We’ll get 415 errors if we try it. If you need to pass parameters to a GET, you are free to use path variables or request params (aka query params).

Tests first

Normally I would go TDD/BDD and write these before implementing the API code. Then again this is a tutorial, so…

Okay, so we want our unsecured end point to return our message, no authentication needed. On the other hand, we want our secure end point to block our request if we are not logged in. In case we are logged in, then we need our secured end point to return a customized message… that’s the privileged greeting for authenticated users!

We set up a Spring Boot context that relies on an IntegrationTestsApplication class. The tests use Spring Boot’s TestRestTemplate to query the unsecured REST end point, and a custom client class of mine to query the secured end point. That custom class simply allows to authenticate oneself through form-based logins and to leverage those credentials when performing requests on secured end points.

If we run these tests now, a couple of them will fail.
One will not be able to obtain credentials and as such will trigger an ugly NullPointerException on my client class (sorry, I guess I have been a tad lazy on that one!). That’s because by default Spring Boot configures Security to use basic authentication with a default user name and a random password. And yeah, our user/user profile does not exist yet either!
The other error we get happens when accessing our unsecured end point. By default, Spring Boot’s configuration will protect all paths! The mere fact of calling our path “/unsecure” won’t change a thing. We’ll have to tell Spring Security that everything under that path must be freely accessible.

In order to make those tests pass, we’ll have to dig into Spring Security’s configuration.

Security check

Since our Application class is enabling component scan, we can set up our security in a separate configuration class and have it picked up at start up.

The SecurityConfiguration class extends WebSecurityConfigurerAdapter so that selected aspects can be overridden as needed.

The overridden method taking an AuthenticationManagerBuilder as a method parameter allows us to define where our users’ profiles are stored. Normally we would specify an implementation that fetches your users’ profiles from a JDBC database or an LDAP. Since we’re in a tutorial we can go for an in-memory manager, which allows us to define a test profile on-the-fly (“user”. Top marks for originality, right?).

The other overridden method takes an HttpSecurity instance, which allows us to configure our web security. Various aspects of security are being addressed there.

  • We start by disabling CSRF protection. In a few words: CSRF protection makes Spring Security generate and send a token to the browser. The browser has to send it back to prove that he, not someone else, is talking to the server. This is an extra layer of protection that is enabled by default by Spring Security. It can be easily implemented for stateful authentication, as described here. However we want to focus on the basics first, so we’ll disable it for now. Later on, we’ll see how to add CSRF protection in a stateless way.
  • We also tap on exceptions handling. Specifically, we want to redefine the authentication entry point which is going to come into play when an exception due to an unauthorized access is raised. Normally Spring Security would redirect to a login page, but since we’re talking RESTful here, we must return a 401.
  • We’re using the form-based login approach to submit our credentials. Again, we customize the handler that defines what happens in case of login success. The same is done for handling login failure, except that in this case we can get the job done by merely creating a SimpleUrlAuthenticationFailureHandler instance.
  • We redefine the logout, specifying the URL to be called for the logout to happen. Here again we customize what must happen when logging out. For RESTful APIs, it can be summarized to… “do nothing“.
  • Last but not least, we define which paths’ accesses must be restricted. Since we want to follow good practices, we think this the other way: we set everything to restricted access, and we define which paths are accessible without authentication. In our case, that would be everything under “/rest/unsecure/”.

With our security configuration set up, you can now try and run those tests again: they should all be green!

Conclusion

In this chapter we’ve merely set up the essential authentication mechanisms to secure your RESTful server application.
That’s not bad. But we still use a stateful, session-based approach: the server is still maintaining a session object to keep track of authenticated users!

So next time we’ll fix that and move to a token-based authentication implementation.

Until then,

Cheers!

 

Leave a Reply

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