Spring login form example
At first you should define this file WEB-INF/spring/serurity-context.xml
:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
<http auto-config="true" />
<beans:bean id="myUserService" class="org.my.UserService" />
<authentication-provider user-service-ref="myUserService" />
</beans:beans>
Now you should create org.my.UserService
class and implement interface org.springframework.security.core.userdetails.UserDetailsService
. This interface has one method:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, org.springframework.dao.DataAccessException
And in this method you can use Hibernate in order to load user by userName. If user does not exists - just throw UsernameNotFoundException, otherwise return new intialized UserDetails instance (there you can provide a lot of stuff like user roles, account expiration date, etc...).
Now comes web.xml
:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>My Webapp</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/*-context.xml
</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
If you have any questions or something goes wrong, feel free to ask :)
PS: So with UserDetailsService you don't have to check password of whether user account is active, etc. You just provide spring-security information about user with provided userName
and framework validates user itself. If you encode your passwords with MD5 for example, than you can use password-encoder
like this:
<beans:bean id="myUserService" class="org.my.UserService" />
<authentication-provider user-service-ref="myUserService">
<password-encoder hash="md5"/>
</authentication-provider>
Update
Now we will dive more deeper in UserService
- my (simplified) real world example.
UserService
class:
import org.my_company.my_app.domain.User
public class UserService implements UserDetailsService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
// load user
User user = userDao.getUser(username);
if (user != null) {
// convert roles
List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
for (Privilege p : user.getPrivileges()) {
roles.add(new GrantedAuthorityImpl(p.getName()));
}
// initialize user
SecurityUser securityUser = new SecurityUser(
user.getUsername(),
user.getLdapAuth() ? getLdapPassword(user.getUsername()) : user.getPassword(),
user.getStatus() != User.Status.NOT_COMMITED, user.getStatus() != User.Status.BLOCKED, true, true,
roles.toArray(new GrantedAuthority[0])
);
securityUser.setUser(user);
return securityUser;
} else {
throw new UsernameNotFoundException("No user with username '" + username + "' found!");
}
}
}
Now SecurityUser
:
import org.my_company.my_app.domain.User
public class SecurityUser extends org.springframework.security.core.userdetails.User {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public SecurityUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, GrantedAuthority[] authorities) throws IllegalArgumentException {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
}
And finally UserDao
:
import org.my_company.my_app.domain.User
public class UserDao extends HibernateDaoSupport {
public User getUser(String username) {
List users = getHibernateTemplate().find("from User where username = ?", username);
return users == null || users.size() <= 0 ? null : (User) users.get(0);
}
}
As you can see I used HibernateTemplate
here.
App-fuse will give you a full working example: http://appfuse.org/display/APF/AppFuse+QuickStart
Or if you have maven installed simply run:
mvn archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes -DarchetypeArtifactId=appfuse-light-spring-security-archetype -DarchetypeVersion=2.1.0-M2 -DgroupId=com.mycompany -DartifactId=myproject
This will generate an appfuse light project with spring mvc, spring security and hibernate.
The basic xml-configuration you can see in the post of "Easy Angle". The part he mentioned as "myUserService" is a bean that implements "UserDetailService" That one has basically just one method to implement which is the following
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException, DataAccessException
If you use Spring, then you'll probably have a Bean, that handles the access to your User-Table. That one you can just inject into that class to retrieve User details, like:
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException, DataAccessException {
UserTable user = userbean.getUserbyName(name);
if (user == null) {
throw new UsernameNotFoundException("User " + name + " not found!");
}
Collection<GrantedAuthority> auth = getAuthorities(user.getAuthorities());
return new User(user.getName(), user.getPassword(), true, true, true, true, auth);
}
Now in an authentication bean all you need is to inject this bean and ask it for the UserDetails. There you can use it to check if the credentials are correct and if so fill the SecurityContext with the needed information in order to be logged in.
@Override
public Boolean authenticate(String username, String password) {
UserDetails userdetail = null;
try {
userdetail = myUserService.loadUserByUsername(username);
} catch (UsernameNotFoundException e) {
return false;
} catch (DataAccessException e) {
return false;
}
if (!myUserService.encodePassword(password).equals(userdetail.getPassword())) {
return false;
}
Authentication auth = new UsernamePasswordAuthenticationToken(userdetail.getUsername(), userdetail.getPassword(),
userdetail.getAuthorities());
SecurityContext sc = new SecurityContextImpl();
ServletRequestAttributes attr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
attr.getRequest().getSession().setAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY, userdetail.getUsername());
sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);
return true;
}
Of course thats a simplified version of the real one. There are way more checks that you have to perform prior to say that the user is authenticated (SQLInjection for instance)