In a common MVC designed applicaton, is it a bad idea to make the service layer dependant on a user session? Let's say there is a service method that fetches a few objects from a database, and you wish to return different results depending on who initializes the call - for example an administrator might get 10 rows of objects, while a normal user might only get 7 rows because the last 3 were "administrator-only" objects. A few ways to solve this would be:
Lately, i've started using the last method more and more since it provides a clean interface and feels very practical to work with. A Filter makes sure that the current thread always has a user set. Is this bad design? I believe that some could see this as a dependancy from the service layer to the web layer, though I personally think that they are pretty uncoupled. The biggest consequence is that a methods behaviour will be different depending on the state of another class, which could be both a bad and a good thing.
What are your thoughts on this? If it's a bad solution, what would be a stronger one?
I strongly advise aginst ThreadLocal style approaches - it seems like too much of a "global variable" design smell. This has many issues, most notably:
Between the other two approaches, I think "it depends":
An alternative option is to pass a "context" object through to the service layer that contains a set of relevant session data (i.e. more than just the user name). This could make sense if you want to minimise parameter bloat. Just beware of it turning into a way to "get around" good layering principles. If it's just "data" then it's probably fine, but as soon as people start passing callback objects / UI components in the context then you are probably heading towards a bit of a mess....
Just use Java EE's role mechanism, it's already there.
As an example:
@Stateless
public class AService {
@Resource
private SessionContext sessionContext;
@PersistenceContext
private EntityManager entityManager;
public List<AObject> fetchAFewObjects() {
String query = "aUserQuery";
if (sessionContext.isCallerInRole("ADMIN")
query = "aAdminQuery";
return entityManager.createNamedQuery(query, AObject.class)
.getResultList();
}
}
On the web side, make sure you're doing a container login, so the server is aware of the logged-in user.
You don't have to implement either solution for your usecase.
In EJB, what you want is already supported by the framework. It's called the security context and it automatically propagates into every method you call (behind the scenes it's indeed often implemented by TLS, but that's an implementation detail).
This security context will give you access to the username and his/her roles. In your case all you seem to need is to check for the roles.
You can do this declaratively by using annotations on your methods (@RolesAllowed), or by injecting the EJB session context and asking it for the roles of the current user.
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