Best practices for entitlement using spring security and/or shiro
I'm interested in opinions on the best way to handle the concept of "entitlement" using either Spring Security or Shiro.
For example, imagine, say, a JAX-RS endpoint that has a signature like this:
AccountDetails getAccountDetails(String accountId);
Using Spring Security, I might annotate an implementation like:
@Secured(AUTHORIZED_USER)
pu开发者_如何学Goblic AccountDetails getAccountDetails(String accountId) { ... }
or using Shiro,
@RequiresAuthentication
public AccountDetails getAccountDetails(String accountId) { ... }
What I am looking for, however, is some recommendations on "best practices" for how to ensure that the user has permission to access the particular account id (which I think is called "entitlement management").
I could imagine a couple of different approaches:
@Secured(AUTHORIZED_USER)
@AccountEntitled
public AccountDetails getAccountDetails(@Account String accountId) { ... }
(which strikes me as not completely straightforward using Spring Security, but I'd love to be wrong).
Or, I could imagine introducing an AccountId
domain object, and a factory which will only succeed in turning a String
into an AccountId
if the principle held by the current security context allows that users to see that account. But that starts to get a bit messy.
On the whole, I don't want to invent new concepts here; this seems like bread & butter stuff, but I've not had much luck finding credible recommendations around best practices here.
Thanks for any suggestions.
It sounds like what you are trying to do is implement row-level security for specific accounts. There are other Stackoverflow questions (How to implement row-level security in Java? and Database independent row level security solution) that discuss potential solutions to this very problem. Additionally, the link provided in the first answer discusses implementing Row Level Security with Spring and Hibernate. However, the higher ranked answer recommends implementing row-level security directly at the database level.
Having worked with Shiro I can say that it can be done. However you must implement your own security structures (Realms, Permissions, Annotations) to accommodate the type of functionality you describe. One approach would be to add an annotation similar to what you have in your last example that indicates the method requires a permission check. This annotation would be tied to an Interceptor which would in turn generate the appropriate permission and then call to the security framework to verify the permission.
It would look something like this.
Method:
@RequiresAuthorization
@Entitled
public AccountDetails getAccountDetails(@Account String accountId) {...}
Interceptor:
@Interceptor
@Entitled
public class EntitledInterceptor {
@AroundInvoke
public void interceptOrder(InvocationContext ctx) {
// return type is AccountDetails
// parameter[0] is acccoundId
Permission p = new CustomPermission(context.getMethod().getReturnType(),
ctx.getParameters()[0]);
if(SecurityUtils.getSubject().isPermitted(p)){
return ctx.proceed();
} else {
throw new RowLevelSecurityException("No access!");
}
}
Realm:
public boolean isPermitted(SubjectPrincipals principal, Permission p){
if( p instanceof CustomPermission){
CustomPermission cp = (CustomPermission) p;
Class<?> type = cp.getType(); //AccountDetails
Integer id = cp.getId(); //accountId
Integer userId = principal.getPrimaryPrincipal(); //or username
customPermissionCheckingLogic(userId, type, id);
}
}
Obviously this implementation relies on CDI and you having a way to determine what table(s) to check based on the object type provided (JPA annotations work in this regard). Additionally there may be ways to hook into Shiro's annotation scanning to provide more direct/native permission functionality than what I've done here.
Documentation on CDI interceptors.
精彩评论