Spring Security (former Acegi) is a Java library that handles authorization and authentication in web applications. Documentation on the project web site is, as expected from Spring Source, easy to read and use. I have a feeling though, that most of us first search Google for a fast, technology tutorial, before reading the docs, so in this little article I'm going to show you a few things Spring Security can do, give you a few hints and code snippets I have, after using it a little bit here and there. I'm not going to explain everything throughly, that's what docs are for, but what is here should help to get you started (or decide whether you want to).
Since this is quite a lot of text for a blog post.
Here is the plan:
1. Set up and form authentication
2. User in the backend (getting logged user, authentication, testing)
3. Securing web resources
4. Securing methods
5. OpenID (login via gmail)
6. OAuth2 (login via Facebook)
7. Writing on Facebook wall with Spring Social
I'm not a Spring Security Master-Blaster, mind you. Hell, I didn't even had a chance to read the book. I'm just a guy, who had to implement all this stuff withing a few days, and would like to make it easier on you (no to mention keeping a record for later use), so there may be bugs & mistakes, but... well, so far it works and you are welcome to diss me in comments :]
First, let's add maven dependencies. At the time of writing, last stable version was 3.0.5. The exclusions are here because spring-security 3.0.5 uses spring framework (aop and so on) in version 3.0.3.RELEASE, and I'd rather have the latest. You may also want to try 3.1.0.RC1, which is available. Spring social is unfortunately in it's first milestone, but it works, so what the hell...
Now, add a security filter to your web.xml. This is our entry point for authentication and web resource based authorization. This filter fires up a bunch of other Spring Security filters, which you can find here
How you want your applicationContext.xml (or any other IoC configuration) is up to you, in my example I have a security.xml file with all the spring-security configuration inside.
Not much, right? Let's do something useful.
2. Form authentication
For your login/password authentication (called form-based authentication), Spring Security will listen and respond on two url's, one for login (default: j_spring_security_check), one for logout (default: /j_spring_security_logout). All we have to do is either POST to one or GET to the other. Let's start with an example, by modyfing security.xml file:
Session-fixation-protection allows us to “migrate”: create new session and rewrite all the data from the old one, after user logs in succesfully. In form-login tag, we can change defaults and configure what will happen after login (whether to use default target or redirect back). Same with logout. Remember-me allows us to leave a cookie in the browser, so that the user doesn't have to login every time.
Our login-page can be in any technology we want, the only three important things are:
Intercept-url may be used for authorization, but here we just require the browser to use https, so that the password is not traveling as plain text over the wire. Just remember that it's not enough to secure the target URL (/j_spring_security_check), you need to make sure that the POST goes there over SSL. Securing only /j_spring_security_check will end in the browser sending one POST with login and password over http (plain text), getting redirected to https and then posting it again (using SSL). Not exactly secure, is it?
The easiest way to fix it, is to require the browser to use https on the login form page as well, but if you cannot do that (because for example you design requires a small login form on every page), and don't want to hardcode server domain in the form action (which would be inconvenient for the developer) you can change the protocol on the fly using javascript. Here's an example together with changing ports (tomcat defaults):
What we need now, is to tell Spring Security how to validate user login and password. So let's add this to security.xml:
Our authentication provider (DaoAuthenticationProvider) is an out of the box spring implementation that allows us to give it a hashing algorithm, a salt source and the most important part: userDetailsService, which is a class get you the user from a repository and hence I name it AuthenticationUserDetailsGetter.
The interface is that simple:
Spring framework will use this class to get the user by login (called username, may as well be an email – you decide how to find the user in this class), and then hash the password using passwordEncoder and check whether it's the same as what UserDetails object has. UserDetails is an interface representing our user from the security perspective.
Next: User in the backend (getting logged user, authentication, testing)
Since this is quite a lot of text for a blog post.
Here is the plan:
1. Set up and form authentication
2. User in the backend (getting logged user, authentication, testing)
3. Securing web resources
4. Securing methods
5. OpenID (login via gmail)
6. OAuth2 (login via Facebook)
7. Writing on Facebook wall with Spring Social
I'm not a Spring Security Master-Blaster, mind you. Hell, I didn't even had a chance to read the book. I'm just a guy, who had to implement all this stuff withing a few days, and would like to make it easier on you (no to mention keeping a record for later use), so there may be bugs & mistakes, but... well, so far it works and you are welcome to diss me in comments :]
1. How to set up Spring Security
First, let's add maven dependencies. At the time of writing, last stable version was 3.0.5. The exclusions are here because spring-security 3.0.5 uses spring framework (aop and so on) in version 3.0.3.RELEASE, and I'd rather have the latest. You may also want to try 3.1.0.RC1, which is available. Spring social is unfortunately in it's first milestone, but it works, so what the hell...
<spring.security.version>3.0.5.RELEASE</spring.security.version>
<spring.social.version>1.0.0.M1</spring.social.version>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.security.version}</version>
<exclusions>
<exclusion>
<artifactId>spring-core</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-expression</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-context</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-tx</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-aop</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.openid4java</groupId>
<artifactId>openid4java</artifactId>
<version>0.9.5</version>
</dependency>
Now, add a security filter to your web.xml. This is our entry point for authentication and web resource based authorization. This filter fires up a bunch of other Spring Security filters, which you can find here
<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>
And I you aren't using Spring IoC container already, don't forget to also add Spring IoC listener:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:ioc/applicationContext.xml</param-value>
</context-param>
<listener>
How you want your applicationContext.xml (or any other IoC configuration) is up to you, in my example I have a security.xml file with all the spring-security configuration inside.
<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">
<http>
</http>
</beans:beans>
Not much, right? Let's do something useful.
2. Form authentication
For your login/password authentication (called form-based authentication), Spring Security will listen and respond on two url's, one for login (default: j_spring_security_check), one for logout (default: /j_spring_security_logout). All we have to do is either POST to one or GET to the other. Let's start with an example, by modyfing security.xml file:
<http>
<session-management session-fixation-protection="migrateSession"/>
<!-- Make sure you are not sending login/password in plain text on open channel. Use SSL (https) instead -->
<intercept-url pattern="/login" requires-channel="https"/>
<intercept-url pattern="/j_spring_security_check" requires-channel="https"/>
<intercept-url pattern="/**" requires-channel="http"/>
<!-- form login -->
<form-login login-page="/login" login-processing-url="/j_spring_security_check"
always-use-default-target="true" default-target-url="/"/>
<!-- logout -->
<logout logout-url="/j_spring_security_logout"/>
<!-- remember me -->
<remember-me key="rememberMeKey" user-service-ref="userDetailsService"/>
</http>
Session-fixation-protection allows us to “migrate”: create new session and rewrite all the data from the old one, after user logs in succesfully. In form-login tag, we can change defaults and configure what will happen after login (whether to use default target or redirect back). Same with logout. Remember-me allows us to leave a cookie in the browser, so that the user doesn't have to login every time.
Our login-page can be in any technology we want, the only three important things are:
- it should point to /j_spring_security_check
- it should POST j_username, j_password and _spring_security_remember_me (checkbox)
- it should POST over https
Intercept-url may be used for authorization, but here we just require the browser to use https, so that the password is not traveling as plain text over the wire. Just remember that it's not enough to secure the target URL (/j_spring_security_check), you need to make sure that the POST goes there over SSL. Securing only /j_spring_security_check will end in the browser sending one POST with login and password over http (plain text), getting redirected to https and then posting it again (using SSL). Not exactly secure, is it?
The easiest way to fix it, is to require the browser to use https on the login form page as well, but if you cannot do that (because for example you design requires a small login form on every page), and don't want to hardcode server domain in the form action (which would be inconvenient for the developer) you can change the protocol on the fly using javascript. Here's an example together with changing ports (tomcat defaults):
<head>
<script language="javascript">
function forceHttpsOnSubmit(objForm) {
objForm.action = objForm.action.replace("http:", "https:").replace("localhost:8080","localhost:8443");
}
</script>
</head>
<body>
<form method="post" action="/j_spring_security_check" onsubmit="forceHttpsOnSubmit(this)">
<div id="passwordLoginOption" class="form">
<div class="row">
<div class="label left">
<label for="j_username">login:</label>
</div>
<div class="right">
<div class="textWrapper">
<input type="text" name="j_username"/>
</div>
</div>
<div class="cl"></div>
</div>
<div class="row">
<div class="label left">
<label for="j_password">password:</label>
</div>
<div class="right">
<div class="textWrapper">
<input type="password" name="j_password"/>
</div>
</div>
<div class="cl"></div>
</div>
<div class="row">
<div class="right">
<label class="forCheckbox" for='_spring_security_remember_me'>
Remember me:
<input type='checkbox' name='_spring_security_remember_me'/>
</label>
</div>
<div class="cl"></div>
</div>
<div class="buttons">
<input type="submit" value="Login"/>
</div>
</div>
</form>
</body>
What we need now, is to tell Spring Security how to validate user login and password. So let's add this to security.xml:
<!-- authentication manager and password hashing -->
<authentication-manager alias="authenticationManager">
<authentication-provider ref="daoAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
<beans:property name="saltSource">
<beans:bean class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<beans:property name="userPropertyToUse" value="username"/>
</beans:bean>
</beans:property>
<beans:property name="passwordEncoder" ref="passwordEncoder"/>
</beans:bean>
<beans:bean id="userDetailsService" name="userAuthenticationProvider" class="eu.solidcraft.backend.infrastructure.services.authentication.form.AuthenticationUserDetailsGetter">
<beans:constructor-arg index="0" ref="userRepository"/>
</beans:bean>
<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<beans:constructor-arg index="0" value="256"/>
</beans:bean>
Authentication Manager fires up all authentication providers against given login and password. Why? Because at some point you may want to change your providers or (more probable) hashing algorithm from, let's say SHA256 to SHA512, and since you cannot recalculate old passwords (you should keep the hash only), and you may not be able to tell which user uses which encoding, you will have to validate the password two times.Our authentication provider (DaoAuthenticationProvider) is an out of the box spring implementation that allows us to give it a hashing algorithm, a salt source and the most important part: userDetailsService, which is a class get you the user from a repository and hence I name it AuthenticationUserDetailsGetter.
The interface is that simple:
public interface UserDetailsService {
UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException;
}
And the implementation no more complicatedpublic class AuthenticationUserDetailsGetter implements UserDetailsService {
private UserRepository userRepository;
//required by cglib because I use class based aspect weaving
protected AuthenticationUserDetailsGetter() {
}
public AuthenticationUserDetailsGetter(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
User user = userRepository.findByLogin(username);
throwExceptionIfNotFound(user, username);
return new AuthenticationUserDetails(user);
}
private void throwExceptionIfNotFound(User user, String login) {
if (user == null) {
throw new UsernameNotFoundException("User with login " + login + " has not been found.");
}
}
}
Spring framework will use this class to get the user by login (called username, may as well be an email – you decide how to find the user in this class), and then hash the password using passwordEncoder and check whether it's the same as what UserDetails object has. UserDetails is an interface representing our user from the security perspective.
public interface UserDetails extends Serializable {
Collection<GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
The interface is self explanatory. “Authorities” will be useful later for authorization. For now let's implement this in a very simple way. Note, that though it may be tempting to just implement this interface in you User class, and pass it on, do not do it, as your User entity may be quite heavy and not serializable (hibernate proxy, lot of properties, some left for lazy loading, etc.) and UserDetails is supposed to be light.public class AuthenticationUserDetails implements UserDetails {
private Long id;
private final String login;
private final String passwordHash;
private final boolean enabled;
private HashSet<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
public AuthenticationUserDetails(User user) {
this.login = user.getUsername();
this.passwordHash = user.getPassword();
this.enabled = user.isEnabled();
this.grantedAuthorities.addAll(user.getAuthorities());
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}
@Override
public String getPassword() {
return passwordHash;
}
@Override
public String getUsername() {
return login;
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
public String getLogin() {
return login;
}
}
Next: User in the backend (getting logged user, authentication, testing)


Can you please add imports to your classes and some details on creating your own User and Role entities. Without role the reading is confusing. For example, on AuthenticatedUserDetails you call getId().... there is no getId()?
ReplyDelete@Ali Muzaffar: there's no getId call in this post, so I assume you are refering to the whole set of my posts about Spring Security. You are correct, AuthenticationUserDetails in my project does have a private Long id, that I've not shown in this post, because I wanted to keep it as simple as possible. I've just fixed that.
ReplyDeleteAs for the User entity: the point in Spring Security is that it can be you own class (no interface to implement), and you only need to have a lightweight (think: Data Transfer Object) UserDetails implementation, which I've shown here.
As for the Roles, there is an interface GrantedAuthority, and an implementation (GrantedAuthorityImpl) out of the box, working on simple strings, as I've described here.
To keep it simple: when talking about Roles you can think about a list of strings, and have no other implementation whatsoever.
For more complex situations (roles that have priviladges, users having many roles), there is an open spource GWT implementation of those entities, with a GUI panel to manage users, roles and privilages here: https://github.com/TouK/gxt-tools/tree/master/gxt-tools-lib/src/main/java/pl/touk/wonderfulsecurity
A user implementation is here: https://github.com/TouK/gxt-tools/tree/master/gxt-tools-lib/src/main/java/pl/touk/wonderfulsecurity/beans
A role implementation is here: https://github.com/TouK/gxt-tools/blob/master/gxt-tools-lib/src/main/java/pl/touk/wonderfulsecurity/beans/WsecRole.java
I wouldn't recommend this project as a reference (it's ugly, not TDD) but it works, so it may be fine for learning.
Quickfix: user implementation is in WsecUser, here
ReplyDeletePlease can you check is remember-me service really works? I have look like configuration of spring security 3.0.5 with spring mvc and I spend 1 day trying to enable it( I try to user own entry point, own remember me service) I don't won't to make some filter that check if user is authorize and create some cookie than. Any ideas?
ReplyDeletemy bad ... or not my ...
ReplyDeleteme or my friend make method getUsername in User class(User implements UserDetails) and write into method only 1 line: return null;
... oh my brain is boiling :) but remember me finally works.