DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • How To Build Web Service Using Spring Boot 2.x
  • How to Introduce a New API Quickly Using Micronaut
  • Efficient API Communication With Spring WebClient
  • How to Merge HTML Documents in Java

Trending

  • Event Driven Architecture (EDA) - Optimizer or Complicator
  • Build a Simple REST API Using Python Flask and SQLite (With Tests)
  • How to Create a Successful API Ecosystem
  • Event-Driven Microservices: How Kafka and RabbitMQ Power Scalable Systems
  1. DZone
  2. Data Engineering
  3. Databases
  4. Using @RequestScope With Your API

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.

By 
John Vester user avatar
John Vester
DZone Core CORE ·
Jan. 27, 20 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
88.3K Views

Join the DZone community and get the full member experience.

Join For Free

Look through the @RequestScope!

As a part of creating a RESTful API for a conversion project, the existing security in place leveraged a deep hierarchy in the object containing users accessing the API via an Angular client. 

You may also like:

To give an example, consider the following JSON example for the User class:

JSON




x
2
25


 
1
{
2
   "id":10001,
3
   "username":"john.doe",
4
   "firstName":"John",
5
   "middleName":"Danger",
6
   "lastName":"Doe",
7
   "role":{
8
      "id":101,
9
      "name":"Freelance Writer"
10
   },
11
   "accounts":[
12
      {
13
         "id":90001,
14
         "name":"Publication One"
15
      },
16
      {
17
         "id":90002,
18
         "name":"Publication Two"
19
      },
20
      {
21
         "id":90003,
22
         "name":"Publication Three"
23
      }
24
   ]
25
}


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.

Original Implementation

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:

Java
x
 
1
accountService.getAccountById(Long accountId, String ticketId);


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:

Java
 




x


 
1
@Data
2
public class UserData {
3
    private UserDto user;
4
    private AccountDto account;
5
}


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:

Java




xxxxxxxxxx
1


 
1
@Bean
2
@RequestScope
3
public UserData requestScopeUserData() {
4
    return new UserData();
5
}



Next, the SecurityInterceptor class was created, which included a reference to the UserData object:

Java




x


 
1
public class SecurityInterceptor extends HandlerInterceptorAdapter {
2
    @Resource(name = "requestScopeUserData")
3
    private UserData userData;
4
}



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:

Java




x


 
1
userData.setUser(userService.getUserFromTicketId(ticketId));
2
userData.setAccount(accountService.getAccountById(accountId, ticketId));



In the WebConfig, the SecurityInterceptor was setup as well:

Java




xxxxxxxxxx
1


 
1
@Bean
2
public SecurityInterceptor getSecurityInterceptor() {
3
    return new SecurityInterceptor();
4
}
5
 
          
6
@Override
7
public void addInterceptors(InterceptorRegistry registry) {   registry.addInterceptor(getSecurityInterceptor()).addPathPatterns("/**");
8
}



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:

Java




x


 
1
@RequiredArgsConstructor
2
@Slf4j
3
@Tag(name = "Invoice API")
4
@CrossOrigin
5
@Controller
6
@RequestMapping(value = "/accounts", produces = MediaType.APPLICATION_JSON_VALUE)
7
public class InvoiceController {
8
    private final InvoiceService invoiceService;
9
 
          
10
  @Operation(summary = "For a given accountId, return a List of invoices.")
11
    @ApiResponses(value = { @ApiResponse(responseCode = Constants.OK, description = Constants.OK_TEXT),
12
            @ApiResponse(responseCode = Constants.UNAUTHORIZED, description = "User from ticketId does not have access or ticketId is expired", content = @Content()),
13
            @ApiResponse(responseCode = Constants.FORBIDDEN, description = "User does not have access to underlying Account", content = @Content()) })
14
    @ResponseBody
15
    @GetMapping(value = "{accountId}/invoices")
16
    @ResponseStatus(HttpStatus.OK)
17
    public ResponseEntity<List<InvoiceDto>> getInvoices(@PathVariable Long accountId, @RequestParam String ticketId) {
18
        try {
19
            return new ResponseEntity<>(invoiceService.getInvoicesByAccountId(accountId, ticketId), HttpStatus.OK);
20
        } catch (SomeException e) {
21
            return new ResponseEntity<>(ExceptionUtils.handleUserExceptions(e));
22
        }
23
    }
24
}



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:

Java




x


 
1
@Slf4j
2
@Service
3
@RequiredArgsConstructor
4
public class InvoiceServiceImpl implements InvoiceService {
5
  @Resource(name = "requestScopeUserData")
6
  private UserData userData;
7
  
8
  private final Converter converter;
9
  private final InvoiceRepository invoiceRepository;
10
  
11
  /**
12
   * {@inheritDoc}
13
   */
14
  @Override
15
  public List<InvoiceDto> getInvoicesByAccountId(Long accountId, String ticketId) throws SomeException {
16
    SecurityUtils.hasEditorOrAboveAccess(userData.getUser());
17
    return converter.convert(invoiceRepository.getInvoicesByAccountId(accountId));
18
  }
19
}



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:

https://gitlab.com/johnjvester/request-scope

Have a really great day!


Further Reading

API Requests Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • How To Build Web Service Using Spring Boot 2.x
  • How to Introduce a New API Quickly Using Micronaut
  • Efficient API Communication With Spring WebClient
  • How to Merge HTML Documents in Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!