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?
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-jcYou 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 inAnnotationSecurityAspect
which describes the class as:The class does actually weave the other Spring annotations including
@PreAuthorize
,@PreFilter
,@PostAuthorize
and@PostFilter
.