GumGum Tech Blog
Published in

GumGum Tech Blog

Let The Right One In…With Spring Security Annotations

Photo by Clément Falize on Unsplash

On the Advertising Applications team here at GumGum, the bulk of the job consists of fleshing out features for a number of web-applications designed to help users with efficient advertising campaign setup, publisher on-boarding/setup, or provide easy-to-digest metrics to measure various KPIs. In the background, a SpringBoot REST API aids in storing, updating, and accessing MySQL and Snowflake databases that hold relative data for the many different companies that partner with GumGum. Ensuring the security of all that company data by securing the API is the main focus of this article, but since it’s close to Halloween, and bad analogies are fun, let’s talk about vampires for a minute.

Remember, with vampires, one is generally safe inside one’s home. Vampires can try the front door, a window, or maybe a side door from the garage, but as long as they aren’t invited inside, they’re just somebody else’s problem. More often than not, software engineers inevitably end up fighting their own “vampires”, people trying to gain access to parts of an application they have no business accessing. Hopefully the tricks that follow will help keep the vampires out on the porch where they belong.

The Setup

Straight out of the box, SpringBoot has a lot of built-in security features that users can utilize to secure their applications. From authentication to role-based authorization, there’s a good base to secure any application. A lot of these features can be further extended to satisfy myriad use-cases, but this article will primarily focus on endpoint authorization, with an emphasis on keeping a user’s eyes on their own data. This will be done by constructing custom Spring Expression Language (SpEL) expressions for use with Spring’s method security annotations, so click on the preceding links if a refresher is needed.

For the examples, the following schema will be assumed, where a user can belong to many companies (can’t forget the contractors), and a company can have many users:

Schema for our Example

Note that for this Spring project, the following Java and Kotlin classes will help with the example alongside CrudRepository interfaces for all three entities (repository classes omitted for brevity):

Users.kt
Company.kt
CompanyData.kt, assume uniqueness for (companyId, data) pair
CompanyDataServiceImpl.java

Notice that in the service class there is a glaring opportunity for some vampirism. The endpoint that calls this method only passes in a company id to the service’s getCompanyData method. Assuming a user has a valid JWT and has authenticated themselves properly, nothing so far prevents them from looking up the API endpoint with their browser’s dev tools and accessing the endpoint with other company ids. For example, going to https://example-api.com/companies/{companyId}/data can return company data to them with any companyId they pass in. Currently, the API is saying “Yes, Dracula, right this way. After you.”

Photo by Dima Pechurin on Unsplash

Shutting The Door

So, as it stands, the service layer is wide open. To close it, validation must occur that the current user has access to the company. Assuming that the email can be obtained from retrieving the current user’s details, Spring can retrieve their respective User object and ensure that their list of companies includes the company being queried. In this case, it is assumed there is a SecurityUtils class that allows retrieval of the current user from the SecurityContext, casts to a concrete UserDetails class, and retrieves the user’s email. So, knowing that access to the current user’s email is available, something like the following can be done:

CompanyUserAuthorization.java

The byCompanyId method will return true if any of the User’s companies’ ids match the companyId parameter. Otherwise, it will return false. Note that the @Component annotation contains a specified name for this class. This will be referenced in the next step where SpEL is used in conjunction with the @PreAuthorize annotation to add the authorization in the service layer, which looks like this:

CompanyDataServiceImpl.java with authorization

Recall, SpEL allows us to access a class method with the @ operator and parameters via the #operator, so what this is doing is telling the @PreAuthorize annotation to call the method created in CompanyUserAuthorization, using the companyId from CompanyDataServiceImpl.getCompanyData(Integer companyId) method. Since this is a pre-authorization, this is actually performed prior to the annotated method being called. If the pre-authorization returns true, getCompanyData will be called and company data will be returned to the user. If it returns false, the user will get a 403 and the method will not be called at all. In other words, there is no longer need to worry about the vampires.

Photo by Sheldon Kennedy on Unsplash

Expanding on this

The vampires are outside the door now, but what happens when they try the window? The best part of how this @PreAuthorize annotation was constructed is that it is extremely reusable. Any method in the service layers that take in a companyId parameter can have this annotation applied! The CompanyUserAuthorization class could also be expanded to include methods that handle request objects, lists of company ids, etc. All that would need to be done is to construct their respective SpEL strings.

The main advantage to these annotations resides in their flexibility, as they can be as granular as needed. In this case, the authorizations were only checking against the User object, but it’s possible that the authorization could be much more complex. For instance, if only some internal users can access all company data, then one might need to also do role-based authorization in addition to the companyId check for external users.

Photo by David Menidrey on Unsplash

Wrapping Up

Securing an application’s endpoints can be accomplished in a number of ways, but as shown in this article, @PreAuthorize can be used in conjunction with re-usable custom methods and SpEL expressions to secure method calls at the service level. Similar methods can be constructed using a variety of parameter types or even alongside @PostAuthorize if the object being compared for authorization resides in the return object.

At GumGum, the Advertising Applications’ API is currently closed to our internal users only, but recent use-cases have arisen that will require us to be more and more external-user facing in the future. The team is planning on using Spring functionality like that in the preceding example (albeit a bit more complicated) to help ensure the vampires aren’t invited inside. When the applications are only letting the right ones in, software engineers can have a happy Halloween, assured that their API is a little less spooky.

We’re always looking for new talent! View jobs.

Follow us: Facebook | Twitter | LinkedIn | Instagram

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store