I would tend to agree with vmarcinko.
It seems like if you want to expose your business service easily through some kind of RMI (Webservices, Hessian, Burlap) then you would want to put credentials into each of the service methods that needed authentication/priviledges.
For example, take the PetClinic sample. In the PetClinicManager (or PetClinicService, whatever) we would probably have something like the following:
Pros:Code:interface PetClinicManager { // Everybody has access to see all pets. public List getPets(); // Only a given owner should be able to save their own pet's data. public Pet saveOrUpdate( String username, String password, Pet pet ); }
1) No assumption is made about the user already being authenticated; The don't trust anyone principal.
2) Very easy to just slap any RMI on top of it and not worry about where authentication takes place (e.g. is it happening in WS-Security?). The authentication will happen in the BL.
Cons:
1) You need to have credentials passed around all the time and you keep needing to revalidate whoever the user is (in the case of most web applications you would have already done this so you are essentially doing it again! YUCK! :evil: ).
2) Potentially a large amount of code duplication (the number one code smell... phew!).
Using ThreadLocal
If we use ThreadLocal then maybe we can eliminate the username and password from being passed to the method. In fact, if we have the ability to set ThreadLocal then we could do the authentication of the user there and only store the username/User object in ThreadLocal as an authenticated user. We would still, however, need to verify that the user who was trying to update a pet in a call to saveOrUpdate( Pet pet ) was the owner of that pet.
Also, if we rely on ThreadLocal then we need to at least modify our RMI interfaces to have a way to set ThreadLocal (maybe using WS-Security in webservices?).
Using AOP
Now, if we use AOP and we stick with the convention that the username and password will be the first two method parameters then we will save ourselves some code duplication in doing authentication of the user and then in our saveOrUpdate() body we would assume that the username that was passed in was authenticated for us. However, we still need to verify that the user is actually the pet's owner.
As was mentioned by vmarcinko:
In the example I posed the "saveOrUpdate" method requires a generic step to authenticate the passed in username and password but it requires a specific business logic to verify that the user is the pet's owner.Using AOP seems as great thing for generic transparent stuff (such as transactioning), but not specific one. And what we have here is clearly very method-specific stuff.
This is not to say that AOP is all bad. Certainly, if I have business methods that require a user to have a certain priviledge and the context of the method (e.g. what data I am performing logic on) doesn't matter then I believe AOP is a GREAT alternative. It simply breaks down a little bit when the context DOES matter.
My solution
For this example I would probably do the following:
Now again, I am not too happy about the fact that I need to pass in both the username AND the password. An alternative might be to pass in the User object and just verify that the password matched but I don't know how much I have saved here other than saving on parameters (I still can't necessarily trust that the User object is already authenticated).Code:// Please forgive the code, it's a little rought ;) class PetClinicManagerImpl implements PetClinicManager { ... omissions ... // Everybody has access to see all pets. public List getPets() { return petDAO.getAll(); } // Maybe move this to some base class. public User authenticateUser( String username, String password ) { User user = userDAO.getUser( username ); if( user.getPassword().equals( password ) ) { return user; }else { throw new UserAuthenticationFailedException( username ); } } // Only a given owner should be able to save their own pet's data. public Pet saveOrUpdate( String username, String password, Pet pet ) { Pet pet = null; User owner = authenticateUser( username, password ); if( pet.getOwner().getUserName().equals( username ) ) { pet = petDAO.saveOrUpdate( pet ); }else { throw new InvalidAccessException( username, pet ); } return pet; } }
Enough babbling... Any thoughts?
Thanks in advance. Great discussion!


) we would probably have something like the following:
Reply With Quote