Skip to content

3. Security

Sille Kamoen edited this page May 11, 2017 · 1 revision

As our application is hooked up to the internet, the CH network and because we're dealing with money, security is a very important aspect! This page covers the basic stuff you need to know about the api security. For nearly all of it, we use the Spring Security project, with some customized features you'll learn about here. The page is split up in 2 sections. The first is on the practical application, meaning the security aspects you'll find in a lot of classes like Services and Controllers. The second part discusses the configuration and deeper classes you probably won't see unless we decide to overhaul the bunch again.

The stuff you'll use

The aspect you'll be dealing with revolves around the currently logged on User, embedded in the Authorization object. That's a Spring class containing details about the authorization of the current User, as well as the user itself, only then it's called the Principal. The principal can be cased to a User due to the way we implemented things. So whenever you see an Authentication object, you'll find details on the authentication, like the currently logged on User, called the Principal, which can be casted to a User.

PreAuthorize

In our application, the most you'll see of Spring Security are the @PreAuthorize annotations. A method annotated with @PreAuthorize checks if the current user is allowed to execute the method. If not, a 403 FORBIDDEN Response is sent immediately without executing the method.

There are two ways of checking if a User is allowed to do things with @PreAuthorize. For example @PreAuthorize("isAuthenticated()") uses the Spring Expression Language, or SpEL. In this example it only requires a logged on User. Another common example is @PreAuthorize("hasRole('ADMIN')"), which requires the User to have the ADMIN role assigned to it. Every User has a List of Roles, which always contains the USER role. If the ADMIN role is not part of the List, the method is not executed.

The second method is used when you need a bit more logic to determine if the User is allowed to use the method. These methods live in the CurrentUserService (to be found here). An easy example where this is used is the getUserById() method:

    @PreAuthorize("@currentUserServiceImpl.canAccessUser(principal, #userId)")
    @RequestMapping(value = "/{userId}", method = RequestMethod.GET)
    public User getUserById(@PathVariable Long userId) {
        return this.userService.getUserById(userId);
    }

As you can see, it's annotated with @PreAuthorize("@currentUserServiceImpl.canAccessUser(principal, #userId)"). This calls the canAccessUser method with the principal object (remember?) and the userId which was a @PathVariable.

Let's take a look at the method itself.

    @Override
    public boolean canAccessUser(Object principal, Long userId) {
        if (principal instanceof UserDetails) {
            User user = (User) principal;
            return user.getId().equals(userId) || user.getAuthorities().contains(Role.ROLE_ADMIN);
        } else {
            return false;
        }
    }

The method checks if the currently logged on User has the same ID as the User it's requesting, or if the currently logged on User has the ADMIN role. If one of those is true, the method returns true, indicating the User is allowed to execute the method. If not, false is returned.

Clone this wiki locally