1. Overview

In this article, we'll secure an existing service using the proxy pattern. We'll perform the authorization checks according to the current user's roles. Also, unauthorized calls will be discarded.

2. Sample Application

Let's start with our sample application.

public interface DataService {

    void read();

    void update();

    void delete();
}

We have the DataService interface which performs read, update and delete operations. For our purposes, the actual resource/entity isn't important.

public class DataServiceImpl implements DataService {

    @Override
    public void read() {
        System.out.println("Read the value...");
    }

    @Override
    public void update() {
        System.out.println("Edited the value...");
    }

    @Override
    public void delete() {
        System.out.println("Deleted the value...");
    }
}

DateServiceImpl is the default implementation.

3. Securing the Service

Now, we'll apply the proxy pattern to secure the service.

The resulting class will be a securing proxy which allows/disallows method calls to the backing service according to user roles:

public class User {

    private boolean canRead;
    private boolean canUpdate;
    private boolean canDelete;

    public boolean isCanRead() {
        return canRead;
    }

    public void setCanRead(boolean canRead) {
        this.canRead = canRead;
    }

    public boolean isCanUpdate() {
        return canUpdate;
    }

    public void setCanUpdate(boolean canUpdate) {
        this.canUpdate = canUpdate;
    }

    public boolean isCanDelete() {
        return canDelete;
    }

    public void setCanDelete(boolean canDelete) {
        this.canDelete = canDelete;
    }
}

The User class holds basic authority related fields. Note that we created this class for demonstration purposes and it is obviously not a real-world implementation.

The proxy class must intercept the method calls and use the User class to apply security checks:

public class SecuringDataServiceProxy implements DataService {

    private final DataService dataService;
    private final User user;

    public SecuringDataServiceProxy(DataService dataService, User user) {
        this.dataService = dataService;
        this.user = user;
    }

    @Override
    public void read() {
        if (!user.isCanRead()) {
            return;
        }

        dataService.read();
    }

    @Override
    public void update() {
        if (!user.isCanUpdate()) {
            return;
        }

        dataService.update();
    }

    @Override
    public void delete() {
        if (!user.isCanDelete()) {
            return;
        }

        dataService.delete();
    }
}

Here, we have the SecuringDataServiceProxy class. We're passing the actual DataService and the User to the proxy class. Then SecuringDataServiceProxy either allows or disallows the access after the authorization checks.

4. Summary

In this article, we've investigated how we can secure an existing service using the proxy pattern.

Finally, check out the source code for all examples in this article over on Github.