Archive

Archive for the ‘Spring Security’ Category

Open Lane – Sunday Housekeeping

April 16, 2012 Leave a comment

Up to now I’ve been focused on get Open Lane up and running. Today I went back and made it more manageable. I don’t want to get too far down the road without being able to build it from a shell using Maven. Also I don’t want to keep committing Eclipse specific files to my version code system Git. I want to be able to:

  • Download the latest version from github.
  • Build a war package using Maven.
  • Deploy that package to a Tomcat server and bring up Open Lane in a browser.
  • Generate an Eclipse project using Maven.

There’s plenty of  good Maven and Git documentation on the Internet. So I won’t get into the details on how to much to technologies work. With that in mind, I will highlight a few things.

The Maven pom file that comes with Spring Web Flow’s booking-faces sample was a good place to start. It very vanilla and doesn’t invoke exotic or highly customized commands or dependenicies. It expects the source tree to follow Maven defaults such as src/main/java. The only significant change was to add my application id’s and fix some the version in some Spring dependencies that were sharing the version.

	<groupId>org.bwgz.swim.openlane</groupId>
	<artifactId>open-lane</artifactId>
	<name>Open Lane</name>
	<version>0.0.1.RELEASE</version>

This results in a pom file that can be found here. After modifying my source tree the new pom could generate a war file and Eclipse project.

Command Description
mvn package Creates a war file in the target directory.
mvn eclipse:eclipse Creates an Eclipse project.

When creating the Eclipse project I found that I needed to ensure I didn’t have any old Eclipse directories hanging around. If I did then things didn’t go well when running the project in Eclipse.

The generated Eclipse project also defaults to Java 1.5 and Web 2.5. I’d prefer it to Java 1.6 or event 1.7 and Web 3.0. Those changes will come later.

During all of this I found a bug in home-flow.xml. The problem was with the evaluation expression …

expression="userService.findUser(currentUser.name)" result="viewScope.user"

Spring Web Flow (SWF) sets currentUser only if a user has been authenticated. Starting up the application on a clean install meant that no one was authenticated and in turn currentUser was not initialized (null). That caused my home flow to blow up because I was attempting to access a member of a null object. I fixed this by changing the expression to …

expression="currentUser != null ? userService.findUser(currentUser.name) : null"

This bug still perplexes me a bit because with the old code, if I logged out the user, there wasn’t a problem. I was as if currentUser was an empty (vs. null) object.

Advertisements

Open Lane – Supporting a User Part 2

April 14, 2012 Leave a comment

Having added some user authentication to my open lane application the next step involved associating the user with a profile. Things can start to get complex quickly when you need more than simple authentication and authorization. At lot of applications do not need their own form of user management because they’re part of a larger solution that already has it. Those application tap into the existing service for authentication and authorization. In our case we don’t have that. So there are a couple of options. I could add a separate user management service from another provider into the solution or I could write my own user management service.

At this point I don’t want to get into integrating with another solution. I’m sure that day will come but not today. For now I’m going to write some code that supports Spring Security and gives me enough of what I need to continue building out the application.

So what do I really need:

  1. My core user is a swimmer. Someone who will apply for an open lane swim. I’ll also need operational users such as an administrator but that can wait. I need to gather and store enough information about a swimmer to process the application. I’ll call this the user’s profile.
  2. A means to authentication a user via a login. I’ll be working with Spring Security to implement this.
  3. A means to secure pages and actions to authorized users. Again I’ll use Spring Security to implement this.
  4. A registration process to add new user.

In the previous post I used Spring’s in-memory UserDetailsService to handle authentication.

<security:authentication-manager>
	<security:authentication-provider>
		<security:password-encoder hash="md5" />
		<security:user-service>
			<security:user name="keith" password="417c7382b16c395bc25b5da1398cf076" authorities="ROLE_USER, ROLE_SUPERVISOR" />
			<security:user name="erwin" password="12430911a8af075c6f41c6976af22b09" authorities="ROLE_USER, ROLE_SUPERVISOR" />
			<security:user name="jeremy" password="57c6cbff0d421449be820763f03139eb" authorities="ROLE_USER" />
			<security:user name="scott" password="942f2339bf50796de535a384f0d1af3e" authorities="ROLE_USER" />
		</security:user-service>
	</security:authentication-provider>
</security:authentication-manager>

Given the Spring centricity of this application I’ll stick with Spring Security. The question is how to get authentication/authorization with customized user management. I’ve got specific profile information that I need to capture and store.

I could continue to use a Spring Security implementation and create a look aside table but this mean creating/updating two distinct elements when a change occurs. Or, I could subclass UserDetailsService but I’m concerned that this could be a rabbit hole that I don’t want to go down right now. Instead I’ll take a look at Spring Security’s JdbcDaoImpl. JdbcDaoImpl is an implementation of UserDetailsService which uses a database to fetch the authentication and authorization data.

<security:authentication-manager>
	<security:authentication-provider>
		<security:jdbc-user-service data-source-ref="dataSource"/>
		<security:password-encoder hash="md5" />
	</security:authentication-provider>
</security:authentication-manager>

When using JdbcDaoImpl you must ensure that you’ve correctly configured the database tables. You can find details on this here. I created two JPA entities – User,  and Authority. I’m using Hibernate on the backside.

User combines the fields that JdbcDaoImpl requires with user profile fields that the application needs.

User.java

package org.bwgz.swim.openlane.model;

import java.io.Serializable;
import java.util.Collection;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "Users")
public class User implements Serializable {
	private static final long serialVersionUID = -3475658623185783516L;

	private String username;
	private String password;
	private Boolean enabled;
	private String name;
	private String email;
	private String usasId;

	private Collection<Authority> authorities;

	public User() {
	}

	public User(String username, String name) {
		this.username = username;
		this.name = name;
	}

	@Id
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getUsasId() {
		return usasId;
	}

	public void setUsasId(String usasId) {
		this.usasId = usasId;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Boolean getEnabled() {
		return enabled;
	}

	public void setEnabled(Boolean enabled) {
		this.enabled = enabled;
	}

    @OneToMany(mappedBy = "username", fetch=FetchType.EAGER)
    public Collection<Authority> getAuthorities() {
        return authorities;
    }

	public void setAuthorities(Collection<Authority> authorities) {
		this.authorities = authorities;
	}

    @Override
    public String toString() {
    	return String.format("%s@%x; Username: %s; Password: %s; Enabled: %s; Authorities: %s; Name: %s; Email: %s; UsasId: %s;",
    			this.getClass().getName(), this.hashCode(),
    			getUsername(), getPassword(), getEnabled(), getAuthorities(),
    			getName(), getEmail(), getUsasId());
    }

}

Authority.java

package org.bwgz.swim.openlane.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "Authorities")
public class Authority implements Serializable {
	private static final long serialVersionUID = -3475658623185783516L;

	private String username;
	private String authority;

	public Authority() {
	}

	public Authority(String username, String authority) {
		this.username = username;
		this.setAuthority(authority);
	}

	@Id
        public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getAuthority() {
		return authority;
	}

	public void setAuthority(String authority) {
		this.authority = authority;
	}

    @Override
    public String toString() {
    	return String.format("%s@%x; Username: %s; Authority: %s;", this.getClass().getName(), this.hashCode(), getUsername(), getAuthority());
    }

}

For now I’ll use an in-memory instance of HSQLDB to store my data. I initialize the tables with a SQL file that Hibernate loads when the application starts up.

import.sql

insert into Users (username, password, enabled, name, email, usasId) values ('keith', '417c7382b16c395bc25b5da1398cf076', TRUE, 'Keith Lee', 'keith@email.com', 'leemkei0891' )

insert into Authorities (username, authority) values ('keith', 'ROLE_USER, ROLE_SUPERVISOR, ROLE_SWIMMER' )

Now when I go to the profile page I can see that SWF’s currentUser and the user’s profile are set.

Current User: Name: keith
Credentials: [ROLE_USER, ROLE_SUPERVISOR, ROLE_SWIMMER]
Principal: org.springframework.security.core.userdetails.User@0: Username: keith; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER, ROLE_SUPERVISOR, ROLE_SWIMMER
Autorities: [ROLE_USER, ROLE_SUPERVISOR, ROLE_SWIMMER]
Details: org.springframework.security.web.authentication.WebAuthenticationDetails@255f8: RemoteIpAddress: 127.0.0.1; SessionId: 543BB337D17562B62F8CFFC8428272FB
User Profile: Object: org.bwgz.swim.openlane.model.User@3c8c7; Username: keith; Password: 417c7382b16c395bc25b5da1398cf076; Enabled: true; Authorities: [org.bwgz.swim.openlane.model.Authority@14bda9d; Username: keith; Authority: ROLE_USER, ROLE_SUPERVISOR, ROLE_SWIMMER;]; Name: Keith Lee; Email: keith@email.com; UsasId: leemkei0891;
Username: keith
Name: Keith Lee
Email: keith@email.com
UsasId: leemkei0891

Source code is available at github.