0% found this document useful (0 votes)
26 views

Workshop 8

Uploaded by

Surendra
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
26 views

Workshop 8

Uploaded by

Surendra
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 109

Default Login page Authentication/ Form based Authentication:

Default Login page Authentication means whenever user types


localhost;8080/YourProjectName/login, then Spring's own login page will be displayed[ You
should not have to code for this login page ]. If specified password does not match with the one
stored on SecurityConfig.java, then System generated error page and message will be displayed.
However, if username and password matches with the one stored on SecurityConfig.java file,
then the user will be redirected to welcome.jsp page. Note that earlier in Chapter 4 in Securing
Methods topic; we have developed a sample application for spring security based on XML
configuration. Here, We will develop sample application for spring security that does not use
XML at all but only uses Java classess.

1. Create a Dynamic Web Project named MVCNOXML


2. Right Click your project->Configure->Convert to Maven Project and place
MVCNOXML in the Name field.
3. Inside Java Resources/src folder create two packages namely ch8.mvc.configuration and
ch8.mvc.controller
4. Inside ch8.mvc.configuration, create four different configuration files namely
ServletInitializer.java, MyConfiguration.java, SecurityWebApplicationInitializer.java and
SecurityConfig.java.
5. Keep following lines of code inside ServletInitializer.java:
package ch8.mvc.configuration;

import
org.springframework.web.servlet.support.AbstractAnnotationConfigDispatch
erServletInitializer;

public class ServletInitializer extends


AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected Class<?>[] getRootConfigClasses() {
System.out.println("Inside Security Config Classes");
return new Class[] {MyConfiguration.class};
}

@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[] { "/" };
}
}
Note:
Above class will initialize the DispatcherServlet without web.xml file.

6. Keep following lines of code inside MyConfiguration.java:

package ch8.mvc.configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import
org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "ch8.mvc")
public class MyConfiguration extends WebMvcConfigurerAdapter {

@Bean(name="My")
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new
InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
System.out.println("Inside View Resolver");
return viewResolver;
}
}
Above class defines Spring MVC View Resolvers to avoid writing in dispatcher servlet-
servlet.xml file .Equivalent code for above code for web.xml is:

<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver
">
<property name="prefix">
<value>/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

7. Keep following lines of code inside SecurityWebApplicationInitializer.java:


package ch8.mvc.configuration;

import
org.springframework.security.web.context.AbstractSecurityWebApplicationI
nitializer;

public class SecurityWebApplicationInitializer extends


AbstractSecurityWebApplicationInitializer {

}
Note:

Above class is used to register the DelegatingFilterProxy to use the springSecurityFilterChain. It avoids writing
Filters configuration in web.xml file. The equivalent code that we keep in web.xml file for above is:

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

8. Keep following lines of code inside SecurityConfig.java:

package ch8.mvc.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.authentication.builders.AuthenticationMana
gerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerA
dapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
public void configureUsers(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("USER");

auth.inMemoryAuthentication().withUser("admin").password("root123").roles("ADMIN");

auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DB
A");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().defaultSuccessUrl("/welcome")
.and().httpBasic();
}
}

Note:

To execute above code: we type http://localhost:8080/MVCNOXML/login and even though


we have not coded anything for login page[In above code in formLogin() we have not
specified any URL as a parameter to this method], login page from Spring itself will
automatically popup and if the username and password matches with above credentials, then
user's will be redirected to welcome.jsp page else the Spring itself will generates and displays
error page.

Above code is equivalent to the one that we keep inside application-security.xmle


<intercept-url pattern="/main*" access="ROLE_ADMIN,ROLE_USER" />
<form-login login-page="/login" default-target-url="/main" authentication-
failure-url="/loginError" />
<logout logout-success-url="/logout" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="mukesh" password="password" authorities="ROLE_USER" />
<user name="admin" password="password"
authorities="ROLE_DBA,ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>

9. Inside ch8.mvc.controller class, create a java class file namely MyController.java and
keep following lines of code inside it:

package ch8.mvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MyController {
@RequestMapping(value = { "/welcome","/"}, method =
RequestMethod.GET)
public String homePage(ModelMap model) {
model.addAttribute("greeting", "Hi, Welcome to mysite");
return "welcome";
}
}
10. Inside WeContent/WEB-INF folder, create folder named views and create jsp files
namely welcome.jsp
11. Put following lines of code inside welcome.jsp
<%@ page language="java" contentType="text/html; charset=ISO-
8859-1" pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<title>Welcome page</title>
</head>
<body>
Greeting :
This is a welcome page.
</body>
</html>

12. pom.xml file for above project is:


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>MVCNOXML</groupId>
<artifactId>MVCNOXML</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>MVCNOXML</name>
<description>MVCNOXML</description>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<springframework.version>4.2.6.RELEASE</springframework.version>
<springsecurity.version>4.0.1.RELEASE</springsecurity.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
13. To execute, Right Click Project->Run as-> i) select maven clean and after that ii) select
maven install and after that iii) select Run As-> Run on Server

References:

1. http://www.journaldev.com/8718/spring-4-security-login-logout-inmemory-example
2. https://www.luckyryan.com/2013/02/07/migrate-spring-mvc-servlet-xml-to-java-config/

Custom Login page Authentication:


Custom Login page Authentication means whenever user types
localhost;8080/YourProjectName/login, then instead of Spring's own login page, user coded
login page will be displayed. If specified password does not match with the one stored on
SecurityConfig.java, then instead of System generated error page and message being displaying,
user specified error page will be displayed. However, if username and password matches with
the one stored on SecurityConfig.java file, then the user will be redirected to welcome.jsp page.
We will modify the above program to include custom login feature. For this, you have to follow
below steps:

1. In SecurityConfig.java, modify configure( ) method as shown below:


protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.and().formLogin().loginPage("/login").defaultSuccessUrl("/welcome").failureUrl("/
Access_Denied").usernameParameter("username").passwordParameter("password").and().csr
f().and().exceptionHandling().accessDeniedPage("/Access_Denied");
}

Note that in above code we have specified the URL for login page(/login) and if login is
successful, we have specified the URL of landing page URL(/welcome) and similarly if login is
not successful, we have specified the landing page URL(/Access_Denied)

2. MyController.java should contain following codes

package ch8.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MyController {

@RequestMapping(value = { "/welcome"}, method = RequestMethod.GET)


public String homePage(ModelMap model) {
model.addAttribute("greeting", "Hi, Welcome to mysite");
return "welcome";
}
@RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
public String accessDeniedPage() {

return "accessDenied";
}
@RequestMapping(value = {"/login","/"}, method = RequestMethod.GET)
public String loginPage() {
return "login";
}
}
3. Inside views folder create three files namely login.jsp welcome.jsp and accessDenied.jsp
4. login.jsp should contain following codes:
<%@ page language="java" contentType="text/html;
charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"
uri="http://www.springframework.org/tags/form" %>
<%@ page isELIgnored="false" %>

<html>
<head>
<title>Login Page</title>
</head>
<body>
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" method="post">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}"/>
<c:if test="${param.error != null}">
<p>
Invalid username and password.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</p>
<button type="submit">Log in</button>
</form>
</body>
</html>
5. welcome.jsp should contain following codes
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Welcome page</title>
</head>
<body>
Greeting :
This is a welcome page.
</body>
</html>

6. accessDenied.jsp should contain following codes:


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>AccessDenied page</title>
</head>
<body>
Dear <strong></strong>, You are not authorized to access this page
<a href="<c:url value="/logout" />">Logout</a>
</body>
</html>
Authentication Based on Database:
Custom Login

1. Create users table as shown below:


create table users(
username varchar(50) not null,
password varchar(50) not null,
enabled boolean not null default true,
primary key (username)
);
2. Create authorities table as shown below:
create table authorities (
username varchar(50) not null,
authority varchar(50) not null,
constraint fk_authorities_users foreign key(username)
references users(username)
);
3. Create two users namely user1 and user2 as shown below:
insert into users (username, password) values ('user1','pwd1')
insert into users (username, password) values ('user2','pwd2')
4. Create roles for user1 and user2 as shown below:
insert into authorities (username, authority) values
('user1', 'ADMIN');
insert into authorities (username, authority) values
('user2', 'USER');
5. Add following dependency in pom.xml file:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>
6. In the SecurityConfig class, add a DataSource bean with the following database
connection details:
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("opmcm6718");
return dataSource;
}

7. In the SecurityConfig class, add a DataSourceTransactionManager bean as shown


below:
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new
DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
8. In the SecurityConfig class, make sure you have following method:
@Autowired
public void configureUsers(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource())
.usersByUsernameQuery(
"select username,password,enabled from users where username=?")
.authoritiesByUsernameQuery(
"select username,authority from authorities where username=?");
}

Now, you have to specify the user name and password stored on mysql database
table[user1/pwd1 or user2/pwd2] in order to successfully logged onto the system.

Adding logout Link

In order for logout link to work, you have to follow below steps.

1. If login is successful, user sees welcome.jsp page. In welcome.jsp page, we will add
logout link as shown below:
2. In SecurityConfig.java, the configure() method should have following code:
protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.and().formLogin().loginPage
("/login").defaultSuccessUrl("/welcome").failureUrl("/Access_Denied")
.usernameParameter("username").passwordParameter("password")
.and().logout().logoutRequestMatcher(new
AntPathRequestMatcher("/logout"))
.and().exceptionHandling().accessDeniedPage("/Access_Denied");

In above code, we have removed .and().csrf() because if we keep this code, then we have to send
logout request as POST request. In welcome.jsp, we are going to send /logout request as GET
request. In order to send logout request as a GET request, we have to specify logout() method as
shown above. It has to be noted that, above code alone is sufficient for logout to work. Whenever
we click, logout link on main.jsp, user is automatically redirected to login.jsp

Authorizing only users with a specific roles to view some pages


1. Create a jsp file named admin.jsp inside views folder and keep following codes inside it:
2. Create following method in Controller class defined above to map /admin URL
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String adminPage() {
return "admin";
}
3. Modify welcome.jsp as shown below to include link for viewing admin.jsp as shown
below:

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


pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-
8859-1">
<title>Welcome page</title>
</head>
<body>
Greeting :
This is a welcome page.
<br>
<a href ="/MVCNOXML/logout">Click Here for Logout</a>
<br>
<a href ="/MVCNOXML/admin"> View Admin Page</a>
</body>
</html>
4. Note the following code you have kept in configure( ) method of SecurityConfig.java
You should avoid using hasRole () such as
.antMatchers("/admin/**").access("hasRole('ADMIN')")

Instead, you should use following;

.antMatchers("/admin").access("hasAuthority('ADMIN')")

It means access to URL /admin is permitted only to those users who has ADMIN role.
Hence, if you login with user1[which has ADMIN role], then only you are able to click
on "View Admin Page" link on main.jsp and then admin.jsp will be displayed. However,
of you were logged in using user2 then you are not able to display admin.jsp because
user2 does not have ADMIN role.

Displaying page elements only to authenticated users in views


Sometimes there might come requirement to display some page elements only to authenticated
users. For example, information about the currently logged in user etc. To implement this
feature, follow below steps:

1. Add following dependency in pom.xml file.


<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
2. In welcome.jsp, define following taglib at the very top:
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
3. Add following code in main.jsp file
<sec:authorize access="isAuthenticated()">
Username: <sec:authentication property="principal.username" />
</sec:authorize>
Above code displays username only if the user is authenticated.
4. Add following code in main.jsp file
<sec:authorize access="isAnonymous()">
This will be shown only to not-authenticated users.
</sec:authorize>

Above code is displayed only if the user is logged as Anonymous user.


5. Add following code in main.jsp file
<sec:authorize access="hasAnyAuthority('ADMIN', 'DBA')">
This will be shown only to users who have the "ADMIN" or the "WARRIOR"
authority.
</sec:authorize>

Above code is executed only if the currently logged user has either ADMIN or DBA role.

Using Public Folders


Some folders require their contents to be accessed without authentication, for example folder
containing CSS files, Java Script files and static images. There are two ways for achieving this:

1. override the configure(WebSecurity web) in above SecurityConfig.java file


@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/css/**")
.antMatchers("/js/**")
.antMatchers("/img/**");
}
In above code, /css,/js, /img are all public URLs.
2. define configure(HttpSecurity http) method

In above code URLs "/" and "/home" are public URLs.

Using Https in Web Server/Generating Self Signed Certificate


1. Type the command highlighted as shown below:

keytool -genkey -alias tomcat -keyalg RSA

When asked for password, Enter "password" and fill the information accordingly and when
finished enter "yes" at last.

Above command will create .keystore file in user's directory[C:\Users\user] directory.


2. Copy the above .keystore file to tomcat root folder
3. Modify server.xml and put following codes inside it:

<Connector SSLEnabled="true" acceptCount="100" clientAuth="false"


disableUploadTimeout="true" enableLookups="false" maxThreads="25"
port="8443" keystoreFile=".keystore" keystorePass="password"
protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https"
secure="true" sslProtocol="TLS" />

4. Now, start your tomcat server by clicking startup


5. Access your server using URL https://localhost:8443

Annotation based authorization for classes and methods

Access to a particular method or class can be secured by applying annotations such as


@PreAuthorize, @PostAuthorize,@PreFilter,@PostFilter. For more details, refer to Chapter-4
"Securing methods"

View Layer Authorization using JSP Tag Libraries

The JavaServer Pages Standard Tag Library (JSTL) is a collection of useful JSP tags which
encapsulates core functionality common to many JSP applications. JSTL has support for
common, structural tasks such as iteration and conditionals, tags for manipulating XML
documents, internationalization tags, and SQL tags.

So far we have learned securing our methods and login form. All the security implementations
were made at controller or model layer. We can also implement security at view layer. This is
necessary because we want to hide some content of a web page based on user's role so that he
will not be able to see those.

Spring provide basically 3 tags for securing view layer information i.e.

 authorize tag <sec:authorize>


 authenticate tag <sec:authentication>
 accesscontrollist tag

In above section "displaying page elements only to authenticated users in views" We have
already discussed about implementing above first two tags.

Following are the common expressions that can be used in access attribute of “<sec:authorize/>”
tag:
 hasRole([role]) : Returns true only if the login user has the role specified in [role].
 hasAnyRole([role1,role2]) : Returns true only if the login user has atleast one role
specified in [role1,role2]. The roles will be specified in comma separated format.
 isAnonymous() : Returns true only is the login user is an anonymous user.
 isAuthenticated() : Returns true if the user is not an anonymous user.
 isFullyAuthenticated() : Returns true if the user is not an anonymous user or a
remember me user.
 isRememberMe() : Returns true if the user is a remember me user.

The accesscontrollist tag is only valid when used with Spring Security's ACL module. It checks a
comma-separated list of required permissions for a specified domain object. If the current user
has any of those permissions, then the tag body will be evaluated. If they don't, it will be
skipped. An example might be

<sec:accesscontrollist hasPermission="1,2" domainObject="someObject">

This will be shown if the user has either of the permissions


represented by the values "1" or "2" on the given object.

</sec:accesscontrollist>

Reference:

http://docs.spring.io/spring-security/site/docs/3.0.x/reference/taglibs.html

Authorization using Custom Expression Evaluators

Spring Expression Language(SpEL) allows the developer to embed simple codes in the
configuration files much alike simple strings, which are executed runtime.

Even though, there are a lot of built-in expressions that we can use in Spring Security, such as
has Authority( ), hasAnyRole( ), permitAll( )etc, We might require some more expressions
which are not provided by Spring built in for example: "over18".

Now, we are going to create our own custom expression isLocal( ) which is true if user is
accessing the web application from local machine i.e. from the same machine where web server
resides.

In order to implement above requirement, we have to develop Custom Expression Evaluators as


shown below: [We are going to increase feature on our MVCNOXML project]
1. Create a package named ch8.mvc.customexpression and create classes namely
CustomWebSecurityExpressionRoot

package ch8.mvc.customexpression;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.expression.WebSecurityExpressionRoot;

public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot


{
public CustomWebSecurityExpressionRoot(Authentication a, FilterInvocation fi)
{
super(a, fi);
}
public boolean isLocal()
{
//return false;
return "localhost".equals(request.getServerName());
}
}

2. CustomWebSecurityExpressionRoot is a class where we implement our custom


expression isLocal( ) as shown below:
package ch8.mvc.customexpression;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.expression.WebSecurityExpressionRoot;

public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot


{
public CustomWebSecurityExpressionRoot(Authentication a, FilterInvocation fi)
{
super(a, fi);
}
public boolean isLocal()
{
//return false;
return "localhost".equals(request.getServerName());
}
}

3. Create another class named CustomWebSecurityExpressionHandler and keep following


codes inside it:
package ch8.mvc.customexpression;

import org.springframework.security.access.expression.SecurityExpressionOperations;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHan
dler;
import
org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
import org.springframework.stereotype.Component;

@Component
public class CustomWebSecurityExpressionHandler extends
DefaultWebSecurityExpressionHandler
{
private final AuthenticationTrustResolver trustResolver = new
AuthenticationTrustResolverImpl();
protected SecurityExpressionOperations
createSecurityExpressionRoot(Authentication authentication,FilterInvocation fi)
{
System.out.println("Inside the createSecurityExpressionRoot method");
WebSecurityExpressionRoot root = new
CustomWebSecurityExpressionRoot(authentication, fi);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
public MyPermissionEvaluator getPermissionEvaluator()
{
return new MyPermissionEvaluator();
}
public RoleHierarchy getRoleHierarchy() {
RoleHierarchyImpl r = new RoleHierarchyImpl();
r.setHierarchy("USER > ADMIN ");
System.out.println("Role Hierarchy is Set");
return r;
}
}

Note:
i) In above code, We had set roleHierarchy and hence if any user who has USER
previleges, has also ADMIN privilege as defined inside getRoleHierarchy( )
method. Hence, if you login using user2/pwd2 and click "admin page" link on
main.jsp file, you are allowed to do it.

4. Create another class named MyPermissionEvaluator and put following codes inside it:
package ch8.mvc.customexpression;

import java.io.Serializable;

import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;

public class MyPermissionEvaluator implements PermissionEvaluator{

@Override
public boolean hasPermission(Authentication authentication, Object
targetDomainObject, Object permission) {
boolean hasPermission = false;
if ( authentication != null && permission instanceof String){
hasPermission =false;

} else {
hasPermission =false;
}
System.out.println("Upper Method Called");
return hasPermission;
}
@Override
public boolean hasPermission(Authentication authentication,
Serializable targetId, String targetType, Object permission) {
System.out.println("Lower Method Called");
throw new RuntimeException("Id and Class permissions are not
supperted by this application");
}

}
5. Now, inside SecurityConfig.java, modify configure(HttpSecurity http) method as shown
below to register CustomWebSecurityExpressionHandler and use isLocal( ) expression as
shown below:

See the highlighted code above: It means access to /admin can be made if currently logged on
user had ADMIN role and the URL is being accessed from the local machine.

ACL for Domain Objects


In Spring, We can secure domain objects using Access Control List(ACL). ACL is used to map
the permissions on the domain objects against the users of an application. ACL stores operation
against an object by user.

If we need to implement the complex rules, the intercept-url is unable to cope with the complex
rules. Because intercept-url is meant to secure at the URL-level only. However, The complex
rules are operating at the domain level. Hence, to implement complex rules; we have to use
ACL at the object level and intercept-url at the URL-level.

Assume that there are following three users:

 Mukesh (ROLE_ADMIN role)


 Manisha(ROLE_USER role)
 Muskan(ROLE_VISITOR role)

We are going to develop a bulleting application where user having above roles will manage
following three types of posts:

 AdminPost
 PersonalPost
 PublicPost

[Quick Thought: We require a bulletin database along with three tables namely admin_post,
personal_post, public_post each table contain three fields namely id,date,message]

Let us further assume that we are going to enforce following rules:

 Only users with ROLE_ADMIN can create AdminPost


 Only users with ROLE_USER can create PersonalPost
 Only users with ROLE_ADMIN or ROLE_USER can create PublicPost
 Users with ROLE_VISITOR cannot create any post Note: When we use the word 'create',
we mean adding a new post.
 A user can edit and delete posts that belongs only to them regardless of the role.
 A user with ROLE_ADMIN or ROLE_USER can edit and delete PublicPosts.
 We are required to show all posts in the main Bulletin page
 ROLE_ADMIN can see all posts
 ROLE_USER can see Personal and Public posts
 ROLE_VISITOR can only see Public posts

Implementation Steps:
 In order to implement above requirement, we have to create two databases on mysql
namely: acl and bulletin. [ You can create these databases and following tables using
phpmyadmin]
 In acl database, we have to create following four tables namely
acl_class,acl_sid,acl_object_identity and acl_entry
 In bulleting database, We need to create three tables namely admin_post, personal_post
and public_post] Each table contains three columns namely id,date and message
 acl_class is a table where we keep FQDN of domain object classes that we want to secure

eld Description
id The primary key
class The fully qualified name of the domain object

 acl_sid is a table where we keep information about the user's or role that are going to
access on domain object.

Field Description
id The primary key
principal A flag to indicate if the sid field is a username or a role
sid The actual username (ie. john) or role (ie. ROLE_ADMIN)

 Main complicated table is acl_object_identity. This table gathers information from three
tables[acl_class,acl_sid and all three tables of bulletin database] and provide this
information to acl_entry table. It keeps information like "Which particular particular row
of bulletin database table[object_id_identity] is associated with which particular domain
class[object_id_class]. We assign ADMIN user to each of the association as
well[owner_sid]. In conclusion, this table maintains information about domain objects by
relating actual rows of three database tables of bulletin database and acl_class[ There are
9 domain objects in our case since each table comprises three rows. Hence, 9 rows in this
table as well. ]

Field Description
Id The primary key
Refers to the id field in the acl_class. This is a reference to the fully qualified
object_id_class
name of the class
Refers to the primary id of the domain object. The id is assigned from another
object_id_identity database: the Bulletin database (See the Bulletin Database below). Every
domain object in the application needs to have a unique id.
parent_object Refers to the id of the parent object if existing
owner_sid Refers to the id field in the acl_sid. This is a reference to the username or role
entries_inheriting A flag to indicate whether the object has inherited entries
 Main, important and core table is acl_entry. Acl_object_identity table stores information
about domain objects whereas this table stores which user has what permission on which
particular domain object. Domain object represented by acl_object_identity column that
references id field of acl_object_identity table. User is represented by sid and the
permission is represented by mask.

Field Description
id The primary key
acl_object_identity Refers to the id field in the acl_object_identity table
ace_order Refers to the ordering of the access control entries
sid Refers to the id field in the acl_sid table
A bitwise mask to indicate the permissions. A value of 1 is equivalent to READ
mask
permission, 2 for WRITE, and so forth.
A flag to indicate whether the mask should be interpreted as granting access or
granting
deny access
audit_success A flag to indicate whether to audit a successful permission
audit_failure A flag to indicate whether to audit a failed permission

Relationships among the acl table is as following:


acl_object_identity

 Object_id_class column references id(AdminPost,PersonalPost,PublicPost) of acl_class


table
 Object_id_identity corresponds to the id values of admin_post(3 rows), personal_post(3
rows), public_post( 3 rows) of bulletin database]
 Owner_sid corresponds to the id field of acl_sid table.
 First three row means [ AdminPost(id=1) class instance corresponds to three rows of
admin_post table[ 1,2,3->ids]- for mukesh user(owner_sid=1)
 Row 4,5,6 means [ PersonalPost(id=2) class instance corresponds to three rows of
personal_post table[ 1,2,3->ids]- of bulletin database]-for mukesh user(owner_sid=1)
 Row 7,8,9 means [ PublicPost(id=3) class instance corresponds to three rows of
public_post table[ 1,2,3->ids]- of bulletin database]-for mukesh user(owner_sid=1)

Acl_entry table
 Row no 1 to row no 15[ 15 rows] represent permission information for users having
ADMIN role[sid=1] i.e. mukesh user

We can see 15 rows in following matrix


User Previlege: Admin
Post Type View Add Edit Delete
Admin x x x x
Personal x
Public x x x x

o Row no 1 to 3 represent the fact that user having ADMIN role(sid=1 i.e. user
mukesh ) has read(mask=1) permission on all three rows of admin_post table
(acl_object_identity=1,2,3) [ Second Row Second Column]
o Row no 4 to 6 represent the fact that user having ADMIN role(sid=1 i.e. user
mukesh ) has write/delete/add(mask=2) permission on all three rows of
admin_post table (acl_object_identity=1,2,3) [ Second Row and Third,Fourth
and Fifth Column]
o Row no 7 to 9 represent the fact that user having ADMIN role(sid=1 i.e. user
mukesh ) has read(mask=1) permission on all three rows of personal_post
table (acl_object_identity=4,5,6)[ Third Row and Second Column]
o Row no 10 to 12 represent the fact that user having ADMIN role(sid=1 i.e.
user mukesh ) has read(mask=1) permission on all three rows of public_post
table (acl_object_identity=7,8,9) )[ Fourth Row and Second Column]

o Row no 13 to 15 represent the fact that user having ADMIN role(sid=1 i.e.
user mukesh ) has write/delete/add(mask=2) permission on all three rows of
public_post table (acl_object_identity=7,8,9) )[ Fourth Row and Third, Fourth
and Fifth Column]

 Row no 16 to row no 27 [ 12 rows altogether] represent permission information for


users having USER role[sid=1] i.e. manisha user
We can see 12 rows in matrix form as following;
Previlege:User
Post Type View Add Edit Delete
Admin
Personal x x x X
Public x x x X

o Row no 16 to 18 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has read(mask=1) permission on all three rows of personal_post
table (acl_object_identity=4,5,6) )[ Third Row and Second Column]
o Row no 19 to 21 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has add/edit/delete(mask=2) permission on all three rows of
personal_post table (acl_object_identity=4,5,6) )[ Third Row and
Third,Fourth and Fifth Column]
o Row no 22 to 24 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has read(mask=1) permission on all three rows of public_post table
(acl_object_identity=7,8,9) )[ Fourth Row and Second Column]
o Row no 25 to 27 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has add/edit/delete(mask=2) permission on all three rows of
personal_post table (acl_object_identity=7,8,9) )[ Fourth Row and
Third,Fourth and Fifth Column]
 Row no 28 to row no 30 [ 3 rows] represent permission information for users having
VISITOR role[sid=1] i.e. muskan user.
In Matrix Form,

Role:Visitor
Post Type View Add Edit Delete
Admin
Personal
Public x

o Row no 28 to 30 represent the fact that user having Visitor role(sid=3 i.e. user
muskan ) has read(mask=1) permission on all three rows of public_post table
(acl_object_identity=7,8,9) )[ Fourth Row and Second Column]

Note:

Priority [ace_order]is mained as following:

 Admin User read=1; Admin user Write=2;User read=3;User Write=4; Visitor Read=5

Inside WebContent/WEB-INF folder we have to create 6 different XML files namely


web.xml,spring-servlet.xml,spring-security.xml,acl-context.xml,applicationContext.xml and
bulletin-context.xml. Let us explain each of these files in more detail:

 Web.xml is a system generated file where we maintain information about dispatcher


servlet, servlet URL mapping and Filter. In this file, we specify location of
applicationContext.xml and spring-security.xml
 acl-context.xml:In this file, we will specify configuration information for acl database
connectivity.
 spring-security.xml: We import acl-context.xml file from this file and define user name
and password information here.
 applicationContext.xml: In this file, we import bulletin-context.xml and we specify folder
to be scanned to detect controllers and mvc annotations.
 In bulletin.xml file we specify database connectivity information for accessing bulletin
database.
 spring-servlet.xml:It is dispatcher servlet's context file where we define view resolver
information
Let us explain in detail the content of spring-security.xml and acl-context.xml in detail

spring-security.xml

This contains standard Spring Security configuration. It declares the following:

 We import acl-context.xml resource in this file as following:


 A set of intercept-url patterns.
<security:http auto-config="true" use-expressions="true" >
<security:csrf disabled="true"/>
<security:intercept-url pattern="/auth/login" access="permitAll"/>
<security:intercept-url pattern="/acldemo/bulletin/view"
access="hasRole('ROLE_VISITOR')"/>
<security:intercept-url pattern="/acldemo/role/admin"
access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/acldemo/role/user"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/acldemo/role/visitor"
access="hasRole('ROLE_VISITOR')"/>
<security:form-login login-page="/acldemo/auth/login" default-target-
url="/acldemo/bulletin/view" login-processing-url="/j_spring_security_check"
authentication-failure-url="/acldemo/auth/login?error=true" username-
parameter="username" password-parameter="password" />
<security:logout logout-success-url="/logout" />

</security:http>
 An authentication manager

<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service>
<security:user name="mukesh"
password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_ADMIN, ROLE_USER,
ROLE_VISITOR" />
<security:user name="manisha"
password="ee11cbb19052e40b07aac0ca060c23ee" authorities="ROLE_USER, ROLE_VISITOR" />
<security:user name="muskan"
password="127870930d65c57ee65fcc47f2170d38" authorities="ROLE_VISITOR" />
</security:user-service>
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
 An Md5 password encoder

<bean
class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"
id="passwordEncoder"/>
acl-context.xml

This contains ACL-related configuration. It declares the following:

1. A global-method-security tag which enables method security expressions: It enables


method security annotation as following:

<security:global-method-security pre-post-annotations="enabled">
<!-- Reference to a custom expression handler with ACL support -->
<security:expression-handler ref="expressionHandler" />
</security:global-method-security>

2. An expression handler: It defines a custom expression handler instance. Without this


property Spring Security will declare a default expression handler with no ACL support.
We need to declare a custom handler because we need ACL support. It has to be noted
that Spring provides a default implementation of Expression Handler so that we can
customize it and we don't have to create it from scratch.

<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecu
rityExpressionHandler" p:permissionEvaluator-ref="permissionEvaluator"
p:roleHierarchy-ref="roleHierarchy" />

Here we declared a reference to a customized expression


handler:DefaultMethodSecurityExpressionHandler. This is actually the default
expression handler but it needs to be declared manually so that we can provide a
customized permission evaluator.The permissionEvaluator property defines a reference
to a custom permission evaluator, while theroleHierarchy allows us to define the
hierarchy of our roles.

3. A permission evaluator: It evaluates permission via the ACL module.


AclPermissionEvaluator is the default implementation for evaluating ACLs with
expression-based access control but it's not enabled by default. It needs to be declared
manually and it needs to be customized.
The <constructor-arg ref="aclService"/> is a reference to a custom ACL service.
Basically this is the service that will access the ACL database.

<bean class="org.springframework.security.acls.AclPermissionEvaluator"
id="permissionEvaluator">
<!-- Reference to the ACL service which performs JDBC calls to an ACL
database -->
<constructor-arg ref="aclService"/>
</bean>

4. An ACL service: We are going to use JdbcMutableAclService as an ACL service for our
application. The JdbcMutableAclService is a JDBC-based ACL service. It
uses JdbcTemplate to simplify JDBC access to MySQL ACL schema that we had created
earlier. By default the JdbcMutableAclService uses HSQLDB; even though PostgreSQL
schema is also available.

<bean class="org.springframework.security.acls.jdbc.JdbcMutableAclService"
id="aclService">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="lookupStrategy"/>
<constructor-arg ref="aclCache"/>
</bean>

o The <constructor-arg ref="dataSource"/> is a reference to a datasource, in our


case, a MySQL datasource.
o The <constructor-arg ref="lookupStrategy"/> is a reference to a lookup strategy.
Its purpose is to provide an optimized lookup when querying the database.
o The <constructor-arg ref="aclCache"/> is a reference to an ACL cache. Its
purpose is to lessen database access by checking first if the ACL entry is already
available in the cache.

5. A lookup strategy: The purpose of a lookup strategy is to provide an optimized lookup


when querying the database. BasicLookupStrategy performs lookups in a manner that is
compatible with ANSI SQL.

<bean id="lookupStrategy"
class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="aclCache"/>
<constructor-arg ref="aclAuthorizationStrategy"/>
<constructor-arg ref="auditLogger"/>
</bean>
o The <constructor-arg ref="dataSource"/> is a reference to the same MySQL
datasource described below.
o The <constructor-arg ref="aclCache"/> is a reference to the same ACL cache
described below.
o The <constructor-arg ref="aclAuthorizationStrategy"/> is a reference to
an AclAuthorizationStrategyimplementation.
6. A datasource: This is a standard MySQL datasource that uses a C3P0 connection pool.
The jdbcUrl property points to the acl database.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://localhost:3306/acl"
p:user="root"
p:password="opmcm6718"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
7. An ACL cache : We declare Spring Security's default implementation
of AclCache interface in order to lessen database lookups. Ehcache is an open source,
standards-based cache used to boost performance, offload the database and simplify
scalability. The EhCacheBasedAclCache constructor accepts an instance of an EHCache
instance. TheEhCacheFactoryBean is responsible for crafting this instance.
8. An ACL authorization strategy: It is used to determine whether a principal is permitted to
call administrative methods.

<bean id="aclAuthorizationStrategy"
class="org.springframework.security.acls.domain.AclAuthorizationStrategy
Impl">
<constructor-arg>
<list>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
</list>
</constructor-arg>
</bean>
9. A role hierarchy: Role hierarchy is a way of declaring which role is the boss of other
roles. In our sample configuration ROLE_ADMIN > ROLE USER, means whenever a
user has a ROLE_ADMIN, he also gets
the ROLE_USER. And because we declared ROLE_USER > ROLE_VISITOR, he also
gets the ROLE_VISITOR.

<bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImp
">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
ROLE_USER > ROLE_VISITOR
</value>
</property>
</bean>
Steps for creating application:

1. Create a Dynamic Web Project named SpringACL and include following 27 different jar
files both in Java Build Path and Deployment Assembly
c3po-0.9.0.4 cglib-nodep-2.2 aopalliance-1.0.0 commons-logging- ehcache-2.7.0
1.2
hsqldb-2.2.8 jstl-1.2 log4j-1.2.15 mysqlconnector sl4j-api-1.7.21.jar
sl4j-log4j12-1.7.21 spring-aop- spring-beans- spring-context- spring-context-
4.2.2.RELEASE.jar 4.2.2.RELEASE.jar 4.2.2.RELEASE.jar support-
4.2.2.RELEASE.jar
spring-core- spring-expression- spring-jdbc- spring-security-acl- spring-security-
4.2.2.RELEASE.jar 4.2.2.RELEASE.jar 3.2.8.RELEASE.jar 3.1.2.RELEASE.jar config-
4.1.0.RELEASE.jar
spring-security-core- spring-security-web- spring-tx- spring-web- spring-webmvc-
4.1.0.RELEASE.jar 4.1.0.RELEASE.jar 4.2.2.RELEASE.jar 4.2.2.RELEASE.jar 4.2.2.RELEASE.jar
standard-1.1.2.jar taglibs-standard-
impl-1.2.5.jar

2. Create 3 packages inside src folder namely ch8.acl.demo.controller, ch8.acl.demo.domain


and ch8.acl.demo.service.
3. Inside ch8.acl.demo.domain package create three Java files namely
AdminPost,PersonalPost and PublicPost and one interface file namely Post.
o AdminPost is a simple POJO class that represents admin_post.This class has
following codes:
package ch8.acl.demo.domain;

import java.util.Date;

/**
* A simple POJO representing admin posts
*/
public class AdminPost implements Post {
private Long id;
private Date date;
private String message;

public Long getId() {


return id;
}

public void setId(Long id) {


this.id = id;
}

public Date getDate() {


return date;
}

public void setDate(Date date) {


this.date = date;
}
public String getMessage() {
return message;
}

public void setMessage(String message) {


this.message = message;
}
}

o PersonalPost is a simple POJO class that represents personal_post.This class has


following codes:
package ch8.acl.demo.domain;

import java.util.Date;

/**
* A simple POJO representing personal posts
*/
public class PersonalPost implements Post {
private Long id;
private Date date;
private String message;

public Long getId() {


return id;
}

public void setId(Long id) {


this.id = id;
}

public Date getDate() {


return date;
}

public void setDate(Date date) {


this.date = date;
}

public String getMessage() {


return message;
}

public void setMessage(String message) {


this.message = message;
}
}
o PublicPost is a simple POJO class that represents public_post.This class has
following codes:
package ch8.acl.demo.domain;

import java.util.Date;

/**
* A simple POJO representing public posts
*/
public class PublicPost implements Post {
private Long id;
private Date date;
private String message;

public Long getId() {


return id;
}

public void setId(Long id) {


this.id = id;
}

public Date getDate() {


return date;
}

public void setDate(Date date) {


this.date = date;
}

public String getMessage() {


return message;
}

public void setMessage(String message) {


this.message = message;
}
}

o Post is just a simple interface which is implemented by AdminPost,PersonalPost


and PublicPost class.
package ch8.acl.demo.domain;
import java.util.Date;

/**
* A simple interface for Post objects
*/
public interface Post {
public Long getId();

public void setId(Long id);

public Date getDate();

public void setDate(Date date);

public String getMessage();

public void setMessage(String message);

4. Inside ch8.acl.demo.service package create three Java files namely AdminService,


PersonalService and PublicService and one interface file called Generic service
o AdminService class comprises of methods to add/edit/delete posts, retrieve all
posts, get a particular post by user having ROLE_ADMIN authority

package ch8.acl.demo.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import ch8.acl.demo.domain.AdminPost;
import ch8.acl.demo.domain.Post;

/**
* Service for processing Admin related posts.
* <p>
* For a complete reference to Spring JDBC and JdbcTemplate
* see http://static.springsource.org/spring/docs/3.0.x/spring-framework-
reference/html/jdbc.html
* <p>
* For transactions, see http://static.springsource.org/spring/docs/3.0.x/spring-
framework-reference/html/transaction.html
*/
@SuppressWarnings("deprecation")
@Service("adminService")
@Transactional
public class AdminService implements GenericService {

protected static Logger logger = Logger.getLogger("service");

// We'll be calling SQL statements. SimpleJdbcTemplate is a perfect tool.


private SimpleJdbcTemplate jdbcTemplate;

@Resource(name="bulletinDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}

@SuppressWarnings("deprecation")
public Post getSingle(Long id) {
try {
logger.debug("Retrieving single admin post");

// Prepare SQL statement


String sql = "select id, date, message from admin_post
where id = ?";

// Assign values to parameters


Object[] parameters = new Object[] {id};

// 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.queryForObject(sql, mapper, parameters);

} catch (Exception e) {
logger.error(e);
return null;
}
}

public List<Post> getAll() {


try {
System.out.println("Retrieving all Admin Posts..");
logger.debug("Retrieving all admin posts");

// Prepare SQL statement


String sql = "select id, date, message from admin_post";

// 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 (Exception e) {
logger.error(e);
return null;
}
}

public Boolean add(Post post) {


try {
logger.debug("Adding new post");

// Prepare SQL statement


String sql = "insert into admin_post(date, message) values "
+
"(:date, :message)";

// Assign values to parameters


Map<String, Object> parameters = new HashMap<String,
Object>();
parameters.put("date", post.getDate());
parameters.put("message", post.getMessage());

// Save
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}

public Boolean edit(Post post) {


try {
logger.debug("Adding new post");

// Prepare our SQL statement


String sql = "update admin_post set date = :date, " +
"message = :message where id = :id";

// Assign values to parameters


Map<String, Object> parameters = new HashMap<String,
Object>();
parameters.put("id", post.getId());
parameters.put("date", post.getDate());
parameters.put("message", post.getMessage());
// Save
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}

public Boolean delete(Post post) {


try {
logger.debug("Deleting existing post");

// Prepare our SQL statement


String sql = "delete from admin_post where id = ?";

// Assign values to parameters


Object[] parameters = new Object[] {post.getId()};

// Delete
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}

o PersonalService class comprises of methods to add/edit/delete posts, retrieve all


posts, get a particular post by user having ROLE_USER authority.
package ch8.acl.demo.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import ch8.acl.demo.domain.PersonalPost;
import ch8.acl.demo.domain.Post;

/**
* Service for processing Personal related posts.
* <p>
* For a complete reference to Spring JDBC and JdbcTemplate
* see http://static.springsource.org/spring/docs/3.0.x/spring-framework-
reference/html/jdbc.html
* <p>
* For transactions, see http://static.springsource.org/spring/docs/3.0.x/spring-
framework-reference/html/transaction.html
*/
@Service("personalService")
@Transactional
public class PersonalService implements GenericService {

protected static Logger logger = Logger.getLogger("service");

// We'll be calling SQL statements. SimpleJdbcTemplate is a perfect tool.


private SimpleJdbcTemplate jdbcTemplate;

@Resource(name="bulletinDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public Post getSingle(Long id) {
try {
logger.debug("Retrieving single personal post");

// Prepare SQL statement


String sql = "select id, date, message from personal_post
where id = ?";

// Assign values to parameters


Object[] parameters = new Object[] {id};

// 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 PersonalPost();
post.setId(rs.getLong("id"));
post.setDate(rs.getDate("date"));
post.setMessage(rs.getString("message"));
return post;
}
};

// Run query then return result


return jdbcTemplate.queryForObject(sql, mapper, parameters);

} catch (Exception e) {
logger.error(e);
return null;
}
}

public List<Post> getAll() {


logger.debug("Retrieving all personal posts");

// Prepare the SQL statement


String sql = "select id, date, message from personal_post";

// 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 PersonalPost();
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);
}

public Boolean add(Post post) {


try {
logger.debug("Adding new post");

// Prepare SQL statement


String sql = "insert into personal_post(date, message)
values " +
"(:date, :message)";

// Assign values to parameters


Map<String, Object> parameters = new HashMap<String,
Object>();
parameters.put("date", post.getDate());
parameters.put("message", post.getMessage());

// Save
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}
public Boolean edit(Post post) {
try {
logger.debug("Adding new post");

// Prepare our SQL statement


String sql = "update personal_post set date = :date, " +
"message = :message where id = :id";

// Assign values to parameters


Map<String, Object> parameters = new HashMap<String,
Object>();
parameters.put("id", post.getId());
parameters.put("date", post.getDate());
parameters.put("message", post.getMessage());

// Save
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}

public Boolean delete(Post post) {


try {
logger.debug("Deleting existing post");

// Prepare our SQL statement


String sql = "delete from personal_post where id = ?";

// Assign values to parameters


Object[] parameters = new Object[] {post.getId()};

// Delete
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}

}
o PublicService class comprises of methods to add/edit/delete posts, retrieve all
posts, get a particular post by user having ROLE_VISITOR authority.

package ch8.acl.demo.service;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import ch8.acl.demo.domain.Post;
import ch8.acl.demo.domain.PublicPost;

/**
* Service for processing Public related posts.
* <p>
* For a complete reference to Spring JDBC and JdbcTemplate
* see http://static.springsource.org/spring/docs/3.0.x/spring-framework-
reference/html/jdbc.html
* <p>
* For transactions, see http://static.springsource.org/spring/docs/3.0.x/spring-
framework-reference/html/transaction.html
*/
@Service("publicService")
@Transactional
public class PublicService implements GenericService {

protected static Logger logger = Logger.getLogger("service");

// We'll be calling SQL statements. SimpleJdbcTemplate is a perfect tool.


private SimpleJdbcTemplate jdbcTemplate;

@Resource(name="bulletinDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}

public Post getSingle(Long id) {


try {
logger.debug("Retrieving single public post");

// Prepare SQL statement


String sql = "select id, date, message from public_post
where id = ?";

// Assign values to parameters


Object[] parameters = new Object[] {id};

// 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 PublicPost();
post.setId(rs.getLong("id"));
post.setDate(rs.getDate("date"));
post.setMessage(rs.getString("message"));
return post;
}
};

// Run query then return result


return jdbcTemplate.queryForObject(sql, mapper, parameters);
} catch (Exception e) {
logger.error(e);
return null;
}
}

public List<Post> getAll() {


try {
logger.debug("Retrieving all public posts");

// Prepare SQL statement


String sql = "select id, date, message from public_post";

// 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 PublicPost();
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 (Exception e) {
logger.error(e);
return null;
}
}

public Boolean add(Post post) {


try {
logger.debug("Adding new post");

// Prepare SQL statement


String sql = "insert into public_post(date, message) values "
+
"(:date, :message)";

// Assign values to parameters


Map<String, Object> parameters = new HashMap<String,
Object>();
parameters.put("date", post.getDate());
parameters.put("message", post.getMessage());

// Save
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}

public Boolean edit(Post post) {


try {
logger.debug("Adding new post");

// Prepare our SQL statement


String sql = "update public_post set date = :date, " +
"message = :message where id = :id";

// Assign values to parameters


Map<String, Object> parameters = new HashMap<String,
Object>();
parameters.put("id", post.getId());
parameters.put("date", post.getDate());
parameters.put("message", post.getMessage());

// Save
jdbcTemplate.update(sql, parameters);

// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}

public Boolean delete(Post post) {


try {
logger.debug("Deleting existing post");

// Prepare our SQL statement


String sql = "delete from public_post where id = ?";

// Assign values to parameters


Object[] parameters = new Object[] {post.getId()};

// Delete
jdbcTemplate.update(sql, parameters);

// Return
return true;

} catch (Exception e) {
logger.error(e);
return false;
}
}
}
o GenericService is an interface implemented by AdminService, PersonalService
and PublicService class.
package ch8.acl.demo.service;

import java.util.List;

import javax.sql.DataSource;

import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;

import ch8.acl.demo.domain.Post;
/**
* A generic service for handling CRUD operations.
* <p>
* The method access-control expressions are specified in this interface.
*/
public interface GenericService {

/**
* Inject the datasource for the bulletingapplication
*/
public void setDataSource(DataSource dataSource);

/**
* Retrieves a single post.
* <p>
* Access-control will be evaluated after this method is invoked.
* returnObject refers to the returned object.
*/
@PostAuthorize("hasPermission(returnObject, 'WRITE')")
public Post getSingle(Long id);

/**
* Retrieves all posts.
* <p>
* Access-control will be evaluated after this method is invoked.
* filterObject refers to the returned object list.
*/
@PostFilter("hasPermission(filterObject, 'READ')")
public List<Post> getAll();

/**
* Adds a new post.
* <p>
* We don't provide any access control here because
* the new object doesn't have an id yet.
* <p>
* Instead we place the access control on the URL-level because
* the Add page shouldn't be visible in the first place.
* <p>
* There are two places where we can place this restriction:
* <pre>
* 1. At the controller method
* 2. At the external spring-security.xml file</pre>
* <p>
*
*/
public Boolean add(Post post);

/**
* Edits a post.
* <p>
* Access-control will be evaluated before this method is invoked.
* <b>#post</b> refers to the current object in the method argument.
*/
@PreAuthorize("hasPermission(#post, 'WRITE')")
public Boolean edit(Post post);

/**
* Deletes a post.
* <p>
* Access-control will be evaluated before this method is invoked.
* <b>#post</b> refers to the current object in the method argument.
*/
@PreAuthorize("hasPermission(#post, 'WRITE')")
public Boolean delete(Post post);

}
5. Inside ch8.acl.demo.controller create following 8 different controller files namely
AdminController, BulletinController, LoginLogout Controller, PersonalController,
PublicController and RoleController.
o LoginLogoutController: This class is responsible for processing a request to
display login page.
package ch8.acl.demo.controller;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Retrieves login related pages
*/
@Controller
@RequestMapping("/auth")
public class LoginLogoutController {

protected static Logger logger = Logger.getLogger("controller");

/**
* Retrieves the login page
*/
@RequestMapping(value="/main", method = RequestMethod.GET)
public String printWelcome( ) {
return "main_page";

}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String getLoginPage(@RequestParam(value="error",
required=false) boolean error,

ModelMap model) {

logger.debug("Received request to show login page");

// Add an error message to the model if login is unsuccessful


// The 'error' parameter is set to true when authentication fails.
if (error == true) {
model.put("error", "You have entered an invalid username
or password!");
} else {
model.put("error", "");
}
System.out.println("Inisde LoginController");
// This will resolve to /WEB-INF/jsp/loginpage.jsp
return "loginpage";
}

/**
* Retrieves the denied page
*/
@RequestMapping(value = "/denied", method = RequestMethod.GET)
public String getDeniedPage() {
logger.debug("Received request to show denied page");

// This will resolve to /WEB-INF/jsp/deniedpage.jsp


return "deniedpage";
}
}
o BulletinController: This controller is responsible for processing a request to
display bulletin page. Bulletin page is where all of the user's post is displayed.
package ch8.acl.demo.controller;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import ch8.acl.demo.service.GenericService;
import ch8.acl.demo.service.PersonalService;
import ch8.acl.demo.service.PublicService;

/**
* Handles Bulletin related requests
*/
@Controller
@RequestMapping("/bulletin")
public class BulletinController{

protected static Logger logger = Logger.getLogger("controller");

@Resource(name="adminService")
private GenericService adminService;

@Resource(name="personalService")
private GenericService personalService;
@Resource(name="publicService")
private GenericService publicService;

/**
* Retrieves the View page.
* <p>
* This loads all authorized posts.
*/
@RequestMapping(value = "/view", method = RequestMethod.GET)
public String getViewAllPage(Model model) {
logger.debug("Received request to view all page");
System.out.println("Inside Bulleting View
Pages="+adminService.getAll().size());
// Retrieve items from service and add to model
model.addAttribute("adminposts", adminService.getAll());
model.addAttribute("personalposts", personalService.getAll());
model.addAttribute("publicposts", publicService.getAll());

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/bulletinpage.jsp


return "bulletinpage";
}
}
o AdminController: This class is responsible for displaying edit record form, delete
record form,processing add,edit and delte post request and forwarding it to
AdminService class for those users who has ROLE_ADMIN authority.

package ch8.acl.demo.controller;

import java.util.Date;

import org.apache.log4j.Logger;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import ch8.acl.demo.domain.AdminPost;
import ch8.acl.demo.domain.Post;
import ch8.acl.demo.service.GenericService;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.annotation.Resource;

/**
* Handles Admin-related requests
*/
@Controller
@RequestMapping("/admin")
public class AdminController {

protected static Logger logger = Logger.getLogger("controller");

@Resource(name="adminService")
private GenericService adminService;

/**
* Retrieves the Edit page
*/
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEdit(@RequestParam(value="id", required=true) Long id,
Model
model) {
logger.debug("Received request to show edit page");

// Retrieve existing post and add to model


// This is the formBackingOBject
model.addAttribute("postAttribute", adminService.getSingle(id));

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");
// This will resolve to /WEB-INF/jsp/crud-admin/editpage.jsp
return "crud-admin/editpage";
}

/**
* Saves the edited post from the Edit page and returns a result page.
*/
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String getEditPage(@ModelAttribute("postAttribute") AdminPost post,

@RequestParam(value="id", required=true) Long id,


Model model)
{
logger.debug("Received request to view edit page");

// Re-assign id
post.setId(id);
// Assign new date
post.setDate(new Date());

// Delegate to service
if (adminService.edit(post) == true) {
// Add result to model
model.addAttribute("result", "Entry has been edited successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-admin/resultpage.jsp


return "crud-admin/resultpage";
}

/**
* Retrieves the Add page
* <p>
* Access-control is placed here (instead in the service) because we don't want
* to show this page if the client is unauthorized and because the new
* object doesn't have an id. The hasPermission requires an existing id!
*/
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");

// Create new post and add to model


// This is the formBackingOBject
model.addAttribute("postAttribute", new AdminPost());

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");

// This will resolve to /WEB-INF/jsp/crud-admin/addpage.jsp


return "crud-admin/addpage";
}

/**
* Saves a new post from the Add page and returns a result page.
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String getAddPage(@ModelAttribute("postAttribute") AdminPost post,
Model model) {
logger.debug("Received request to view add page");

// Add date today


post.setDate(new Date());

// Delegate to service
if (adminService.add(post)) {
// Success. Add result to model
model.addAttribute("result", "Entry has been added successfully!");
} else {
// Failure. Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-admin/resultpage.jsp


return "crud-admin/resultpage";
}

/**
* Deletes an existing post and returns a result page.
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String getDeletePage(@RequestParam(value="id", required=true) Long
id,
Model
model) {
logger.debug("Received request to view delete page");

// Create new post


Post post = new AdminPost();
// Assign id
post.setId(id);

// Delegate to service
if (adminService.delete(post)) {
// Add result to model
model.addAttribute("result", "Entry has been deleted successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-admin/resultpage.jsp


return "crud-admin/resultpage";
}
}

o PersonalController: This class is responsible for displaying edit record form,


delete record form,processing add,edit and delte post request and forwarding it to
AdminService class for those users who has ROLE_USER authority.

package ch8.acl.demo.controller;

import java.util.Date;

import org.apache.log4j.Logger;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import ch8.acl.demo.domain.PersonalPost;
import ch8.acl.demo.domain.Post;
import ch8.acl.demo.service.GenericService;
import javax.annotation.Resource;

/**
* Handles Personal-related requests
*/
@Controller
@RequestMapping("/personal")
public class PersonalController {

protected static Logger logger = Logger.getLogger("controller");

@Resource(name="personalService")
private GenericService personalService;

/**
* Retrieves the Edit page
*/
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEdit(@RequestParam(value="id", required=true) Long id,
Model
model) {
logger.debug("Received request to show edit page");

// Retrieve existing post and add to model


// This is the formBackingOBject
model.addAttribute("postAttribute", personalService.getSingle(id));

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");

// This will resolve to /WEB-INF/jsp/crud-personal/editpage.jsp


return "crud-personal/editpage";
}

/**
* Saves the edited post from the Edit page and returns a result page.
*/
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String getEditPage(@ModelAttribute("postAttribute") PersonalPost
post,
@RequestParam(value="id", required=true) Long id,
Model model)
{
logger.debug("Received request to view edit page");

// Re-assign id
post.setId(id);
// Assign new date
post.setDate(new Date());

// Delegate to service
if (personalService.edit(post) == true) {
// Add result to model
model.addAttribute("result", "Entry has been edited successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-personal/resultpage.jsp


return "crud-personal/resultpage";
}

/**
* Retrieves the Add page
* <p>
* Access-control is placed here (instead in the service) because we don't want
* to show this page if the client is unauthorized and because the new
* object doesn't have an id. The hasPermission requires an existing id!
*/
@PreAuthorize("hasAuthority('ROLE_USER') and !
hasAuthority('ROLE_ADMIN')")
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");

// Create new post and add to model


// This is the formBackingOBject
model.addAttribute("postAttribute", new PersonalPost());

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");

// This will resolve to /WEB-INF/jsp/crud-personal/addpage.jsp


return "crud-personal/addpage";
}

/**
* Saves a new post from the Add page and returns a result page.
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String getAddPage(@ModelAttribute("postAttribute") PersonalPost
post, Model model) {
logger.debug("Received request to view add page");

// Add date today


post.setDate(new Date());

// Delegate to service
if (personalService.add(post)) {
// Success. Add result to model
model.addAttribute("result", "Entry has been added successfully!");
} else {
// Failure. Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-personal/resultpage.jsp


return "crud-personal/resultpage";
}

/**
* Deletes an existing post and returns a result page.
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String getDeletePage(@RequestParam(value="id", required=true) Long
id,
Model
model) {
logger.debug("Received request to view delete page");

// Create new post


Post post = new PersonalPost();
// Assign id
post.setId(id);

// Delegate to service
if (personalService.delete(post)) {
// Add result to model
model.addAttribute("result", "Entry has been deleted successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());
// This will resolve to /WEB-INF/jsp/crud-personal/resultpage.jsp
return "crud-personal/resultpage";
}
}

o PublicController: This class is responsible for displaying edit record form, delete
record form,processing add,edit and delte post request and forwarding it to
AdminService class for those users who has ROLE_VISITOR authority.

package ch8.acl.demo.controller;

import java.util.Date;

import org.apache.log4j.Logger;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import ch8.acl.demo.domain.Post;
import ch8.acl.demo.domain.PublicPost;
import ch8.acl.demo.service.GenericService;

import javax.annotation.Resource;

/**
* Handles Public-related requests
*/
@Controller
@RequestMapping("/public")
public class PublicController {

protected static Logger logger = Logger.getLogger("controller");

@Resource(name="publicService")
private GenericService publicService;

/**
* Retrieves the Edit page
*/
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEdit(@RequestParam(value="id", required=true) Long id,
Model
model) {
logger.debug("Received request to show edit page");

// Retrieve existing post and add to model


// This is the formBackingOBject
model.addAttribute("postAttribute", publicService.getSingle(id));

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");

// This will resolve to /WEB-INF/jsp/crud-public/editpage.jsp


return "crud-public/editpage";
}

/**
* Saves the edited post from the Edit page and returns a result page.
*/
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String getEditPage(@ModelAttribute("postAttribute") PublicPost post,

@RequestParam(value="id", required=true) Long id,


Model model)
{
logger.debug("Received request to view edit page");

// Re-assign id
post.setId(id);
// Assign new date
post.setDate(new Date());

// Delegate to service
if (publicService.edit(post) == true) {
// Add result to model
model.addAttribute("result", "Entry has been edited successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-public/resultpage.jsp


return "crud-public/resultpage";
}

/**
* Retrieves the Add page
* <p>
* Access-control is placed here (instead in the service) because we don't want
* to show this page if the client is unauthorized and because the new
* object doesn't have an id. The hasPermission requires an existing id!
*/
@PreAuthorize("hasAuthority('ROLE_USER') or
hasAuthority('ROLE_ADMIN')")
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");

// Create new post and add to model


// This is the formBackingOBject
model.addAttribute("postAttribute", new PublicPost());

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");

// This will resolve to /WEB-INF/jsp/crud-public/addpage.jsp


return "crud-public/addpage";
}

/**
* Saves a new post from the Add page and returns a result page.
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String getAddPage(@ModelAttribute("postAttribute") PublicPost post,
Model model) {
logger.debug("Received request to view add page");

// Add date today


post.setDate(new Date());

// Delegate to service
if (publicService.add(post)) {
// Success. Add result to model
model.addAttribute("result", "Entry has been added successfully!");
} else {
// Failure. Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-public/resultpage.jsp


return "crud-public/resultpage";
}

/**
* Deletes an existing post and returns a result page.
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String getDeletePage(@RequestParam(value="id", required=true) Long
id,
Model
model) {
logger.debug("Received request to view delete page");

// Create new post


Post post = new PublicPost();
// Assign id
post.setId(id);

// Delegate to service
if (publicService.delete(post)) {
// Add result to model
model.addAttribute("result", "Entry has been deleted successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}

// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");

// Add our current role and username


model.addAttribute("role",
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("username",
SecurityContextHolder.getContext().getAuthentication().getName());

// This will resolve to /WEB-INF/jsp/crud-public/resultpage.jsp


return "crud-public/resultpage";
}
}
o RoleController: This controller is responsible for displaying test pages stored on
role directory.

package ch8.acl.demo.controller;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Retrieves welcome pages for various roles.
* These pages are secured via intercept-urls. See the security-context.xml file
*/
@Controller
@RequestMapping("/role")
public class RoleController {

protected static Logger logger = Logger.getLogger("controller");

/**
* Retrieves the admin page.
* <p>
* Only clients with ROLE_ADMIN can see this page
*/
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String getAdminPage() {
logger.debug("Received request to show admin page");

// This will resolve to /WEB-INF/jsp/role/adminpage.jsp


return "role/adminpage";
}

/**
* Retrieves the user page.
* <p>
* Only clients with ROLE_ADMIN and ROLE_USER can see this page
*/
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getUserPage() {
logger.debug("Received request to show user page");

// This will resolve to /WEB-INF/jsp/role/userpage.jsp


return "role/userpage";
}

/**
* Retrieves the visitor page.
* <p>
* Only clients with ROLE_ADMIN, ROLE_USER, and ROLE_VISITOR can
see this page
*/
@RequestMapping(value = "/visitor", method = RequestMethod.GET)
public String getVisitorPage() {
logger.debug("Received request to show visitor page");

// This will resolve to /WEB-INF/jsp/role/visitorpage.jsp


return "role/visitorpage";
}
}
6. Inside WebContent/WEB-INF folder we have to create 6 different XML files namely
web.xml,spring-servlet.xml,spring-security.xml,acl-context.xml,applicationContext.xml
and bulletin-context.xml[These files are already described above ].
o Inside web.xml, keep following codes:

<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>SpringACL</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<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>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<servlet>
<description></description>
<display-name>spring</display-name>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</
servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/spring</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/acldemo/*</url-pattern>
</servlet-mapping>

<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
</web-app>
o Inside spring-servlet.xml, keep following codes:

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


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-
beans.xsd">

<!-- Declare a view resolver -->


<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver
"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

</beans>

o Inside spring-security.xml, keep following codes:

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


<beans:beans xmlns="http://www.springframework.org/schema/beans"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-
context.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-
4.1.xsd">
<!-- Loads ACL related configurations -->
<import resource="acl-context.xml" />

<!-- This is where we configure Spring-Security -->


<security:http auto-config="true" use-expressions="true" >
<security:csrf disabled="true"/>
<security:intercept-url pattern="/auth/login"
access="permitAll"/>
<security:intercept-url pattern="/acldemo/bulletin/view"
access="hasRole('ROLE_VISITOR')"/>
<security:intercept-url pattern="/acldemo/role/admin"
access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/acldemo/role/user"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/acldemo/role/visitor"
access="hasRole('ROLE_VISITOR')"/>
<security:form-login login-page="/acldemo/auth/login"
default-target-url="/acldemo/bulletin/view" login-processing-
url="/j_spring_security_check" authentication-failure-
url="/acldemo/auth/login?error=true" username-parameter="username"
password-parameter="password" />
<security:logout logout-success-url="/logout" />

</security:http>

<!-- Declare an authentication-manager to use a custom


userDetailsService -->

<!-- An in-memory list of users. No need to access an external


database layer.
See Spring Security 3.1 Reference 5.2.1 In-Memory
Authentication -->
<!-- mukesh's password: admin
manisha's password: user
muskan's password: visitor -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service>
<security:user name="mukesh"
password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_ADMIN,
ROLE_USER, ROLE_VISITOR" />
<security:user name="manisha"
password="ee11cbb19052e40b07aac0ca060c23ee" authorities="ROLE_USER,
ROLE_VISITOR" />
<security:user name="muskan"
password="127870930d65c57ee65fcc47f2170d38" authorities="ROLE_VISITOR"
/>
</security:user-service>
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!-- Use a Md5 encoder since the user's passwords are stored as
Md5 in the database -->
<bean
class="org.springframework.security.authentication.encoding.Md5PasswordE
ncoder" id="passwordEncoder"/>

</beans:beans>

o Inside acl-context.xml, keep following codes:

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


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-
beans.xsd
http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security-
4.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-
jdbc.xsd">

<!-- Enables method security with expression-based access control


-->
<security:global-method-security pre-post-annotations="enabled">
<!-- Reference to a custom expression handler with ACL
support -->
<security:expression-handler ref="expressionHandler" />
</security:global-method-security>

<!-- A customized expression handler


permissionEvaluator: a reference to a custom
PermissionEvaluator
roleHierarchy: defines the role order -->
<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMeth
odSecurityExpressionHandler"
p:permissionEvaluator-ref="permissionEvaluator"
p:roleHierarchy-ref="roleHierarchy" />

<!-- A customized PermissionEvaluator that evaluates permissions


via the ACL module -->
<bean
class="org.springframework.security.acls.AclPermissionEvaluator"
id="permissionEvaluator">
<!-- Reference to the ACL service which performs JDBC
calls to an ACL database -->
<constructor-arg ref="aclService"/>
</bean>
<!-- A customized ACL service which provides default JDBC
implementation -->
<bean
class="org.springframework.security.acls.jdbc.JdbcMutableAclService"
id="aclService">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="lookupStrategy"/>
<constructor-arg ref="aclCache"/>
</bean>

<!-- A lookup strategy for optimizing database queries -->


<bean id="lookupStrategy"
class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="aclCache"/>
<constructor-arg ref="aclAuthorizationStrategy"/>
<constructor-arg ref="auditLogger"/>
</bean>

<!-- A MySQL datasource with pooling capabalities for the ACL module
-->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://localhost:3306/acl"
p:user="root"
p:password="opmcm6718"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />

<!-- An ACL cache to minimize calls to the ACL database -->


<bean id="aclCache"
class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
<constructor-arg>
<bean
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
</property>
<property name="cacheName" value="aclCache"/>
</bean>
</constructor-arg>
</bean>

<!-- An ACL authorization strategy to determine whether a


principal is permitted to call administrative methods -->
<bean id="aclAuthorizationStrategy"
class="org.springframework.security.acls.domain.AclAuthorizationStrategy
Impl">
<constructor-arg>
<list>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
</list>
</constructor-arg>
</bean>

<!-- An audit logger used to log audit events -->


<bean id="auditLogger"
class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>

<!-- Defines the role order -->


<!--
http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/
org/springframework/security/access/hierarchicalroles/
RoleHierarchyImpl.html -->
<bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarc
hyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
ROLE_USER > ROLE_VISITOR
</value>
</property>
</bean>
</beans>
o Inside applicationContext.xml, keep following codes:

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


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-
beans.xsd
http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-
mvc.xsd">

<!-- Activates various annotations to be detected in bean classes


-->
<context:annotation-config />

<!-- Scans the classpath for annotated components that will be


auto-registered as Spring beans.
For example @Controller and @Service. Make sure to set the
correct base-package-->
<context:component-scan base-package="ch8.acl.demo" />

<!-- Configures the annotation-driven Spring MVC Controller


programming model.
Note that, with Spring 3.0, this tag works in Servlet MVC only!
-->
<mvc:annotation-driven />

<!-- Loads bulletin related configuration-->


<import resource="bulletin-context.xml" />
</beans>
o Inside bulletin-context.xml, keep following codes:

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


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-
beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-
tx.xsd
http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- Enable annotation style of managing transactions -->

<!-- A MySQL datasource with pooling capabilities for the


Bulletin database -->
<bean id="bulletinDataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"

p:driverClass="com.mysql.jdbc.Driver"

p:jdbcUrl="jdbc:mysql://localhost:3306/bulletin"
p:user="root"
p:password="opmcm6718"
p:acquireIncrement="5"

p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" /
</beans>
7. Inside WebContent Folder create a folder namely jsp and place 3 jsp files namely
loginpage.jsp, bulletinpage.jsp and deniedpage.jsp over there.
o loginpage.jsp page is contains source code for displaying login dialog box

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


<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ 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>Login Page</title>
</head>
<body>
<form name='loginForm' action="<c:url
value='../../j_spring_security_check' />" method='POST'>

<table>
<tr>
<td>User:</td>
<td><input type='text' id= 'username' name='username' value=''>
</td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' id= 'password' name='password' />
</td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"
value="submit" />
</td>
<td colspan='2'><input name="reset" type="reset" />
</td>
</tr>

</table>

</form>
</body>
</html>
o bulletinpage.jsp contains source code for displaying a screen once user
successfully logs onto the system
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Bulletin Page</h1>

<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>

<hr/>

<div style="width: 600px" >


<div>
<table style="border: 1px solid; width: 500px; text-
align:center">
<caption>Admin Posts</caption>
<thead style="background:#fcf">
<tr>
<th>Date</th>
<th>Message</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<c:forEach items="${adminposts}" var="post">
<c:url var="editUrl"
value="/krams/admin/edit?id=${post.id}" />
<c:url var="deleteUrl"
value="/krams/admin/delete?id=${post.id}" />
<c:url var="addUrl"
value="/krams/admin/add" />
<tr>
<td><c:out value="${post.date}" /></td>
<td><c:out value="${post.message}"
/></td>
<td><a href="${editUrl}">Edit</a></td>
<td><a href="$
{deleteUrl}">Delete</a></td>
<td><a href="${addUrl}">Add</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<hr/>
</div>

<div style="width: 600px" >


<div>
<table style="border: 1px solid; width: 500px; text-
align:center">
<caption>Personal Posts</caption>
<thead style="background:#fcf">
<tr>
<th>Date</th>
<th>Message</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<c:forEach items="${personalposts}" var="post">
<c:url var="editUrl"
value="/krams/personal/edit?id=${post.id}" />
<c:url var="deleteUrl"
value="/krams/personal/delete?id=${post.id}" />
<c:url var="addUrl"
value="/krams/personal/add" />
<tr>
<td><c:out value="${post.date}" /></td>
<td><c:out value="${post.message}"
/></td>
<td><a href="${editUrl}">Edit</a></td>
<td><a href="$
{deleteUrl}">Delete</a></td>
<td><a href="${addUrl}">Add</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<hr/>
</div>

<div style="width: 600px" >


<div>
<table style="border: 1px solid; width: 500px; text-
align:center">
<caption>Public Posts</caption>
<thead style="background:#fcf">
<tr>
<th>Date</th>
<th>Message</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<c:forEach items="${publicposts}" var="post">
<c:url var="editUrl"
value="/krams/public/edit?id=${post.id}" />
<c:url var="deleteUrl"
value="/krams/public/delete?id=${post.id}" />
<c:url var="addUrl"
value="/krams/public/add" />
<tr>
<td><c:out value="${post.date}" /></td>
<td><c:out value="${post.message}"
/></td>
<td><a href="${editUrl}">Edit</a></td>
<td><a href="$
{deleteUrl}">Delete</a></td>
<td><a href="${addUrl}">Add</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<hr/>
</div>

</body>
</html>
o deniedpage.jsp contains source code for displaying a screen if the username or
password is in-correct.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Access Denied!</h1>
<p>You are not allowed to access this resource!</p>
</body>
</html>

8. Inside jsp folder create four folder namely crud-admin, crud-personal,crud-public and
role
9. Inside crud-admin folder create three jsp files namely addpage.jsp, editpage.jsp and
resultpage.jsp
o addpage.jsp: Represents code for displaying add posts view by user having
ROLE_ADMIN authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Create New ${source} Post</h1>

<c:url var="saveUrl" value="/krams/admin/add" />


<form:form modelAttribute="postAttribute" method="POST" action="$
{saveUrl}">
<table>
<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>

<input type="submit" value="Save" />


</form:form>

</body>
</html>

o editpage.jsp: Represents code for displaying editing posts view by user having
ROLE_ADMIN authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Edit ${source} Post</h1>

<c:url var="saveUrl" value="/krams/admin/edit?id=${postAttribute.id}" />


<form:form modelAttribute="postAttribute" method="POST" action="$
{saveUrl}">
<table>
<tr>
<td><form:label path="id">Id:</form:label></td>
<td><form:input path="id" disabled="true"/></td>
</tr>

<tr>
<td><form:label path="date">Date:</form:label></td>
<td><form:input path="date" disabled="true"/></td>
</tr>

<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>

<input type="submit" value="Save" />


</form:form>

</body>
</html>
o resultpage.jsp: Displays add/edit posts operation status[Successful/Unsuccessful]
by user having ROLE_ADMIN authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Bulletin Page</h1>

<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>

<hr/>

<p>
${source} <br/><br/>
${result}
</p>

</body>
</html>
10. Inside crud-personal folder create three jsp files namely addpage.jsp, editpage.jsp and
resultpage.jsp
a. addpage.jsp: Represents code for displaying add posts view by user having
ROLE_USER authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Create New ${source} Post</h1>

<c:url var="saveUrl" value="/krams/personal/add" />


<form:form modelAttribute="postAttribute" method="POST" action="$
{saveUrl}">
<table>
<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>

<input type="submit" value="Save" />


</form:form>

</body>
</html>
b. editpage.jsp: Represents code for displaying editing posts view by user having
ROLE_USER authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Edit ${source} Post</h1>

<c:url var="saveUrl" value="/krams/personal/edit?id=${postAttribute.id}"


/>
<form:form modelAttribute="postAttribute" method="POST" action="$
{saveUrl}">
<table>
<tr>
<td><form:label path="id">Id:</form:label></td>
<td><form:input path="id" disabled="true"/></td>
</tr>

<tr>
<td><form:label path="date">Date:</form:label></td>
<td><form:input path="date" disabled="true"/></td>
</tr>

<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>

<input type="submit" value="Save" />


</form:form>

</body>
</html>
c. resultpage.jsp: Displays add/edit posts operation status [Successful/Unsuccessful]
by user having ROLE_USER authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Bulletin Page</h1>

<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>

<hr/>

<p>
${source} <br/><br/>
${result}
</p>

</body>
</html>

11. Inside crud-public folder create three jsp files namely addpage.jsp, editpage.jsp and
resultpage.jsp
a. addpage.jsp: Represents code for displaying add posts view by user having
ROLE_VISITOR authority.
<%@ 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>Insert title here</title>
</head>
<body>

</body>
</html>
b. editpage.jsp: Represents code for displaying editing posts view by user having
ROLE_VISITOR authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Edit ${source} Post</h1>


<c:url var="saveUrl" value="/krams/public/edit?id=${postAttribute.id}"
/>
<form:form modelAttribute="postAttribute" method="POST" action="$
{saveUrl}">
<table>
<tr>
<td><form:label path="id">Id:</form:label></td>
<td><form:input path="id" disabled="true"/></td>
</tr>

<tr>
<td><form:label path="date">Date:</form:label></td>
<td><form:input path="date" disabled="true"/></td>
</tr>

<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>

<input type="submit" value="Save" />


</form:form>

</body>
</html>
c. resultpage.jsp: Displays add/edit posts operation status[Successful/Unsuccessful]
by user having ROLE_VISITOR authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Bulletin Page</h1>

<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>

<hr/>

<p>
${source} <br/><br/>
${result}
</p>

</body>
</html>
12. Inside role folder create three jsp files namely adminpage.jsp,userpage.jsp and
visitorpage.jsp [ These are just the test files only ]

Executing:

Type following URL once you execute your program.

http://localhost:8080/SpringACL/acldemo/auth/login

13.

Reference:

http://krams915.blogspot.in/2011/01/spring-security-3-full-acl-tutorial.html

Acegi Security System for Spring

Acegi is a powerful security framework used especially for securing Java Enterprise Application
built using Spring Framework. Spring-based dependency injection makes Acegi easy to
configure and implement.

Acegi provides authentication service through the use of a web filter, an AuthenticationProvider
and an AuthenticationEntryPoint. Authorization in Acegi can be done at different
levels:authorizing web requests, authorizing which methods can be invoked, and authorizing
access to individual domain object instances.

References:

1. http://java-x.blogspot.in/2006/12/spring-security-with-acegi-security.html
2. http://www.javaworld.com/article/2077780/java-web-development/acegi-security-in-one-
hour.html

Thyme leaf Security Tags

Thyme leaf Security Tags are used for following purpose:

 Display data coming from the authentication layer, for example the user's name and roles,
or hide and display part of a web page according to user's authorities.
 The thymeleaf-extras-springsecurity module allows us to do so.
Example:

Setting up social Authentication

Refer to Chapter 8: Authentication through Twitter and Facebook.

Scheduling
Not all applications are driven by user input. Some applications need to execute without any user
intervention probably running at the background at specified intervals. For instance, a Virus
Scanner application will be running in the background once in 2 days. Another instance could be
where a Software could connect to its server repository once in a day for any updates.

Spring provides integration support for JDK Timer and Quartzthat facilitates in writing job
scheduling applications

Quartz Scheduler

Quartz scheduler helps scheduler to run job or task at a specified date and time. Quartz is a Java
open source job-scheduling system capable of scheduling and executing jobs. A job is a Java
class containing the task to be executed. Job is scheduled by trigger which defines when to
execute a Job. Quartz trigger responsibility is to determine when to run Quartz’s job

There are 2 types of triggers in Quartz 2

 SimpleTrigger – Allows to set start time, end time, repeat interval.


 CronTrigger – Allows Unix cron expression to specify the dates and times to run your
job.

Steps for Developing Application

1. Create a Dynamic Web Project named SchedulerDemo and include following 12 jar files
both in Java Build Path and Deployment Assembly.
commons-logging-1.2.jar,log4j-1.2.15.jar,quartz-1.8.6.jar,slf4j-api-1.7.21.jar,
slf4j-log4j12-1.7.21.jar,spring-beans-4.2.2.RELEASE.jar,spring-context-
4.2.2.RELEASE.jar,spring-context-support-3.0.5.RELEASE.jar,spring-core-
4.2.2.RELEASE.jar,spring-expression-4.2.2.RELEASE.jar,spring-tx-
4.2.2.RELEASE.jar,spring-web-4.2.2.RELEASE.jar
2. Creating two packages namely scheduling.jobs and scheduling.tasks
3. Create files namely AnotherTask.java and SomeTask.java inside scheduling tasks
package. These two files represent tasks. Codes inside the method of these classes will be
executed by Quartz Scheduler in fixed interval of time.
 Put Following Codes inside SomeTask.java

package scheduling.tasks;
public class SomeTask {

public void doSomething() {

System.out.println("SomeTask: I just did


something...");
}

}
 Put Following Codes inside AnotherTask.java

package scheduling.tasks;

public class AnotherTask {

public void doSomethingElse() {

System.out.println("Another Task: I just did


something else...");
}
}

4. Create a java file named SampleQuartzJob.java inside scheduling.tasks package and put
following codes inside it.
package scheduling.jobs;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import scheduling.tasks.AnotherTask;
import scheduling.tasks.SomeTask;

public class SampleQuartzJob extends QuartzJobBean {

private SomeTask someTask;


private AnotherTask anotherTask;
public void setSomeTask(SomeTask someTask) {
this.someTask = someTask;
}

public void setAnotherTask(AnotherTask anotherTask) {


this.anotherTask = anotherTask;
}

@Override
protected void executeInternal(JobExecutionContext
jobExecutionContext)
throws JobExecutionException {

DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy


hh:mm:ss");

System.out.println("SampleQuartzJob: Invoked on " +


dateFormat.format(System.currentTimeMillis()));

someTask.doSomething();

anotherTask.doSomethingElse();
}

5. Create index.jsp file inside WebContent directory and put following codes inside it:
<html>
<body>
<h2>Hello Friends!</h2>
<h3>Check out the console for sysouts from the scheduled jobs.</h3>

</body>
</html>

6. Put following codes inside web.xml file


<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>SchedulingDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<display-name>Sample Scheduling Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

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

7. Create an XML file named applicationContext.xml inside WebContent/WEB-INF folder


and keep following codes inside it:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-
3.1.xsd">
<!-- <context:annotation-config/> -->

<bean name="someTask" class="scheduling.tasks.SomeTask" />

<bean name="anotherTask" class="scheduling.tasks.AnotherTask" />

<bean name="sampleQuartzJobBean"
class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="scheduling.jobs.SampleQuartzJob" />

<property name="jobDataAsMap">
<map>
<entry key="someTask" value-ref="someTask" />
<entry key="anotherTask" value-ref="anotherTask" />
</map>
</property>

</bean>
<bean id="sampleQuartzJobCronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="sampleQuartzJobBean" />
<property name="cronExpression" value="0/15 * * * * ?" />
</bean>

<bean
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="sampleQuartzJobBean" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="sampleQuartzJobCronTrigger" />

</list>
</property>
</bean>

</beans>
8. Right Click your project-> Click Run As-> Run On Server to execute your project. You
can see output of your application on the spring console.

Analysis of applicationContext.xml code:

Main part of the code for above application we have kept inside applicationContext.xml. Let us
analyze the code of this file.

Components of Quartz:

Job

In our example above, SomeTask class has method named doSomething() and AnotherTask has
method named doSomethingElse( ). Indeed, the codes that we write inside these methods
represent job i.e. task to be executed in certain interval of time.

We create two beans for above two classes as following:


<bean name="someTask" class="scheduling.tasks.SomeTask" />

<bean name="anotherTask" class="scheduling.tasks.AnotherTask" />

JobDetail

In Spring, We can create quartz job in two different ways:

1. JobDetail Bean: To implement this bean, you have to follow below steps;
i) We have to create a class called SampleQuartzJob inside scheduling.jobs package
and set it as property "jobClass" of JobDetailBean class.
ii) Similalry, We have to set above two jobs as "jobDataAsMap" property

Example:
<bean name="sampleQuartzJobBean"
class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="scheduling.jobs.SampleQuartzJob" />

<property name="jobDataAsMap">
<map>
<entry key="someTask" value-ref="someTask" />
<entry key="anotherTask" value-ref="anotherTask" />
</map>
</property>

</bean>

jobClass refers to a class which extends QuartzJobBean, an implementation of Quartz job


interface. On invocation of this job, it’s executeInternal method gets called.
jobDataMap provides opportunity to pass some data to underlying job bean. In this case, we are
passing a bean ‘anotherBean’ which will be used by SampleQuartzJob.

iii) In SampleQuartzJob class, we should have setter methods for setting job objects
and executeInternal(JobExecutionContext jobExecutionContext) method where
we specify codes that the scheduler wants to repeatedly execute:

Example:
protected void executeInternal(JobExecutionContext jobExecutionContext)
throws JobExecutionException {

DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");

System.out.println("SampleQuartzJob: Invoked on " +


dateFormat.format(System.currentTimeMillis()));

someTask.doSomething();

anotherTask.doSomethingElse();
}

2. MethodInvokingJobDetailFactoryBean:
You can place following codes instead of above to create JobDetail as shown below:

<bean id="sampleQuartzJobBean"

class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBea
n">

<property name="targetObject" ref="someTask" />


<property name="targetMethod" value="doSomething" />

</bean>

Trigger

Following two different types of Trigger are supported by Spring.

1. Simple Trigger: It allows to set start time, end time and repeat interval to run the job.

Example:
<bean id="simpleTriggerBean"
class="org.springframework.scheduling.quartz.SimpleTriggerBean">

<property name="jobDetail" ref="sampleQuartzJobBean1" />


<property name="repeatInterval" value="5000" />
<property name="startDelay" value="1000" />

</bean>
2. Cron Trigger: It allows UNIX cron expression to specify the dates and times to run job.

<bean id="sampleQuartzJobCronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="sampleQuartzJobBean" />
<property name="cronExpression" value="0/15 * * * * ?" />
</bean>

Corn Expression:
Cron-Expressions are used to configure instances of CronTrigger. Cron-Expressions are strings
that are actually made up of seven sub-expressions, that describe individual details of the
schedule. These sub-expression are separated with white-space, and represent:

1. Seconds
2. Minutes
3. Hours
4. Day-of-Month
5. Month
6. Day-of-Week
7. Year (optional field)

An example of a complete cron-expression is the string “0 0 12 ? * WED” - which means “every


Wednesday at 12:00:00 pm”.

CronTrigger Example 1 - an expression to create a trigger that simply fires every 5 minutes

“0 0/5 * * * ?”

CronTrigger Example 2 - an expression to create a trigger that fires every 5 minutes, at 10


seconds after the minute (i.e. 10:00:10 am, 10:05:10 am, etc.).

“10 0/5 * * * ?”

CronTrigger Example 3 - an expression to create a trigger that fires at 10:30, 11:30, 12:30, and
13:30, on every Wednesday and Friday.

“0 30 10-13 ? * WED,FRI”

CronTrigger Example 4 - an expression to create a trigger that fires every half hour between the
hours of 8 am and 10 am on the 5th and 20th of every month. Note that the trigger will NOT fire
at 10:00 am, just at 8:00, 8:30, 9:00 and 9:30

“0 0/30 8-9 5,20 * ?”


Scheduler

SchedulerFactoryBean integrates both Job Detail and Trigger both together as shown below:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>

<ref bean="sampleQuartzJobBean" />


<ref bean="sampleQuartzJobBean1" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="sampleQuartzJobCronTrigger" />
<ref bean="simpleTriggerBean" />

</list>
</property>
</bean>

SchedulerFactoryBean glues together jobDetails and triggers to Configure Quartz Scheduler

JDK Timer

Steps for Creating Application

1. Create a Dynamic Web Project named JDKTimerDemo and add following Jar files both
in java build path and deployment assembly.
2. Create a package named scheduling.tasks and create a class named SomeTask.java inside
it and put following codes inside it:
package scheduling.tasks;

import org.springframework.scheduling.annotation.Scheduled;

public class SomeTask


{

public SomeTask()
{
System.out.println("Executed...");
}
//@Scheduled(cron = "* * 1 * * ?")
@Scheduled(fixedRate=6000)
public void doSomething() {

System.out.println("SomeTask: I just did something...");


}

}
3. web.xml should contain following codes:
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>JDKTimerDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<display-name>Sample Scheduling Web Application</display-name>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
4. appliCationContext.xml should contain following codes:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">

<task:annotation-driven />

<bean id="sampleTask" class="scheduling.tasks.SomeTask" />

<!-- Use following only for pro Spring 3 version


<bean id="schedulerTask"
class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFacto
ryBean">
<property name="targetObject" ref="sampleTask" />
<property name="targetMethod" value="doSomething" />
</bean>
<bean id="timerTask"
class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="timerTask" ref="schedulerTask" />
<property name="delay" value="1000" />
<property name="period" value="6000" />
</bean>
<bean
class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="timerTask" />
</list>
</property>
</bean>
-->

</beans>

5. Create a jsp file named index.jsp inside WebContent folder and put following codes
inside it:
<html>
<body>
<h2>Hello Friends!</h2>
<h3>Check out the console for sysouts from the scheduled
jobs.</h3>

</body>
</html>

Note: Run above application Right Click Project-> Run As-> Run on Server-.
Select Tomcat. doSomething( ) method is executed every 5 second.

Conclusion:

It can be seen that for an application requiring basic scheduling support, it is wise to
choose JDK Timer, however for applications that requires more sophisticated scheduling
support; it is preferable to choose Quartz.

Reference:

1. http://javabeat.net/spring-job-scheduling-support-with-jdk-timer-and-quartz/
2. http://www.boykosissues.com/2012/09/simple-spring-batch-web-app-with-
maven.html
3. http://www.concretepage.com/spring-4/spring-4-quartz-2-scheduler-integration-
annotation-example-using-javaconfig
4. http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-
06.html

Spring Batch
Spring Batch provides many reusable functions adopted from the Spring framework and
customized for batch applications to perform common batch activity( such as split processing of
huge volumes of data, logging, transaction management etc). Spring Batch can work in
conjunction with a Scheduler such as Quartz, however cannot replace a scheduler. Spring Batch
is not a scheduling framework.

Spring Batch Components


Following are the components of Spring Batch:

 Job represents the actual batch process to be executed.


 JobLauncher is responsible for starting a job. When a job is first launched,
JobLauncher verifies it in the JobRepository to determine whether the job is
already executed and determine the validity of the Job parameter before executing
the job.
 JobExecution contains the run time information of job in execution such as status,
startTime, endTime and so on.
 JobParameters are the set of parameters used for a batch job.
 JobRepository provides CRUD operations for the JobLauncher, Job and Step
implementations.
 Step represents sequential phase of a batch job. Step contains the definition and
control information of a batch job. Each step comprises of three activities namely:
data reading, processing and writing, which are taken care of by ItemReader,
ItemProcessor and ItemWriter respectively. Each record is read,
processed(optional) and written to the system.
 ItemReader is the abstract representation of the retrieval operation of Step.
ItemReader reads one item at a time. ItemProcessor is the abstract representation
of the business porcoessing of the item read by ItemReader. ItemProcessor
processes valid items only and returns null if the item is invalid. ItemWriter is the
abstract representation of the output operation of Step. ItemWriter writes one
batch or chunk of items at a time.
Application:

1. Create a Dynamic Web Project named BatchScheduleDemo and include following jars
both in Java Build Path and Deployment Assembly

aopalliance- commons-logging- javax-batch-api- spring-aop- spring-batch-core-


1.0.0.jar 1.2.jar 1.0.1.jar 4.2.2.RELEASE.ja 3.0.7.RELEASE.ja
r r
spring-batch- spring-beans- spring-context- spring-context- spring-core-
infrastructure- 4.2.2.RELEASE.ja 4.2.2.RELEASE.ja support- 4.2.2.RELEASE.ja
3.0.7.RELEASE.ja r r 4.2.2.RELEASE.ja r
r r
spring-expression- spring-retry- spring-tx- spring-web-
4.2.2.RELEASE.ja 1.0.2.RELEASE.ja 4.2.2.RELEASE.ja 4.2.2.RELEASE.ja
r r r r

2. Create three packages namely ch8.batch.demo,ch8.batch.model and ch8.batch.writer


inside src directory.
3. Inside ch8.batch.demo package create two java files namely SimpleTasklet.java and
MyLauncher.java

4. Keep following codes inside SimpleTasklet.java


package ch8.batch.demo;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class SimpleTasklet implements Tasklet {

@Override
public RepeatStatus execute(StepContribution paramStepContribution,
ChunkContext paramChunkContext) throws Exception {
System.out.println("Batch Task Executed...");
return RepeatStatus.FINISHED;
}
}
5. Keep following codes inside MyLauncher.java
package ch8.batch.demo;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;

class MyLauncher {
private JobLauncher jobLauncher;

private Job job;

public void launch() {


JobParameters jobParameters = new
JobParametersBuilder().addLong("time",
System.currentTimeMillis()).toJobParameters();
try {
JobExecution execution = jobLauncher.run(job, jobParameters);
System.out.println("Exitus : " + execution.getStatus());
} catch (Exception e) {
System.out.println("Unablexecute job");
e.printStackTrace();
}
}

public JobLauncher getJobLauncher() {


return jobLauncher;
}

public void setJobLauncher(JobLauncher jobLauncher) {


this.jobLauncher = jobLauncher;
}

public Job getJob() {


return job;
}

public void setJob(Job job) {


this.job = job;
}
}
6. Create a Java class file named Report.java inside ch8.batch.model package and put
following codes inside it:
package ch8.batch.model;

public class Report {

private int id;


private String Impressions;

public int getId() {


return id;
}

public void setId(int id) {


this.id = id;
}

public String getImpressions() {


return Impressions;
}

public void setImpressions(String impressions) {


Impressions = impressions;
}

@Override
public String toString() {
return "Report [id=" + id + ", Impressions=" + Impressions + "]";
}

}
7. Create a java file named CustomWriter.java inside ch8.batch.writer package and keep
following codes inside it:
package ch8.batch.writer;

import java.util.List;
import ch8.batch.model.Report;
import org.springframework.batch.item.ItemWriter;

public class CustomWriter implements ItemWriter<Report> {

@Override
public void write(List<? extends Report> items) throws Exception {

System.out.println("writer..." + items.size());
for(Report item : items){
System.out.println(item);
}

8. Inside WEB-INF folder create a folder named resources and inside resources folder
create an Excel file named report.csv with following information.

9. Inside WEB-INF folder create two xml files namely applicationContext.xml and
jobConfig.xml
10. applicationContext.xml should conisists of following codes:
<?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-3.0.xsd">

<bean id="transactionManager"

class="org.springframework.batch.support.transaction.ResourcelessTransactionMa
nager" />

<bean id="jobRepository"

class="org.springframework.batch.core.repository.support.MapJobRepositoryFacto
ryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>

<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
</beans>

11. jobConfig.xml should consists of following codes:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch.xsd">
<context:component-scan base-package="com.mkyong" />
<import resource="applicationContext.xml" />

<bean id="mytasklet" class="ch8.batch.demo.SimpleTasklet" />


<bean id="report" class="ch8.batch.model.Report" scope="prototype" />

<bean id="customWriter" class="ch8.batch.writer.CustomWriter" />


<bean id="cvsFileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader">

<!-- Read a csv file -->


<property name="resource" value="/WEB-INF/resources/report.csv"
/>
<property name="lineMapper">
<bean
class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean

class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names"
value="id,impressions" />
</bean>
</property>
<property name="fieldSetMapper">
<bean

class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName"
value="report" />
</bean>
</property>
</bean>
</property>

</bean>
<batch:job id="reportJob">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="cvsFileItemReader" writer="customWriter"
commit-interval="10">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>

<batch:job id="simpleJob">
<batch:step id="simpleTaskletStep">
<batch:tasklet ref="mytasklet" />
</batch:step>
</batch:job>

<bean id="myLauncher" class="ch8.batch.demo.MyLauncher">


<property name="jobLauncher" ref="jobLauncher"></property>
<property name="job" ref="simpleJob"></property>
</bean>
<bean id="myLauncher1" class="ch8.batch.demo.MyLauncher">
<property name="jobLauncher" ref="jobLauncher"></property>
<property name="job" ref="reportJob"></property>
</bean>

<task:scheduled-tasks>
<task:scheduled ref="myLauncher1" method="launch" fixed-delay="6000" />
<task:scheduled ref="myLauncher" method="launch" cron="*/5 * * * * *" />
</task:scheduled-tasks>
</beans>
12. Your web.xml should consists of following codes:
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>BatchScheduleDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/jobConfig.xml</param-value>
</context-param>

<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
</web-app>
13. Inside, WebContent folder create a jsp file named index.jsp and keep following codes
inside it:
<%@ 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>Insert title here</title>
</head>
<body>
<h1>Batch Demo Program</h1>
</body>
</html>

Analysis of Above Program:

 In above program, we have created two batch jobs namely simpleJob and reportJob
 Simple Job we have implemented inside SimpleTasklet class execute method, which
simply prints "Batch Task Executed" message. On the other hand, "reportJob" reads
items from report.csv file and prints it on the console. Printing items on the console is
done by CustomWriter class and excel field name is mapped to java POJO by Report
class. These two classes are for reportJob whereas SimpleTasklet is for simpleJob.
MyLauncher is required by both jobs namely "reportJob" and "simpleJob".
 For each job, we should have separate launcher as shown below:

In above example, simpleJob is executed by myLauncher and reportJob is executed by


myLauncher1
 Each job is scheduled separately as shown below:

Note:

 In above example, myLauncher is scheduled to be executed on every 6 second whereas


myLauncher is scheduled to be executed on every 5 second.
 Withing FlatFileItemReader, you have to set two properties namely
resource[ excel file from where you are going to read] and linemapper
o Within linemapper property, you have to define a bean called
DefaultLineMapper.
o Within DefaultLineMapper bean, you have to set two properties
namely lineTokenizer and fieldSetMapper
 Within lineTokenizer property, you have to set a bean called
DelimitedLineTokenizer
 You have to set names propert of
DelimitedLineTokenizer bean as well
 Within fieldSetMapper property, you have to define a bean
named BeanWrappedFieldSetMapper
 You have to set name property of
BeanWrappedFieldSetMapper.

TaskScheduler
TaskScheduler helps to schedule a job. Below we are going to develop a sample application that
uses TaskScheduler.

1. Create a Dynamic Web Project named TaskSchedulingDemo and include following jar
files in both Java Build Path Delployment Assembly

aopalliance-1.0.0.jar commons-logging-1.2.jar spring-aop- spring-beans-


4.2.2.RELEASE.jar 4.2.2.RELEASE.jar
spring-context- spring-context- spring-core- spring-expression-
4.2.2.RELEASE.jar support4.2.2.RELEASE.jar 4.2.2.RELEASE.jar 4.2.2.RELEASE.jar
spring-web-
4.2.2.RELEASE.jar

2. Crate a package named ch8.demo.scheduling inside src folder and create a class called
TestBean and keep following codes inside it: testMethod ( ) contains code which is going
to get invoked periodically by the scheduler.
package ch8.demo.scheduling;

import org.springframework.stereotype.Component;

@Component("myBean")
public class TestBean {

public void testMethod()


{
System.out.println("I am called by Spring Task
Scheduler");
}
}

3. Create an XML file named appConfig.xml inside WEB-INF folder and keep following
codes inside it:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd

http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd">

<context:component-scan base-package="ch8.demo.scheduling" />

<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="myBean" method="testMethod" fixed-
delay="5000" />
</task:scheduled-tasks>

<task:scheduler id="myScheduler" pool-size="10" />

</beans>
4. Create following codes inside web.xml file:
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>TaskSchedulingDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/appConfig.xml</param-value>
</context-param>

<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
5. Create a file named index.jsp inside WebContent folder and keep following codes inside
it:
<%@ 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>Insert title here</title>
</head>
<body>
<h1> Task Scheduling Demo</h1>
</body>
</html>

Analysis of Above Program:

Following codes configure a scheduler which schedules some tasks to run at some point in the
future.

<task:scheduler id="myScheduler" pool-size="10" />

Next, we configure the tasks to be scheduled.

<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="myBean" method="testMethod" fixed-delay="5000" />
</task:scheduled-tasks>

task:scheduled defines a new task. Attribute ‘ref’ refers to the bean whose method (referred by
attribute ‘method’) will be called on specific time condition. fixed-delay specifies the time in
milliseconds between the completion of first task and start of next task.
Here we are saying that myBean.printMessage() will be by scheduler periodically, with 5
seconds delay between completion of first task and start of next task.

Periodicity of scheduler can be defined in other ways as well. For example:


<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="myBean" method="printMessage" fixed-delay="5000"
initial-delay="1000"/>
<task:scheduled ref="Bean2" method="method2" fixed-rate="5000" />
<task:scheduled ref="Bean3" method="method3" cron="*/5 * * * * MON-
FRI"/>
</task:scheduled-tasks>
In above config, we have specified there scheduled tasks with different periodicity.

initial-delay parameter specifies the number of milliseconds to wait before the first execution of
the method.
fixed-rate specifies the number of milliseconds between each method start , regardless of how
long method takes to complete.
cron provides more fine-grained control on task execution. Here we have configured task3 to run
every 5 seconds but only on weekdays.

Reference:

1. http://websystique.com/spring/spring-job-scheduling-using-xml-configuration/

You might also like