Spring Security is indispensable part of every enterprise Spring based application. Everything looks pretty easy, when you want to secure single web-application. Unfortunately, everything looks completely different when your system consists of many components. In that cases the expected behavior is single login for whole system, not separate login for each web-services. Suppose, you have two RESTful web-services. Application should allow a user to login, receive session token and give access to resources of both services using the token.
At this point we come across the first problem. Spring does not support authentication by JSON object out of the box. In order to send credentials as json object, we have to write some handlers. But let’s start from the beginning.
*All sources for this project you can find on my github repository:
The client1 and the client2 will be RESTful web-services with single endpoint. The security-config will be heart of this article - this project will include whole security stuff, some JSON handlers and filters as well.
client1 and client2 pom.xml
Once we have the basic projects configuration, we can proceed to something more interesting. The first thing will be configuring spring security in our project.
For this purpose we’ll create SecurityConfig class which extends WebSecurityConfigurerAdapter.
As you can see, there are two overridden configure methods. Inside the first one I have added some filters, handlers, and I have indicated that all request should be protected (authorizeRequests().antMatchers(“/**”).authenticated()). I’ve also configured login endpoint as (“/authenticate”). We will back in a moment to discuss the AuthenticationFailureHandler, AuthenticationSuccessHandler and CustomUsernamePasswordAuthenticationFilter. First, I would like to take a minute to highlight the AuthService class. It’s simple service class which implements org.springframework.security.core.userdetails.UserDetailsService interface. The class is responsible for provide information about user. Example implementation:
As you can see, current implementation allows to login users: ‘user’ and ‘admin’. This implementation makes sense only for tests, and production implementation should invoke some repositories to get real user details.
The key thing that is needed to send credentials as JSON object is writing custom filter which extends UsernamePasswordAuthenticationFilter.
This filter provides information about username and password for security spring. But, what about situation when credentials are incorrect.
RESTful service should return appropriate Http code and error as a JSON object. To do this, we have to create AuthenticationSuccessHandler:
In the case of valid credentials, we should return response as a JSON object too. In the following example, I return object consists of userNo, username and list of roles.
At this moment, we have a fully working configuration of Spring Security with JSON credentials. Now, you can add endpoint and configuration to client1 application:
You can run main method from your IDE or execute the following command in your terminal:
mvn spring-boot:run -Dserver.port=8099
If you try to invoke the endpoint http://localhost:8099/time , you will receive the following error:
It shouldn’t be a surprise, because we have forgotten about authorisation. We have defined authentication endpoint as ‘\authenticate’, so let’s send credentials there
Result is easy to predict:
Once we have a session token, we can try invoke curl http://localhost:8099/time --cookie "SESSION=449b0dce-cad9-4aa5-9b27-8896b20265ae" once again. This time with success. Unfortunately, when you try to invoke similar endpoint for client2, you will receive “Full authentication is required to access this resource” error. This is understandable, because the security context is not shared. So what should we do to have such an opportunity? We should use redis as database of session tokens. First of all, you need to install redis on your computer. If you use Windows, you will find appropriate installer here: https://github.com/MSOpenTech/redis/releases
Installers for other systems are available here: http://redis.io/download
To use Redis in our application we need to add dependency to security-config pom.xml:
Spring configuration is quite simple: one annotation @EnableRedisHttpSession above SecurityConfig class, and two beans within this class:
Let’s try invoke \authenticate endpoint once again. Response is similar to previous one, but there is one additional x-auth-token header.
If you want to invoke http://localhost:8099/time, you have to remember resend x-auth-token. You can also invoke any endpoint on the client2 by using the token generated within the client1. Everything will work correctly.
All sources for this project you can find on my github repository: