views:

590

answers:

3

I'm building a web application using Stripes and Spring. It needs to have a login/authentication feature. Right now I store user information separate from user credentials in the database. My User model does not contain the credentials as I don't want to be passing around valuable passwords.

Spring manages all of my DAO's.

Now, I am implementing a non-container based security system. I store the sha-2 hash of the password and perform a comparison on the password that was submitted in the form against what is stored in the database. This comparison has been tested and works. I'm trying to figure out how to wire this thing together. Right now I have a LoginActionBean that catches the login requests and uses a "PasswordService" singleton that uses a UserDAO internally to retrieve credentials and perform comparison against the parameters submitted. My spring bean is:

<bean id="passwordSerivce" class="com.example.store.authentication.PasswordService" factory-method="getInstance">
    <property name="userDAO" ref="userDAO"/>
</bean>

But it then the PasswordService singleton needs a:

public void setUserDAO(UserDAO userDAO) { ...}

method which doesn't really make sense in a singleton (UserDAO is an interface).

I'm looking for a proper design. I've read that ServiceLocators are the very reason Spring was invented. Any thoughts?

I'd also like to know how else I can design this. I have an ActionBean that is called when the user clicks "Login", but how do I actually authenticate. Do I inject the authentication service into the bean? Do i create a singleton that anyone can call? Do I have a generic interface that the LoginAcionBean uses which Spring injects? If I wasn't using Spring, how would it be done?

+1  A: 
  1. I don't understand why it doesn't make any sense to inject the userDao.

  2. I don't understand why your xml is using factory-method="getInstance"; you can throw away all the instance stuff and singleton stuff; spring will instantiate a single instance of the password service class and inject it into however many classes need it, and they'll all get the same instance. So spring is creating a singleton for you. The password service class can be a simple pojo. Similarly for the userDao implementation.

I'd also advise looking at, understanding, and using the spring annotations. Basically you use @Service on the class that will be injected, then use @Autowired on the setter in the class where it's injected. You also need to add something to your xml configuration file to turn on the annotation stuff.

lumpynose
I'm not sure if I was clear about this, but you don't need to do any singleton stuff; spring does all that for you. You just set up your spring beans and they'll all effectively be singletons. You have to do extra gyrations to get spring to create non-singleton beans.
lumpynose
The reason I don't think it's right is because the singleton class has a "setUserDAO" method. This can be set by anyone that uses the service, meaning someone can technically null it.I'm wondering what the proper way to "authenticate" someone is from an OOD point of view.
djunforgetable
I agree about the problem where anyone can call the setter; Gandalf's constructor answer is what I use when I'm worried about that. You may also be able to make the setter private and then if you use annotations (@Autowired) spring can still use the setter. Not positive about that; and it looks really sketchy in my opinion.
lumpynose
+1  A: 

Adding to the above answer, just use constructor injection if you don't want a setter for the DAO:

<bean id="passwordSerivce" class="...PasswordService">
   <constructor-arg ref="userDAO"/>
</bean>

Unless the bean is not a class you control, you don't need to make it a Singleton, Spring will do that by default.

I personally am not a fan of annotations.

Gandalf
A: 

My solution was to have an interface with an "authenticate" method. Then create a service class that implements the interface with a constructor that takes in a UserDAO object. This way, the object that requires the service doesn't perform instantiation (it's done by spring) and is unaware of what underlying implementation is performed (ldap, SSO, simple password comparison, etc). Seems to get the job done :P

djunforgetable