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.