Using @RequestScope With Your API
A Zone Leader talks about how using Spring's @RequestScope allowed objects required at login to be re-used through the life of the request.
Join the DZone community and get the full member experience.Join For Free
You may also like:
To give an example, consider the following JSON example for the User class:
In this example, the freelance writer John "Danger" Doe is linked to three accounts, which appear as child records to the User class. In reality, the User, Role and Account objects are far more populated.
Not seen is a ticketId, which is part of a custom-built (and out of scope) authentication mechanism to keep track of end-user activity, which can expire if unused for a period of time. Of course, logging out will invalidate the ticketId too.
When a RESTful request arrives into the application, the ticketId is used to look up the User id tied to the ticket. In this case, when the ticketId is linked to User id = 10001, the john.doe record will be found.
Additionally, a path parameter for the HTTP request will include the Account id for the request that is being made. So, once the User record is returned, a check can be made to see if the accounts list includes the Account id in the request.
If everything checks out, the request is able to move to the next level — which can check to see if freelance writers do indeed have the necessary clearance to perform the request.
The original implementation of the REST API passed the ticketId and the Account id from the Controller class to the Service implementation being employed. From there, a service call was made similar to what is displayed below:
If the ticketId was not valid or the User did not have access to the Account linked to the accountId, exceptions would be thrown back to the Controller, which would result in a 4xx HTTP Response.
While this approach seemed to work, it had some drawbacks:
- Every service class needed to inject the accountService.
- Every service method needed to perform the security check.
- The ticketId from the request was getting passed into the service layers.
- The service was getting involved in throwing exceptions that the controller, an interceptor or a filter could be handling.
Enter Security Interceptor + @RequestScope
Since the RESTful API was using Spring Boot, a bean was created called UserData which contained the following information:
In order to keep the payload light, the User object was converted to use the UserDto class, which did not include the deep hierarchy of information. Part of the exception process would catch cases where the user did not have access to the accountId provided. So instead, the AccountDto class was being used as well, to avoid including accounts that had nothing to do with the current request.
With the UserData object created, the next step was to update the WebConfig to register the request scope bean:
Next, the SecurityInterceptor class was created, which included a reference to the UserData object:
The preHandle() implementation would handle all of the necessary security checks and performing the conversion from User to UserDto and creating the AccountDto object as well. Once these were created, they were added to the UserData instance using a couple of setters:
In the WebConfig, the SecurityInterceptor was setup as well:
At this point, the necessary security checks have been obscured from the service layer and processed in the background with every request being made to the API.
New Implementation Example
With everything in place, a simple controller would appear as shown below:
If an exception is thrown by the SecurityInterceptor, it will be reflected in the HTTP Response for the API and not go any further. If the request does not encounter an exception in the SecurityInterceptor, the service class would be called:
As shown above, the UserData set in the SecurityInterceptor is available for use — freeing the service layer from having to make that same request again. This is good news since the UserDto in the UserData object is required to call the static SecurityUtils class in order to determine if the user has proper authority to receive the list of invoices.
Source Code Link
A simple example of the approaches employed for the article can be found in the following link at GitLab:
Have a really great day!
Opinions expressed by DZone contributors are their own.