Setting up Spring Security in Eclipse – III

Using Method based Security

 Scenario: Only an account holder can deposit or withdraw money. Only an employee can charge fees to the account, and only a manager can allow overdraft on an account.

 ———————————————————————

Modify WEB-INF/applicationContext-security.xml by adding the following line under the root element:

<global-method-security secured-annotations=”enabled” />

 ———————————————————————

Create a new xml called WEB-INF/applicationContext-security.xml which is mentioned in web.xml

 ———————————————————————

Use terminal to go the root of the project, and type:

ant deploy

 This will deploy the web application to the tomcat server and available at http://localhost:8080/springacldemo

The error logs will be available at $CATALINA_HOME/logs/Catalina.out and $CATALINA_HOME/logs/localhost.yyyy-mm-dd.log

 

Setting up Spring Security in Eclipse – II

Setting up Spring Security – Using URL based Security

 Scenario: There is a bank called NoFraudBank, which has two branches – HisBranch and HerBranch. Each branch has 4 employees – a Manager, a Teller Supervisor, and two Tellers. Each branch has one account, being a small privately owned bank. The bank allows the account holders to deposit or withdraw money. The bank employees can charge fees to the account and see an admin view of the account, with some comments that the bank employees have entered for each account.

 Download and unzip the latest Spring Security jar files from http://www.springsource.org/download/community?project=Spring%2520Security

Copy the following files from the above unzipped folder into the Web/WEB-INF/lib folder:

standard.jar

org.springframework.aop-3.1.1.RELEASE.jar

spring-security-acl-3.1.x.RELEASE.jar

spring-security-config-3.1.0.RELEASE.jar

spring-security-core-3.1.x.RELEASE.jar

spring-security-taglibs-3.1.x.RELEASE.jar

spring-security-web-3.1.x.RELEASE.jar

———————————————————————

Modify Web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

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>Spring ACL Demo</display-name>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

<context-param>

<param-name>webAppRootKey</param-name>

<param-value>springacldemo_root</param-value>

</context-param>

<servlet>

<servlet-name>springacldemo</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>springacldemo</servlet-name>

<url-pattern>*.htm</url-pattern>

</servlet-mapping>

<welcome-file-list>

<welcome-file>login.jsp</welcome-file>

</welcome-file-list>

<!-- - Location of the XML file that defines the root application context

- Applied by ContextLoaderListener. -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

WEB-INF\applicationContext-security.xml

</param-value>

</context-param>

<!-- - Loads the root application context of this web app at startup. -

The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<!-- Spring security uses filters to enforce security. The springSecurityFilterChain

tells the application context to load the security specific configuration

in applicationContext-security.xml. -->

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

</web-app>

———————————————————————

Create a new xml called WEB-INF/applicationContext-security.xml which is mentioned in web.xml under context-param.

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

<http pattern="/login.jsp" security="none"/>

<http auto-config="true">

<intercept-url pattern="/viewAccount.htm"

access="ROLE_ACCHOLDER,ROLE_MANAGER,ROLE_SUPERVISOR,ROLE_TELLER" />

<intercept-url pattern="/viewAccountAdmin.htm"

access="ROLE_MANAGER,ROLE_SUPERVISOR,ROLE_TELLER" />

<intercept-url pattern="/viewEmployees.htm" access="ROLE_MANAGER" />

<form-login login-page="/login.jsp" default-target-url="/viewAccount.htm"

always-use-default-target="false" authentication-failure-url="/login.htm?authfailed=true" />

<logout invalidate-session="true" logout-url="/logout.jsp"

logout-success-url="/login.htm?loggedout=true" />

</http>

<authentication-manager>

<authentication-provider>

<!-- <password-encoder hash="plaintext" /> -->

<user-service>

<user name="HeManager" password="test" authorities="ROLE_MANAGER" />

<user name="HisSupe" password="test" authorities="ROLE_SUPERVISOR" />

<user name="HisTeller1" password="test" authorities="ROLE_TELLER" />

<user name="HisTeller2" password="test" authorities="ROLE_TELLER" />

<user name="HisAccHolder" password="test" authorities="ROLE_ACCHOLDER" />

<user name="SheManager" password="test" authorities="ROLE_MANAGER" />

<user name="HerSupe" password="test" authorities="ROLE_SUPERVISOR" />

<user name="HerTeller1" password="test" authorities="ROLE_TELLER" />

<user name="HerTeller2" password="test" authorities="ROLE_TELLER" />

<user name="HerAccHolder" password="test" authorities="ROLE_ACCHOLDER" />

</user-service>

</authentication-provider>

</authentication-manager>

</beans:beans>

———————————————————————

Create a new xml called WEB-INF/springacldemo-servlet.xml which is mentioned in web.xml under <servlet-mapping><servlet-name>

<?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-2.5.xsd">

<!-- This configuration will take any url request that matches *.htm pattern

and map it to file with the same name but with a .jsp extension using the

UrlFilenameViewController and InternalResourceViewResolver provided by spring. -->

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="mappings">

<value>

/*.htm=urlController

</value>

</property>

</bean>

<bean id="urlController"

class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />

<!-- Because of the viewResolver, we do not have to specify our internal

structure. -->

<bean id="viewResolver"

class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="viewClass"

value="org.springframework.web.servlet.view.JstlView" />

<property name="prefix" value="WEB-INF/jsp/" />

<property name="suffix" value=".jsp" />

</bean>

</beans>

———————————————————————

Create the files mentioned in the above xml file. login.jsp goes under WebContent, all others go under WEB-INF, so they are not directly accessible by url.

login.jsp

<%@ page session="true"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<html>

<head>

<title>Login: Spring Security Web Application</title>

<style TYPE="text/css">

.errormessage {

color: red;

}

.successmessage {

}

</style>

"javascript">

function doSubmit(userid) {

document.getElementById('usernameField').value = userid;

document.getElementById('passwordField').value = "test";

}

</head>

<body onload='document.loginForm.j_username.focus();'>

<form id="loginForm" name="loginForm" action="j_spring_security_check"

method="post">

<c:if test="${not empty param.authfailed}">

<span id="infomessage" class="errormessage"> Login failed due

to: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />. </span>

</c:if>

<c:if test="${not empty param.authfailed}">

<span id="infomessage" class="errormessage"> Login failed due

to: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />. </span>

</c:if>

<c:if test="${not empty param.newpassword}">

<span id="infomessage" class="errormessage"> Login failed due

to: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />. </span>

</c:if>

<c:if test="${not empty param.acclocked}">

<span id="infomessage" class="errormessage"> Login failed due

to: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />. </span>

</c:if>

<c:if test="${not empty param.accdisabled}">

<span id="infomessage" class="errormessage"> Login failed due

to: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />. </span>

</c:if>

<c:if test="${not empty param.loggedout}">

<span id="infomessage" class="successmessage"> You have been

successfully logged out. </span>

</c:if>

<table>

<tr>

<td>Username</td>

<td><input id="usernameField" type="text" name="j_username"

value="<c:out value="${SPRING_SECURITY_LAST_USERNAME}"/>" />

</td>

</tr>

<tr>

<td>Password</td>

<td><input id="passwordField" type="password" name="j_password" />

</td>

</tr>

<tr>

<td colspan="2" align="right"><input type="button"

value="Login" />

</td>

</tr>

</table>

<br />

<table style="height: 28px;" border=1 bordercolor=#cccccc

cellspacing=0 cellpadding=2 width=100%>

<tr>

<td align=center colspan=2>Login as</a></td>

</tr>

<tr>

<td align=center>His Branch</a>

</td>

<td align=center>Her Branch</a>

</td>

</tr>

<tr>

<td align=center><input type="submit" value="He Manager"

onClick="doSubmit('HeManager');" /></td>

<td align=center><input type="submit" value="She Manager"

onClick="doSubmit('SheManager');" /></td>

</tr>

<tr>

<td align=center><input type="submit" value="His Supervisor"

onClick="doSubmit('HisSupe');" /></a></td>

<td align=center><input type="submit" value="Her Supervisor"

onClick="doSubmit('HerSupe');" /></a></td>

</tr>

<tr>

<td align=center><input type="submit" value="His Teller 1"

onClick="doSubmit('HisTeller1');" /></a></td>

<td align=center><input type="submit" value="Her Teller 1"

onClick="doSubmit('HerTeller1');" /></a></td>

</tr>

<tr>

<td align=center><input type="submit" value="His Teller 2"

onClick="doSubmit('HisTeller2');" /></a></td>

<td align=center><input type="submit" value="Her Teller 2"

onClick="doSubmit('HerTeller2');" /></a></td>

</tr>

<tr>

<td align=center><input type="submit"

value="His Account Holder" onClick="doSubmit('HisAccHolder');" /></a>

</td>

<td align=center><input type="submit"

value="Her Account Holder" onClick="doSubmit('HerAccHolder');" /></a>

</td>

</tr>

</table>

</form>

</body>

</html>

———————————————————————

WebContent/WEB-INF/jsp/viewAccount.jsp

<%@ page session="true"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<%@ taglib prefix='security'

uri='http://www.springframework.org/security/tags'%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>View Account</title>

</head>

<body>

<%@ include file="/WEB-INF/jsp/navigation.jsp"%>

<br />

<b>View Account Page</b>

<br />

<br /> Visible to employees and accountholder.

</body>

</html>

———————————————————————

WebContent/WEB-INF/jsp/viewAccountAdmin.jsp

<%@ page session="true"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<%@ taglib prefix='security'

uri='http://www.springframework.org/security/tags'%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>View Account Admin Page</title>

</head>

<body>

<%@ include file="/WEB-INF/jsp/navigation.jsp"%>

<br />

<b>View Account Admin Page</b>

<br />

<br /> Visible only to employees

</body>

</html>

———————————————————————

WebContent/WEB-INF/jsp/viewEmployees.jsp

<%@ page session="true"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

<%@ taglib prefix='security'

uri='http://www.springframework.org/security/tags'%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>View Employees</title>

</head>

<body>

<%@ include file="/WEB-INF/jsp/navigation.jsp"%>

<br />

<b>View Employees</b>

<br />

<br /> Visible only to Manager

</body>

</html>

———————————————————————

WebContent/WEB-INF/jsp/navigation.jsp

"navcontainer">

28px;" border=1 bordercolor=#cccccc cellspacing=0 cellpadding=2 width=100%>right colspan=3>You are logged in as:

#0000ff; font-weight:bold">"principal.username"/>

</td>

</tr>

<tr>

<td align=center><a href="viewAccount.htm">Account Info</a></td>

<td align=center><a href="viewAccountAdmin.htm">Account Admin Info</a></td>

<td align=center><a href="viewEmployees.htm">Employees</a></td>

</tr>

</table>

</div>

———————————————————————

Use terminal to go the root of the project, and type:

ant deploy

This will deploy the web application to the tomcat server and available at http://localhost:8080/springacldemo

The error logs will be available at $CATALINA_HOME/logs/Catalina.out and $CATALINA_HOME/logs/localhost.yyyy-mm-dd.log

 

Setting up Spring Security in Eclipse – I

Setting up Spring

Create a new Dynamic Web Project in Eclipse (needs Web tools download from Eclipse) with the following parameters:

Project name: <your-project-name>

Rest default settings

Download and unzip the latest Spring jar files from http://www.springsource.org/download/community?project=Spring%2520Framework

Copy the following files from the above unzipped folder into the Web/WEB-INF/lib folder:

org.springframework.asm-3.x.x.RELEASE-x.jar

org.springframework.beans-3. x.x.RELEASE-x.jar

org.springframework.context-3. x.x.RELEASE-x.jar

org.springframework.core-3. x.x.RELEASE-x.jar

org.springframework.expression-3. x.x.RELEASE-x.jar

org.springframework.web.servlet-3. x.x.RELEASE-x.jar

org.springframework.web-3. x.x.RELEASE-x.jar

Download and unzip the latest jar file from http://commons.apache.org/logging/download_logging.cgi

Copy the following files from the above unzipped folder into the Web/WEB-INF/lib/ folder:

commons-logging-1.x.x.jar

Download and unzip the latest jstl jar file from http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/

Copy the following files from the above unzipped folder into the Web/WEB-INF/lib/ folder:

jakarta-taglibs-standard-1.x.x.zip

Create the following files using the code supplied:

———————————————————————

WEB-INF/web.xml

 <?xml version="1.0" encoding="UTF-8"?>

<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>Spring ACL Demo</display-name>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

 <!-- For issues with multiple apps on tomcat -->

<context-param>

<param-name>webAppRootKey</param-name>

<param-value>springacldemo_root</param-value>

</context-param>

 <!-- MVC application controller. See springacldemo-servlet.xml. -->

<servlet>

<servlet-name>springacldemo</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

 <servlet-mapping>

<servlet-name>springacldemo</servlet-name>

<url-pattern>*.htm</url-pattern>

</servlet-mapping>

 </web-app>

 

———————————————————————

Create our welcome file – index.jsp as defined in web.xml under <welcome-file-list> tag:

 <%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>Spring ACL Demo Index page</title>

</head>

<body>

<a href="hello.htm">Say Hello</a>

</body>

</html>

———————————————————————

Create our hello file – hello.jsp linked from index.jsp as hello.htm:

 <%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>Spring ACL Demo Hello page</title>

</head>

<body>${message}

</body>

</html>

———————————————————————

Create WEB-INF/springacldemo-servlet.xml. Spring knows to look for this file because of “springacldemo” in servlet-name. The servlet class is defined in <servlet-name>-servlet.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"

xsi:schemaLocation="http://www.springframework.org/schema/beans

 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

 <!--

This configuration will take any url request that matches *.htm pattern and

map it to file with the same name but with a .jsp extension using the

UrlFilenameViewController and InternalResourceViewResolver provided by spring.      -->

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="mappings">

<value>

/*.htm=urlController

</value>

</property>

</bean>

<bean id="urlController"

class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
<!--

Because of the viewResolver, we do not have to specify our internal structure.

-->

<bean id="viewResolver"

class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="viewClass"

value="org.springframework.web.servlet.view.JstlView" />

<property name="prefix" value="WEB-INF/jsp/" />

<property name="suffix" value=".jsp" />

</bean>

</beans>

———————————————————————

Create build.xml

 <project name="Spring ACL Demo" default="war" basedir=".">

 <property name="app.name" value="springacldemo" />

<property name="app.path" value="/${app.name}" />

<property name="web.home" value="WebContent" />

<property name="webDir" value="${web.home}/WEB-INF" />

<property name="build.home" value="${basedir}/build" />

<property name="war.dir" value="${basedir}/warfile" />

<property name="catalina.home" value="/Users/amitabh/programs/apache-tomcat-6.0.29" />

<property name="manager.url" value="http://localhost:8080/manager" />

<property name="src.home" value="${basedir}/src" />

<property name="manager.username" value="admin" />

<property name="manager.password" value="admin" />

 <property name="dist.home" value="${basedir}/dist" />

 <path id="classpath">

<pathelement path="." />

<fileset dir="${webDir}/lib">

<include name="*.jar" />

</fileset>

 <pathelement location="${catalina.home}/common/classes" />

<fileset dir="${catalina.home}/common/endorsed">

<include name="*.jar" />

</fileset>

<fileset dir="${catalina.home}/common/lib">

<include name="*.jar" />

</fileset>

 <pathelement location="${catalina.home}/shared/classes" />

<fileset dir="${catalina.home}/shared/lib">

<include name="*.jar" />

</fileset>

 <fileset dir="${catalina.home}/lib">

<include name="catalina-ant.jar" />

</fileset>

</path>

 <target name="clean">

<delete dir="${build.home}" />

</target>

 <target name="init" depends="clean">

<mkdir dir="${build.home}" />

<mkdir dir="${build.home}/WEB-INF" />

<mkdir dir="${build.home}/WEB-INF/classes" />

<copy todir="${build.home}">

<fileset dir="${web.home}" />

</copy>

<mkdir dir="${build.home}/WEB-INF/lib" />

</target>

 <target name="compile" depends="init" description="Compile Java sources">

<mkdir dir="${build.home}/WEB-INF/classes" />

<javac srcdir="${src.home}" destdir="${build.home}/WEB-INF/classes" debug="${compile.debug}" deprecation="${compile.deprecation}" optimize="${compile.optimize}">

<classpath refid="compile.classpath" />

</javac>

<copy todir="${build.home}/WEB-INF/classes">

<fileset dir="${src.home}" excludes="**/*.java" />

</copy>

</target>

 <target name="all" depends="clean,compile" description="Clean build and dist directories, then compile" />

 <target name="war" depends="compile" description="Create binary distribution">

<jar jarfile="${war.dir}/${app.name}.war" basedir="${build.home}" />

</target>

 <target name="undeploy" description="Undeploy war from tomcat">

<delete file="${catalina.home}/webapps/${app.name}.war" />

</target>

 <target name="deploy" depends="war, undeploy" description="Deploy application to servlet container">

<copy file="${war.dir}/${app.name}.war" todir="${catalina.home}/webapps" />

</target>

 </project>

———————————————————————

Use terminal to go the root of the project, and type:

ant deploy

 This will deploy the web application to the tomcat server and available at http://localhost:8080/springacldemo

The error logs will be available at

$CATALINA_HOME/logs/Catalina.out and 

$CATALINA_HOME/logs/localhost.yyyy-mm-dd.log

Understanding Spring Security

Spring Security, formerly known as Acegi Security, is a framework that provides a way to secure your spring application. As you look for online resources on Spring Security, you will find several people using it with Grails as well. I am not familiar with Grails, so we will discuss only spring based applications, specifically web applications.

Spring Security is currently in version 3. Several examples online use version 2, but may not mention it. There are several differences between version 2 and 3. As an example, the <authentication-provider> element in the security.xml file is within the <beans> element in version 2, but in version 3, it is inside another element called <authentication-manager>. Also, the registerPermissionsFor method that is used when extending the BasePermission class in version 2 is deprecated in version 3. So just be aware that you may need to modify your code if you test code from version 2 but intend to use it with version 3.

To add spring security to your application, you need to download spring security jars and add them to your application. The following jars are available:

 

Jar Use
spring-security-core Must be included in
spring-security-remoting If using Servlet API
spring-security-web Use in web applications
spring-security-ldap If using LDAP
spring-security-config If using namespace configuration (web, ldap, openid, protect-pointcut)
spring-security-acl If using ACL security
spring-security-cas If integrating with JA-SIG CAS
spring-security-openid If integrating with OpenID
spring-security-taglibs For using Spring Security JSP tag implementations

 

Add the following lines to your web.xml under the <web-app> element:

 <!-- - Location of the XML file that defines the root application context - Applied by ContextLoaderListener. -->

   <context-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>WEB-INF\applicationContext-security.xml</param-value>

   </context-param>

 <!-- - Loads the root application context of this web app at startup. - The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). -->

   <listener>

      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

   </listener>

 <!-- Spring security uses filters to enforce security. The springSecurityFilterChain tells the application context to load the security specific configuration in applicationContext-security.xml. -->

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

 

The above will add spring security capabilities to your app. Our application is still not secure yet, because we haven’t decided what it is we want to secure in our application. Spring Security provides three broad categories of handling security:

  • URL-based Security
  • Method Security
  • ACL Domain Object Security

The three categories are discussed in detail below.

URL based Security

Perhaps the simplest type of security for beginners to think of is url based security. Spring Security allows the securing individual pages using role-based authentication. You can define the roles and the pages to secure, the url to go to when login is successful, or fails, or link your application to an authentication source. Spring Security provides configuration for LDAP, OpenID, CAS and JAAS based authentications.

In order to add url based security, we create a new xml called WEB-INF/applicationContext-security.xml which is mentioned in the web.xml under context-param.

This file looks like this:

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

 <http pattern="/login.jsp" security="none"/>

 <http auto-config="true">

   <intercept-url pattern="/viewAccount.htm" access="ROLE_ACCHOLDER,ROLE_MANAGER,ROLE_SUPERVISOR,ROLE_TELLER" />

   <intercept-url pattern="/viewAccountAdmin.htm" access="ROLE_MANAGER,ROLE_SUPERVISOR,ROLE_TELLER" />

   <intercept-url pattern="/viewEmployees.htm" access="ROLE_MANAGER" />

   <form-login login-page="/login.jsp" default-target-url="/viewAccount.htm" always-use-default-target="false" authentication-failure-url="/login.htm?authfailed=true" />

   <logout invalidate-session="true" logout-url="/logout.jsp" logout-success-url="/login.htm?loggedout=true" />

</http>

<authentication-manager>

   <authentication-provider>

   <!-- <password-encoder hash="plaintext" /> -->

      <user-service>

            <user name="user1" password="test" authorities="ROLE_MANAGER" />

            <user name="user2" password="test" authorities="ROLE_SUPERVISOR" />

            <user name="user3" password="test" authorities="ROLE_TELLER" />

      </user-service>

   </authentication-provider>

</authentication-manager>

</beans:beans>

In the above example, the login.jsp has no security applied to it, because we want unauthenticated users to be able to get to the login page. The http element is where you list the pages that you want to secure and which roles have access. The auto-config attribute automatically configures form login, logout and basic authentication features. The intercept-url uses the pattern property to match the pages against the url request. Since the url’s are matched in order, the more specific patterns should be listed first.

In our example above, we have listed the usernames and passwords in plain text. In a production application, the authentication provider would refer to another bean that provided the user authentication details via a datasource.

The following entries are used to configure a hierarchical based role system:

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">

<constructor-arg ref="roleHierarchy" /></class>

<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">

                  <property name="hierarchy">

                        ROLE_ADMIN > ROLE_STAFF

                        ROLE_STAFF > ROLE_USER

                        ROLE_USER > ROLE_GUEST

                  </property>

</bean>

 Method Security

In Spring Security, it is possible to secure individual methods for authentication. In order to use method security, the following lines need to be added to the files specified:

In security.xml

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

The above line activates method security using pre and post annotations throughout your application. The next step to securing your methods is to add annotations like the one below for each of your methods:

@PreAuthorize("hasRole('ROLE_USER')")

The above annotation is added before the method declaration in the interface for the method that you want to secure. The role names should start with “ROLE_” prefix in order for spring security to detect them as being roles. It is possible to configure this prefix if needed.

The other available method security annotations are:

@PreAuthorize("hasPermission(#item, 'admin')")
public void addPermission(Item item,…);

In the above example, the #item refers to the argument “item”, and evaluates whether the user has the permission ‘admin’ to this particular item.

Spring Security has the following five built in permissions:

  1. Read
  2. Write
  3. Create
  4. Delete
  5. Admin

It is possible to create custom permissions, which may be numbered up to 32.

The hasPermission evaluator can be configured to use a custom permission evaluator by adding an expression handler to the global-method-security tag. The expression handler has a property named “permissionEvaluator” which refers to our custom permission evaluator. The custom permission evaluator has access to the authentication object, through which we can get the user details of the current user, as well as the target Domain Object. The target domain object is the one for which the permission is being evaluated. This gives us a high degree of flexibility to evaluate the permission using the properties of either of those objects.

There are other annotation options as listed below:

@PreAuthorize("#item.property == authentication.property")   
public void myMethod(Item item);
//“authentication” is a built in expression for Authentication in the security context.  @PreAuthorize("hasRole('ROLE_USER')")   @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")   public List<Contact> getAll();

PostFilters filters the returned collection and filters the list by removing each ‘filterObject” to which the current user does not have ‘read’ permissions.

Some other security expressions are:

  • authentication
  • principal
  • denyAll
  • permitAll
  • hasAnyRoles(role1,role2,role3)
  • hasRole(role)
  • hasIpAddress(ip)
  • isAnonymous()
  • isAuthenticated()
  • isFullyAuthenticated()
  • isRememberMe()

Another way to protect the methods is to use protect-pointcut.

       <global-method-security>

            <protect-pointcut expression="execution(* com.mycompany.*Service.*(..))"

                  access="ROLE_USER" />

      </global-method-security>

This protects all the methods that match the expression provided and only users with the role specified in the access property are allowed access to them.

According to the reference documentation, The SecurityContextHolder is the most fundamental object in Spring Security. It holds the user details in the Authentication object. A user is considered authenticated when the SecurityContextHolder contains a fully populated Authentication object.

The following code from the reference docs shows how to query the Authentication object:

 Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (principal instanceof UserDetails) {

              String username = ((UserDetails) principal).getUsername();

       } else {

              String username = principal.toString();

       }

The principal object returned above can be cast into a UserDetails object. This UserDetails object can then be cast into the application specific user object. The following method in the UserDetailsService accepts a string based username argument and returns a UserDetails object:

 UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

JdbcDaoImpl and InMemoryDaoImpl are some of the implentations of the UserDetailsService interface provided by Spring Security. UserDetailsService is just that – it provides user details but does not authenticate a user.

The AuthenticationManager inserts GrantedAuthority objects into the Authentication object to be read by the AccessDecisionManager. The AccessDecisionManager makes the final access control decision based on the AccessDecisionVoter responses. Each AccessDecisionManager polls one or more AccessDecisionVoters specified in the security.xml config file. It passes all the arguments to all the voters. For each argument, each voter has only three options (int) to return – ACCESS_GRANTED, ACCESS_DENIED or ACCESS_ABSTAIN. A voter returns ACCESS_ABSTAIN when the config attriute it is asked to vote on does not apply. For example, if a RoleVoter is asked to vote on anything other than the role of a user, it will return ACCESS_ABSTAIN.

The following AccessDecisionManagers are available:

  • AffirmativeBased (Any yes)
  • ConsensusBased (Simple Majority yes, property to decide what to do if all decisions abstain)
  • UnanimousBased (All yes or abstain).

The following voters are provided by Spring Security:

  • RoleVoter (Votes only whether the user has a specified Role.)
  • AuthenticatedVoter (Checks whether the user is anonymous, fully-authenticated or remember-me authenticated)

It is also possible to create custom voters.

ACL Security

Spring Security also provides domain object level security. In simple terms, ACL provides a way to specify permissions based on a combination of role, business object (referred to as domain object) and permissions. For example, if you want to grant a user read permission based on their role, on their own user data, you would use ACL security.

In order to secure the domain objects, the following tables need to be created:

ACL_SID: Uniquely identifies a principal or authority (user or role or group)

ACL_ENTRY: Individual permissions assigned to each sid for each object_identity

ACL_CLASS: Uniquely identifies a domain object class. The field class contains fully qualified name of the java class.

ACL_OBJECT_IDENTITY: Stores information for each unique domain object instance.

ACL_SID
id
principal
sid

 

ACL_ENTRY
id
acl_object_identity
ace_order
sid
mask
granting
audit_success
audit_failure

 

ACL_ENTRY
id
class

 

ACL_OBJECT_IDENTITY
id
object_id_class
object_id_identity
parent_object
owner_sid
entries_inheriting

A datasource is created and injected into a JdbcMutableAclService and BasicLookupStrategy instance. BasicLookupStrategy does the lookup of the acl. The domain objects that we want to secure should have a public Serializable getId() method that returns a type long or compatible with long. Next step is to create a AccessDecisionVoter or AfterInvocationProvider that would use AclService’s isGranted() method to retrieve the ACL and check whether the permission is granted or not.

 

 

 

 Appendix A: Code to create a custom permission evaluator:

 

security.xml

 

       <security:global-method-security

              pre-post-annotations="enabled">

              <security:expression-handler ref="expressionHandler" />

       </security:global-method-security>

       <bean id="expressionHandler"

              class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">

              <property name="permissionEvaluator">

                     <bean id="permissionEvaluator"

                           class="org.krams.tutorial.infrastructure.SomePermissionsEvaluator" />

              </property>

       </bean>

 

 

Service Interface

       @PostFilter("hasPermission(filterObject, 'READ')")

       public List<Post> getAll();

 

 

Custom Permissions Evaluator

 

       @Override

       public boolean hasPermission(Authentication authorities,

                     Object targetDomainObject, Object permission) {

              boolean Decision = false;

              System.out.println("Initial Decision: " + Decision);

              Date cutoffDate = null;

              try {

                     cutoffDate = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH)

                                  .parse("January 1, 2012");

                     System.out.println("Cutoff Date: " + cutoffDate.toString());

              } catch (ParseException e) {

                     e.printStackTrace();

              }

              System.out.println("Domain Object Date: "

                           + Post.class.cast(targetDomainObject).getDate());

              if (Post.class.cast(targetDomainObject).getDate().before(cutoffDate)) {

                     Decision = false;

                     System.out.println("In before");

              } else {

                     Decision = true;

                     System.out.println("In after");

              }

              System.out.println("Final Decision: " + Decision);

              System.out.println("--------");

              return Decision;

       }

 

 

 

Appendix B: SQL For creating ACL Tables

 

create database acl;

use acl;

CREATE TABLE IF NOT EXISTS `acl_sid` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`principal` tinyint(1) NOT NULL,

`sid` varchar(100) NOT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `unique_uk_1` (`sid`,`principal`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

CREATE TABLE IF NOT EXISTS `acl_class` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`class` varchar(255) NOT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `unique_uk_2` (`class`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

CREATE TABLE IF NOT EXISTS `acl_entry` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`acl_object_identity` bigint(20) NOT NULL,

`ace_order` int(11) NOT NULL,

`sid` bigint(20) NOT NULL,

`mask` int(11) NOT NULL,

`granting` tinyint(1) NOT NULL,

`audit_success` tinyint(1) NOT NULL,

`audit_failure` tinyint(1) NOT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `unique_uk_4` (`acl_object_identity`,`ace_order`),

KEY `foreign_fk_5` (`sid`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=43 ;

CREATE TABLE IF NOT EXISTS `acl_object_identity` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`object_id_class` bigint(20) NOT NULL,

`object_id_identity` bigint(20) NOT NULL,

`parent_object` bigint(20) DEFAULT NULL,

`owner_sid` bigint(20) DEFAULT NULL,

`entries_inheriting` tinyint(1) NOT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `unique_uk_3` (`object_id_class`,`object_id_identity`),

KEY `foreign_fk_1` (`parent_object`),

KEY `foreign_fk_3` (`owner_sid`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;

ALTER TABLE `acl_entry`

ADD CONSTRAINT `foreign_fk_4` FOREIGN KEY (`acl_object_identity`) REFERENCES `acl_object_identity` (`id`),

ADD CONSTRAINT `foreign_fk_5` FOREIGN KEY (`sid`) REFERENCES `acl_sid` (`id`);

ALTER TABLE `acl_object_identity`

ADD CONSTRAINT `foreign_fk_1` FOREIGN KEY (`parent_object`) REFERENCES `acl_object_identity` (`id`),

ADD CONSTRAINT `foreign_fk_2` FOREIGN KEY (`object_id_class`) REFERENCES `acl_class` (`id`),

ADD CONSTRAINT `foreign_fk_3` FOREIGN KEY (`owner_sid`) REFERENCES `acl_sid` (`id`);

 

Appendix C: How to create custom permission

 

Security.xml:

      <bean id="permissionEvaluator"

            class="org.springframework.security.acls.AclPermissionEvaluator">

            <constructor-arg ref="aclService" />

            <property name="permissionFactory" ref="permissionFactory" />

      </bean>

      <bean id="lookupStrategy"

            class="org.springframework.security.acls.jdbc.BasicLookupStrategy">

            ...

            <property name="permissionFactory" ref="permissionFactory" />

      </bean>

      <bean id="permissionFactory" class=" com.company.security.myPermissionFactory" />

 

 

Custom Permission class:

      public class myPermission extends BasePermission {

            public static final Permission CUSTOMX = new myPermission (1 << 5,

                        'X');

            public static final Permission CUSTOMY = new myPermission (1 << 6, 'Y');

            protected myPermission (int mask) {

                  super(mask);

            }

            protected myPermission (int mask, char code) {

                  super(mask, code);

            }

      }

Custom Factory Class to register the new permissions:

      public class myPermissionFactory extends DefaultPermissionFactory {

            public myPermissionFactory() {

                  super();

                  registerPublicPermissions(myPermission.class);

            }

      }

 

The method annotation:

      @PreAuthorize("hasPermission(#user,'customx')")

      public void myMethod(User user) {

      ...

      }

 

Appendix D: Paging Implementation

 

Original interface:

public interface theOriginalServiceInterface {

      public void setDataSource(DataSource dataSource);

      /**

      * Retrieves the next two items.

      */

      @PostFilter("hasPermission(filterObject, 'READ')")

      public List<Post> getNextTwo(int startRow);

}

 

Original implementation class:

@Service("origService")

@Transactional

public class theOriginalService implements theOriginalServiceInterface {

            public List<Post> getNextTwo(int startRow) {

            try {

                  logger.debug("Retrieving two admin posts");

                  if ( jdbcTemplate == null )

                  {

                        System.out.println("No JDBC Template!");

                  }

                  // Prepare SQL statement

                  String sql = "select id, date, message from db_table Limit "

                              + startRow + ",2";

                  // Map SQL result to a Java object

                  RowMapper<Post> mapper = new RowMapper<Post>() {

                        public Post mapRow(ResultSet rs, int rowNum)

                                    throws SQLException {

                              Post post = new AdminPost();

                              post.setId(rs.getLong("id"));

                              post.setDate(rs.getDate("date"));

                              post.setMessage(rs.getString("message"));

                              return post;

                        }

                  };

                  // Run query then return result

                  return jdbcTemplate.query(sql, mapper);

            }

            catch (NullPointerException ne)

            {

                  System.out.println("NPE");

                  ne.printStackTrace();

                  throw ne;

            }

            catch (Exception e) {

                  e.printStackTrace();

                  logger.error(e);

                  throw new RuntimeException(e);

            }

      }

}

 

My extended interface:

public interface myServiceInterface extends theOriginalServiceInterface {

      public List<Post> getNextTwoSecured(int startRow);

}

 

My extended implementation class:

@Service("myService")

@Transactional

public class myService implements myServiceInterface {

      @Resource(name="origService")

      theOriginalServiceInterface myServiceObj;

      public List<Post> getNextTwoSecured(int startRow) {

            List<Post> myPosts = myServiceObj.getNextTwo(startRow);

            while (myPosts.size() < 2) {

                  myPosts.addAll(myServiceObj.getNextTwo(startRow + 2));

            }

            return myPosts.subList(0, 2);

      }

}