Wednesday, March 2, 2011

Spring Security

Hey...
Most of projects need security to check logged in user and its roles to access to different parts of an application. In Spring MVC(Simple CRUD) and Spring MVC-View  post we could create a simple CRUD usecase. Here I am going to make a simple security with customary Roles.
Spring has a powerfull security framework that can commiunicate usual security systems like CAS, LDAP and so on. Here I will create a simple security system including authentication and authorization.
the usecase senario is:
  1. An anonymous user first visit the home page. it can log in to the system
  2. In log in page user should enter its username and password.
  3. After login user can see a list of system user and it can change the list depending on its roles:
    1. administrator: it can add, edit, delete the records. and also make a user expired or disabled and so on.
    2. poweruser: it can add and view users. but it can not make user expired or disabled and so on.
    3. user: it can just view users.
  4. It can log out.
  5. The log out page will be displayed.
  6. It can go back to home page(refer 1).
The first step of spring security is to make an spring application context and make a listener to load a ContextLoaderListener. So in web.xml:
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
then we should create a security filter to pass any request from this filter:
    <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>

Now everything is ready for spring security configuration. in applicationContext.xml I import spring-servlet.xml. it is the main spring configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="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.xsd">

    <import resource="classpath:spring-servlet.xml"/>

</beans>
in spring-servlet.xml file first I import security-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--security config-->
    <import resource="classpath:security-config.xml"/>

    ...
</beans>
other configuration description has been taken in Spring MVC(Simple CRUD). this is the spring security configuration file:
<?xml version="1.0" encoding="UTF-8"?>

<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-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <global-method-security pre-post-annotations="enabled">
    </global-method-security>

    <http use-expressions="true" auto-config="true">
        <intercept-url pattern="/user/**" access="isAuthenticated()"/>
        <intercept-url pattern="/**" access="permitAll"/>
        <form-login authentication-success-handler-ref="loginSuccess"/>
        <logout logout-success-url="/home/logout.html"/>
        <remember-me/>
        <session-management>
            <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
        </session-management>
    </http>
    <beans:bean id="loginSuccess" class="com.security.LoginSuccessHandler"/>
    <beans:bean id="userDetailsService" class="com.security.UserSecurityService"/>
    <authentication-manager>
        <authentication-provider user-service-ref="userDetailsService">
            <password-encoder hash="md5"/>
        </authentication-provider>
    </authentication-manager>
</beans:beans>
  1. global-method-security: we can use spring security annotation(@PreAuthorize and @PostAuthorize) by this tag.
  2. intercept-url: we can check security in any url patterns by this tag.
  3. form-login: we can make a custom login form or use default spring security login form. in this sample we use the default spring security form. But you can get authentication success handler class to set general attribute when a user successfully logs in. For example setting current user theme. Here we create "com.security.LoginSuccessHandler" class to get the loggin success handler.
  4. logout: you can set logout url and logout from via this tag.
  5. authentication-provider: we can determine which authentication-providers should authenticate the user. we can use different providers like: CAS, LDAP and so on. Here we use implicit provider: "com.security.UserSecurityService". This class should implements "UserDetailsService".
  6. password-encoder: we can determine password encoder policy via this tag. we can use hash encoder like: MD4, MD5, SHA, ... and also Base64.
In this sample we have 2 major entity beans:
  • Role: implements GrantedAuthority (needed for spring security)
  • User: implements UserDetails (needed for spring security)
the class diagram is available here:
UserSecurityService class should implements loadUserByUsername method. so in this method i use userService calls to fetch a user from database by its user name:
public class UserSecurityService implements UserDetailsService {

    @Autowired
    UserService userService;

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
        return userService.findByUsername(username);
    }
}
Scince the returned user should be an standard user(implements "org.springframework.security.core.userdetails.UserDetails"), so when I return a user via this method, spring security will compare the input user and password and this returned user's username and password and also its lock and other states.
this was the authentication step.
the authorization step will be check in different levels: 
1. action url
in security-config.xml file we can use "intercept-url" to make and access for each url. for example:
<intercept-url pattern="/user/**" access="hasRole('administrator')"/>

2. code
we can use @PreAuthorize or @PostAuthorize annotation on top of a method or we can check a role manually by use of SecurityContextHolder class.
here is annotation sample:
    @PreAuthorize("hasRole('administrator') or hasRole('power_user')")
    public void saveOrUpdate(User user, List<Long> roleIds) {

    ...
    }
and here is manual sample:
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
for(Role role : user.getRoleList()){
if(role.getAuthority().equals("administrator") || role.getAuthority().equals("power_user"))
return true;
}
return false;
3. jsp
there are some tag libraries to in "http://www.springframework.org/security/tags". we can use them to check authentication and authorization and also display current user and current user access list:
  • security:authorize: A tag which outputs the body of the tag if the configured access expression
                evaluates to true for the currently authenticated principal.
  • authentication: Allows access to the current Authentication object.
  • accesscontrollist: Allows inclusion of a tag body if the current Authentication
                has one of the specified permissions to the presented
                domain object instance.
for example:
<security:authorize access="hasRole('administrator')">
                        <input type="submit" value="edit"
                               onclick="setUserId(${user.id}); setFormAction('<spring:url value="/user/edit.html"/>');"/>

</security:authorize>
and:
    <security:authorize access="isAuthenticated()">
        <a href="/j_spring_security_logout">logout</a> welcome <security:authentication property="name"/>
    </security:authorize>
well...
now its the time to download the sample source code and take a tour in it yourself. in this sample I imported spring security jar files. But other needed Spring jar files are available in sample of  Spring MVC(Simple CRUD) post. after running the sample, the first page you can see is: http://localhost:8080/home/view.html
To create the needed databse schema, there is a mysql script in [sample root]/db/security.sql file. you can restore it in you mysql server. The datasource properties are available in [sample root]/src/database.properties file. after you restore the script, a single user with role of administrator will be created. its specification is:
username:administrator
password:123456
you can run the sample and log in by this user then create more users with different roles.
Enjoy yourselves.


all rights reserved by Mostafa Rastgar and Programmer Assistant weblog

1 comment:

Unknown said...

thanks for giving the nice example, but i have facing the configuration problem spring-servlet is not configured please help me how to solve the problem