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)