Skip to content

Spring Security – The big stuff

February 10, 2013

Security… It is a very modern and à la mode theme. I tried to implement the Spring Security (also known as AEGI in the old times). This is an awesome framework for security. You can implement your own security. I worked hours to implement what I wanted based on information pieces found on the web. Here my implementation.

NOTE: this article is for programmers who knows Spring Core and Spring MVC. Questions are welcome if you need clarifications.

What I want to implement (not a big stuff):

  • A form-based login form (with a username and a password).
  • Having a automatic login for testing my application quickly (based on a System property variable to ensure we will not have this behaviour in production).
  • Having hashed passwords in the database.
  • Using a basic table in the database.

The libraries

Of course, you have to add the good libraries. I use Maven for this. Then you have to add these dependencies in the POM.XML file. The current version (as I write these lines is the version 3.1.3) is available in the Maven2 repository.

Here are the libraries:

  • spring-security-web: is the Spring Security framework in its last version.
  • spring-security-config: is for the configuration (I suppose…)
  • spring-context-support: don’t know really…
  • jsr250-api: is to be compliant with standard JEE notations for the roles. Spring offers a more complex way to do if you want but for basic roles, this JSR is good enough.
  • spring-security-taglibs: is used to include some security stuff directly in the viewer part (can be JSP or Velocity or FreeMarker).
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>3.1.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>3.1.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>3.2.0.RELEASE</version>
</dependency>
<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>jsr250-api</artifactId>
  <version>1.0</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-taglibs</artifactId>
  <version>3.1.3.RELEASE</version>
</dependency>

The JSR-250 which permits to add the @RolesAllowed annotation. Of course, Spring Security provides their own annotations but I prefer the use of @RolesAllowed because it is a quite simple one.

Configuring

To configure, you have to create a XML file for the security or add the security configuration in an independent file. The last option is the best. I have already split my Spring configuration in several files (one for DAOs, one for database configuration, one for beans including for services, see this post for details). Then I have added a applicationContext-security.xml file in my configuration.

<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.1.xsd">

  <http pattern="/static/**" security="none"/>
  <http auto-config='true'>
     <intercept-url pattern="/public/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
     <intercept-url pattern="/pages/**" access="ROLE_USER" />
     <form-login login-page="/public/login" />
  </http>

  <beans:bean
      id="passwordEncoder"
      class="org.springframework.security.crypto.password.StandardPasswordEncoder">
  </beans:bean>

  <beans:bean
      id="userAuthenticator"
      class="com.oxande.office240.service.UserAuthenticator">
   <beans:property name="userDao" ref="userDao" />
  </beans:bean>

  <authentication-manager alias="authenticationManager">
    <authentication-provider user-service-ref="userAuthenticator">
      <password-encoder ref="passwordEncoder" />
    </authentication-provider>
  </authentication-manager>

  <global-method-security jsr250-annotations="enabled" />

</beans:beans>

Explanation


<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.1.xsd">

First of all, the <beans:beans> root element includes xmlns="http://www.springframework.org/schema/security" for setting the default naming space to the security one (usually, it is defaulted to "http://www.springframework.org/schema/beans"). This is just to help the write of the XML file. Other part is to include the needed schemas used by Spring. Don't bother a lot: just cut and paste. For the schemaLocations, check it is consistent with the library you installed.

The <http> tag

Those blocks are to define the security rules based on URL regular expressions (or something similar). Here, I defined a first rule for static pages (images, CSS and scripts). For these pages, no security is applied.

To do this, simply add one line with <http> and no security.

<http pattern="/static/**" security="none"/>

First of all, I declared this line to remove the security on my static files. All the "/static" directory contains Javascript libraries, CSS, images and some other stuff. I don't want a test on these files. This declaration is the counterpart of the following Spring MVC (in your spring-servlet.xml file not described here).

<mvc:resources mapping="/static/**" location="/static/" />

It is a Spring 3.0 feature (see this article). As I declared these pages as accessible directly through the MVC, it is quite normal to also have them ignored by the security.

Then, you have to give the security part, it is done like this:

<http auto-config='true'>
 <intercept-url pattern="/public/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
 <intercept-url pattern="/pages/**" access="ROLE_USER" />
 <form-login login-page="/public/login" />
 </http>

I declared the pages in "/public" as anonymous ones. Then the authentification is done on these pages but any user can access as you just need to be anonymous.

For /pages, you need to be authenticated (then you have the ROLE_USER). After this, I declared /public/login the page to log in. If a user not having sufficient privileges (not yet logged) tries to connect a protected page, he will be sent to this page.

Spring Security provides its own login page but you have to create a serious one instead of the provided. You have to create a Spring Controller and its view if you use Spring MVC. Note the variables j_username and j_password are provided to your page if you want to use them. You also receive the redirect variable to redirect to the original requested page when logged.

In this page, I have directly added the "autologin" stuff (see at the end of the article).

Password encoder

Spring Security is in charge of everything including to check you user in the database and also to verify the password. To achieve this, you just have to provide a password encoder. My first choice was to use a MD5 very basic encoding. Then, reading some documentation, I decided tu use something more robust and using a salt to improve the security. Basically, I use what Spring provides:

<beans:bean id="passwordEncoder"
            class="org.springframework.security.crypto.password.StandardPasswordEncoder">
</beans:bean>

<authentication-manager alias="authenticationManager">
   <authentication-provider user-service-ref="userAuthenticator">
      <password-encoder ref="passwordEncoder" />
   </authentication-provider>
</authentication-manager>

You say to Spring Security to use the passwordEncoder I provide as a bean. As you can see the bean is a class provided by Spring directly (a very standard but not so trivial password encoder). But to authenticate the user, I use my own bean: userAuthenticator.

Why? Because I created first the application and the database then I have some issues to use standard authentication mecanism from Spring. But the authentication is quite simple. Just declare you bean and provide its implementation. Let's see in details.

<beans:bean id="userAuthenticator" class="com.oxande.office240.service.UserAuthenticator">
   <beans:property name="userDao" ref="userDao" />
</beans:bean>

Of course, I provide my User DAO to access the database where my users are. At the end, the bean is the following on


package com.oxande.office240.service;
import org.springframework.beans.factory.annotation.Autowired;</span>
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.oxande.office240.dao.UserDao;
import com.oxande.office240.entities.User;

@Service
public class UserAuthenticator implements UserDetailsService {

   @Autowired
   protected UserDao userDao;

   /**
    * HERE THE INTERESTING PART!
    */
   public UserDetails loadUserByUsername(String login)
throws UsernameNotFoundException {
      User user = userDao.getByLogin(login);
      return user;
   }

   public UserDao getUserDao() {
      return userDao;
   }

   public void setUserDao(UserDao userDao) {
      this.userDao = userDao;
   }
}

The interesting part is the following: UserDetailsService is a basic implementation provided by Spring. You just have to provide the loadUserByUserName method (no need to worry about the password, the password encoder does the job later).

UserDetails is an interface which provides the information about the user. I just adapted my User (loaded from the database) to be compliant with the interface. Not so difficult: getPassword() and getUsername() are trivial implementation.

For boolean methods, my proposal is to return true in all the cases: this is basically what is expected. The methods are: isAccountNonExpired(), isAccountNonLocked(), isCredentialsNonExpired() and isEnabled().

The last one is more tricky: getAuthorities() must return a collection of GrantedAuthority objects which are basically an interface with only one method: getAuthority() which returns a role. Fortunately, Spring provides an implementation: the SimpleGrantedAuthority class.

The roles in the application

Rather than creating a table to store the roles, I create a enum class. The main reason is the security is always stored in the application itself. The database only store the roles for a user. For this I use a USER_ROLES table. The main trick is the declaration of the user's roles with the Hibernate annotations (in fact, there are JPA 2.0 annotations).

Spring Security expects roles as simple strings but it is not an issue because the Enum classes have always their string representation (through their name() method).

Basically, I have made the following implementation in my User implementation:


   @ElementCollection(targetClass=Role.<b>class</b>)
   @Enumerated(EnumType.<i>STRING</i>)
   @CollectionTable(name="USER_ROLES",
                    joinColumns = @JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID"))
   @Column(name="ROLE_ID")

   private Set<Role> roles = new HashSet<Role>();

   public Set<Role> getRoles() {
      return roles;
   }

   public void setRoles(Set<Role> roles) {
      this.roles = roles;
   }

   public Collection<? extends GrantedAuthority> getAuthorities() {
      Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
      for( Role role : this.getRoles() ){
         GrantedAuthority authority = new SimpleGrantedAuthority(role.name());
         authorities.add(authority);
      }
      authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
      return authorities;
   }

Well, Role is a JAVA enumeration to enumerate the roles, the implementation is trivial. I decided to use a enum to be able to access directly to a role through a constant rather than a string, but it depends how do you manage the roles in your application:

package com.oxande.office240.entities;

import java.io.Serializable;

public enum Role implements Serializable {
   ROLE_ADMIN("Administrator"),
   ROLE_HR("Human Resources"),
   ROLE_FINANCIAL("Financial supervisors"),
   ROLE_IT("IT Staff"),
   ROLE_ACCOUNTANT("Accountant"),
   ROLE_ANONYMOUS("Anonymous"),

   private String label;

   Role( String label ) {
      this.label = label;
   }

   public String getLabel() {
      return label;
   }
}

Last little things

We have finished to manage the security. You just have to add the following lines.

<global-method-security jsr250-annotations="enabled" />

to enable the JSR-250 and the JAVA annotations to your request controllers (if you use Spring MVC).

<beans:bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>

if you want to use the taglib of Spring Security in your views (JSP, Velocity or Freemarker).

Changes in web.xml

Don't forget to add the security filter. Simply add the following code. There is no configuration to do for the filter, everything is done elsewhere.

<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>

Log the user automatically

I want to log myself automatically (without having to type my login and password). This feature is for development only. In production, it is a very bad idea.

To be sure that security teams will not kill me and to be sure nobody will use this functionality in production, I choose to use the system variable available in the System properties. In fact, I need to pass the information directly in the JVM. Then no configuration file for this and discretion is absolute.

If, as I do, you use the jetty:run maven command to debug your application in Eclipse, you must add -Dautologin=wrey to the command line ("wrey" is the username of the user to log automatically).

You could use several mechanism to achieve this. The perfect one should be to create your own authentication filter but I choose a simpler one but discrete and not so tricky: I just intercept the username in the login form and I authenticate myself.

Then I just need to create the following request mapping and le tour est joué.


   @RequestMapping(value="/public/login")
   public String login(Model model, HttpServletRequest request, HttpServletResponse response) {
      String autologin = System.<i>getProperty</i>("autologin");
      if( autologin != null ){
         LOG.warn("Trying to log *automatically* " + autologin);
         UserDetails u = userAuthenticator.loadUserByUsername(autologin);
         if( u == null ){
            LOG.warn("User " + autologin + " not found.");
         }
         else {
            LOG.warn("User " + autologin + " logged automatically.");
            Authentication auth = new PreAuthenticatedAuthenticationToken(u.getUsername(), u.getPassword(), u.getAuthorities());
            SecurityContextHolder.<i>getContext</i>().setAuthentication(auth);

            // http://stackoverflow.com/questions/5389274/spring-security-3-get-initially-requested-url/5389357
            SavedRequest savedRequest = <b>new</b> HttpSessionRequestCache().getRequest(request, response);
            String redirectUrl = savedRequest.getRedirectUrl();
            return "redirect:" + redirectUrl;
         }
      }
      return</b> "/public/login";
   }

Using taglib in the view part

You should be able to find documentation over the net about the use of the taglib available in Spring Security.

For Freemaker, just add:

<assign security=JspTaglibs["http://www.springframework.org/security/tags"] >

Then you can add some stuff. The user name of the connected user can be retrieved through the following:

<@security.authentication property="principal.username" />

Conclusion

So many help I found on the web, but I can't cite all of them. I spend many hours to find what I was looking for because many articles were related to Spring Security 2 then this is a definitive guide if you want to implement Spring Security on an existent website. If you have questions, do not hesitate to post them as comments.

I also have found (just now, at the end of this writing) an interesting page on the subject: http://www.jtips.info/index.php?title=Spring_Security (but careful: it seems related to Spring Security 2)

About these ads

From → Computers

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: