I have a Spring MVC application which presents a view which shows all of the fields from a Customer
entity such as name, address, phone number etc. The application has various roles such as ROLE_USER
and ROLE_ADMIN
. Users with ROLE_USER
are only able to see the customer name where as users with ROLE_ADMIN
can see all of the customer fields.
At the moment the way I have implemented this is with a Thymeleaf view which makes use of the SpringSecurityDialect to restrict access to certain fields based on the user's role:
<th:block sec:authorize="hasRole('ROLE_ADMIN')">
<div th:text="${customer.phoneNumber}" />
</th:block>
Whilst this works absolutely fine it doesn't feel right and it's difficult to test. I would like to write tests against the controller which call the controller method with a principal that has different roles such as testViewCustomerAsRoleAdmin
and testViewCustomerAsRoleUser
. This isn't possible to verify as the controller returns a Customer
to the view which is fully populated and has every field accessible via the getters.
What I'm after is some sort of field level security at the entity level where I can make use of the Spring Security annotations:
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String getPhoneNumber()
{
return phoneNumber;
}
It would be ideal if this could then raise an AccessDeniedException
when trying to access the field if the principal is not authorized.
The Jersey project seems to have this sort of concept which is mentioned here, here and an example here. It does however seem to be limited to role based security rather than the full SpEL support offered with @PreAuthorize
Is there any way or implementing this sort of thing with Spring Security, I know the security context or SpEL evaluation context is not available in entities as they are not Spring managed. If not, is there any other approach which might allow me to achieve the same thing?
EDIT:
I don't know the Spring Security framework inside out but it seems like something that might be able to be done using Spring AOP in ApectJ mode to instrument methods of domain (entity) classes. It would then be a case of getting hold of the AccessDecisionManager
and passing along the authentication (presumably obtained from the SecurityContextHolder
) along with the contents of the annotation on the domain class methods (if present). Does anyone have experience with doing this sort of thing?
Simply put, Spring Security supports authorization semantics at the method level. Typically, we could secure our service layer by, for example, restricting which roles are able to execute a particular method — and test it using dedicated method-level security test support.
Description. hasRole([role]) Returns true if the current principal has the specified role. hasAnyRole([role1,role2]) Returns true if the current principal has any of the supplied roles (given as a comma-separated list of strings)
Method-level security is implemented by placing the @PreAuthorize annotation on controller methods (actually one of a set of annotations available, but the most commonly used). This annotation contains a Spring Expression Language (SpEL) snippet that is assessed to determine if the request should be authenticated.
I've managed to solve the problem using Spring AOP in AspectJ mode. The various Spring annotations such as @Transactional
and @PreAuthorize
will work on any non-Spring managed classes if you enable AspectJ mode and perform either compile time or load time weaving, there is a very good example here: https://github.com/spring-projects/spring-security/tree/4.0.1.RELEASE/samples/aspectj-jc
You need to make sure you have the spring-security-aspects
dependency in your project (or plugin if you're using compile-time weaving) to enable weaving of @Secured
annotations. Despite the comment in AnnotationSecurityAspect
which describes the class as:
Concrete AspectJ aspect using Spring Security @Secured annotation for JDK 1.5+.
The class does actually weave the other Spring annotations including @PreAuthorize
, @PreFilter
, @PostAuthorize
and @PostFilter
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With