Skip to content

Commit 147853a

Browse files
author
Manish Baxi
committed
Added application code.
0 parents  commit 147853a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2167
-0
lines changed

.gitignore

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.classpath
2+
.project
3+
.settings/
4+
target/
5+
api/.classpath
6+
api/.project
7+
api/.settings/
8+
api/target/
9+
common/.classpath
10+
common/.project
11+
common/.settings/
12+
common/target/
13+
data/.classpath
14+
data/.project
15+
data/.settings/
16+
data/target/
17+
domain/.classpath
18+
domain/.project
19+
domain/.settings/
20+
domain/src/main/webapp/WEB-INF/classes/
21+
domain/target/
22+
service/.classpath
23+
service/.project
24+
service/.settings/
25+
service/target/
26+
transfer/.classpath
27+
transfer/.project
28+
transfer/.settings/
29+
transfer/target/
30+
web/.classpath
31+
web/.project
32+
web/.settings/
33+
web/src/main/webapp/WEB-INF/classes/
34+
web/target/
35+
*~
36+
*.tmp

README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# 1. Introduction
2+
Nowadays many applications expose business functionality as REST services. These services
3+
can be consumed not only within the enterprise where they were created but from outside as
4+
well because REST is platform-agnostic and almost every development platform currently
5+
provides some way of consuming REST services. When creating REST services it is common to
6+
incorporate some sort of security for every REST API endpoint. This ensures that each API
7+
endpoint is only invoked by clients that are authorised and intended to use that endpoint.
8+
This prevents information leakage and malicious use of the application by internal and
9+
external users.
10+
11+
Spring Security is a flexible framework for implementing security requirements for web
12+
based applications. It is therefore not uncommon for developers to use Spring Security to
13+
enforce security restrictions on REST API endpoints. One of the challenges encountered
14+
when using Spring Security to secure REST API endpoints stems from the requirement to
15+
support stateless REST clients. In this context, *stateless* means that the client does
16+
not provide any information with REST requests that would allow the server to determine the
17+
identity of the user associated with the client. This is a challenge because Spring Security
18+
(and any other security framework) requires some sort of state information in order to
19+
authenticate and authorize application users. In the absence of an in-built state
20+
transmission mechanism, a custom mechanism needs to be evolved to support stateless REST
21+
and similar scenarios.
22+
23+
# 2. Overview
24+
This application uses the following architecture to demonstrate how the default Spring
25+
Security configuration can be used to secure a standard web application and another
26+
configuration to secure a stateless REST API.
27+
28+
=================== ===================
29+
| | | |
30+
| W e b L a y e r | | A P I L a y e r |
31+
| | | |
32+
=================== ===================
33+
34+
=============================================
35+
| |
36+
| B u s i n e s s L o g i c L a y e r |
37+
| |
38+
=============================================
39+
40+
The *Business Logic* layer is responsible for performing all business operations. It is
41+
secured using Spring Security annotations. The *Web* and *API* layers call the *Business
42+
Logic* layer as and when required. This means, both these layers need to authenticate
43+
users correctly and consistently so that users can use either of these layers to get access
44+
to the data and business functionality provided by the *Business Logic* layer.
45+
46+
# 3. Design
47+
The *Web* layer uses the default Spring Security configuration and takes advantage of form
48+
login capability provided by Spring Security. The following configuration for the *Web*
49+
layer enables this configuration.
50+
51+
<security:http access-denied-page="/" auto-config="true" use-expressions="true">
52+
<security:form-login />
53+
</security:http>
54+
55+
With this configuration, Spring Security uses HTTP sessions to store user credentials in
56+
between requests.
57+
58+
The *API* layer cannot use the default configuration because all communication between this
59+
layer and its clients has been assumed to be stateless. Spring Security configuration for
60+
this layer is therefore slightly more involved and is shown below.
61+
62+
<bean class="org.example.api.security.APIAuthenticationEntryPoint" id="apiAuthenticationEntryPoint" />
63+
<bean class="org.example.api.security.EhcacheSecurityContextRepository" id="apiSecurityContextRepository" />
64+
<security:authentication-manager alias="authenticationManager" erase-credentials="false">
65+
<security:authentication-provider ref="authenticationProvider" />
66+
</security:authentication-manager>
67+
<security:http auto-config="true" create-session="stateless" entry-point-ref="apiAuthenticationEntryPoint" security-context-repository-ref="apiSecurityContextRepository" use-expressions="true" />
68+
69+
The class `APIAuthenticationEntryPoint` simply rejects all requests that are unauthenticated
70+
but were expected to be. This ensures that unauthenticated users cannot invoke API endpoints.
71+
The class `EhcacheSecurityContextRepository` leverages the modular architecture of Spring
72+
Security to store authentication information in an expirable Ehcache. It implements the
73+
Spring Security interface `SecurityContextRepository`, whose other implementation
74+
`HttpSessionSecurityContextRepository` is the default implementation used by Spring Security.
75+
Of course, it is not mandatory to use Ehcache. Any other caching solution could be used to
76+
store user credentials in between REST calls.
77+
78+
#4. Running the application
79+
The following pre-requisites apply to this application.
80+
81+
1. Java Development Kit (JDK) 6.0 or higher;
82+
1. Apache Maven 3.0.4 or higher.
83+
84+
Once these have been installed and the code checked out, the `web` application can be run
85+
as `mvn clean package tomcat7:run -pl common,data,domain,service,transfer,web`. This starts an
86+
embedded Tomcat instance on local port `8888`. The application can then be accessed using
87+
any web browser on [http://localhost:8888](http://localhost:8888). When accessed for the first
88+
time, the application will present a login screen with instructions on logging in.
89+
Successfully logging in as an *Admin* user provides access to a list of users for the system.
90+
This functionality is not accessible to normal users (try accessing it as a normal user).
91+
92+
Similarly, the `api` application can be run as
93+
`mvn clean package tomcat7:run -pl api,common,data,domain,service,transfer`. This starts
94+
an embedded Tomcat instance on local port `9999`. The application can then be accessed
95+
using a REST client, such as the *Postman* extension for Google Chrome on
96+
`http://localhost:9999`. There are two REST endpoints - `http://localhost:9999/authenticate`
97+
to authenticate clients and `http://localhost:9999/users` to access the user list.
98+
First, make a *POST* request to `http://localhost:9999/authenticate` with two form parameters
99+
`username` (same as the one used for the web application) and `password` (set to *password*
100+
for all users). Note the text token returned by this call. Then, make a *GET* request to
101+
`http://localhost:9999/users`, including the value of the text token retrieved on the
102+
previous call as an HTTP header `X-API-TOKEN`. This should return the list of users similar
103+
to how it was displayed for the web application.
104+
105+
# 5. License
106+
This sample application and its associated source code in its entirety is being made
107+
available under the following licensing terms.
108+
109+
Copyright (C) 2014
110+
111+
Permission is hereby granted, free of charge, to any person obtaining a copy of
112+
this software and associated documentation files (the "Software"), to deal in the
113+
Software without restriction, including without limitation the rights to use, copy,
114+
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
115+
and to permit persons to whom the Software is furnished to do so, subject to the
116+
following conditions:
117+
118+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
119+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
120+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
121+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
122+
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
123+
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

api/pom.xml

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<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">
2+
<modelVersion>4.0.0</modelVersion>
3+
<parent>
4+
<groupId>org.example</groupId>
5+
<artifactId>spring-security-rest</artifactId>
6+
<version>1.0.0.0</version>
7+
<relativePath>../pom.xml</relativePath>
8+
</parent>
9+
<artifactId>spring-security-rest-api</artifactId>
10+
<packaging>war</packaging>
11+
<build>
12+
<finalName>spring-security-rest-api</finalName>
13+
<plugins>
14+
<plugin>
15+
<groupId>org.apache.tomcat.maven</groupId>
16+
<artifactId>tomcat7-maven-plugin</artifactId>
17+
<configuration>
18+
<port>9999</port>
19+
</configuration>
20+
</plugin>
21+
</plugins>
22+
</build>
23+
<dependencies>
24+
<dependency>
25+
<groupId>${project.groupId}</groupId>
26+
<artifactId>spring-security-rest-common</artifactId>
27+
<version>${project.version}</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>${project.groupId}</groupId>
31+
<artifactId>spring-security-rest-data</artifactId>
32+
<version>${project.version}</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>${project.groupId}</groupId>
36+
<artifactId>spring-security-rest-domain</artifactId>
37+
<version>${project.version}</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>${project.groupId}</groupId>
41+
<artifactId>spring-security-rest-service</artifactId>
42+
<version>${project.version}</version>
43+
</dependency>
44+
<dependency>
45+
<groupId>${project.groupId}</groupId>
46+
<artifactId>spring-security-rest-transfer</artifactId>
47+
<version>${project.version}</version>
48+
</dependency>
49+
<dependency>
50+
<groupId>com.fasterxml.jackson.core</groupId>
51+
<artifactId>jackson-core</artifactId>
52+
</dependency>
53+
<dependency>
54+
<groupId>com.fasterxml.jackson.core</groupId>
55+
<artifactId>jackson-databind</artifactId>
56+
</dependency>
57+
<dependency>
58+
<groupId>commons-logging</groupId>
59+
<artifactId>commons-logging</artifactId>
60+
</dependency>
61+
<dependency>
62+
<groupId>javax.annotation</groupId>
63+
<artifactId>jsr250-api</artifactId>
64+
</dependency>
65+
<dependency>
66+
<groupId>javax.servlet</groupId>
67+
<artifactId>servlet-api</artifactId>
68+
</dependency>
69+
<dependency>
70+
<groupId>javax.transaction</groupId>
71+
<artifactId>javax.transaction-api</artifactId>
72+
</dependency>
73+
<dependency>
74+
<groupId>net.sf.ehcache</groupId>
75+
<artifactId>ehcache-core</artifactId>
76+
</dependency>
77+
<dependency>
78+
<groupId>org.mockito</groupId>
79+
<artifactId>mockito-core</artifactId>
80+
</dependency>
81+
<dependency>
82+
<groupId>org.springframework</groupId>
83+
<artifactId>spring-webmvc</artifactId>
84+
</dependency>
85+
<dependency>
86+
<groupId>org.springframework.security</groupId>
87+
<artifactId>spring-security-config</artifactId>
88+
</dependency>
89+
<dependency>
90+
<groupId>org.springframework.security</groupId>
91+
<artifactId>spring-security-web</artifactId>
92+
</dependency>
93+
</dependencies>
94+
</project>

api/src/main/resources/ehcache.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
3+
<diskStore path="${cache.path}" />
4+
<defaultCache diskExpiryThreadIntervalSeconds="300" eternal="false" maxElementsInMemory="100" maxElementsOnDisk="10000000"
5+
memoryStoreEvictionPolicy="LRU" timeToIdleSeconds="300" timeToLiveSeconds="300" overflowToDisk="true" />
6+
<cache eternal="false" maxElementsInMemory="1000" name="SSSC" overflowToDisk="false" timeToLiveSeconds="1800" />
7+
</ehcache>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="ISO-8859-1"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
3+
xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:security="http://www.springframework.org/schema/security"
4+
xmlns:util="http://www.springframework.org/schema/util"
5+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
6+
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
7+
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
8+
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
9+
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
10+
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
11+
<import resource="classpath*:springSecurityContext.xml" />
12+
<aop:aspectj-autoproxy proxy-target-class="true" />
13+
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
14+
<property name="mediaTypes">
15+
<map>
16+
<entry key="json" value="application/json" />
17+
</map>
18+
</property>
19+
</bean>
20+
<context:annotation-config />
21+
<context:component-scan base-package="org.example.api" />
22+
<mvc:annotation-driven>
23+
<mvc:message-converters>
24+
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
25+
<property name="prettyPrint" value="true" />
26+
<property name="supportedMediaTypes" value="application/json" />
27+
</bean>
28+
</mvc:message-converters>
29+
</mvc:annotation-driven>
30+
</beans>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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"
3+
xmlns:security="http://www.springframework.org/schema/security" xmlns:util="http://www.springframework.org/schema/util"
4+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
5+
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
6+
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
7+
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
8+
<bean class="org.example.api.security.APIAuthenticationEntryPoint" id="apiAuthenticationEntryPoint" />
9+
<bean class="org.example.api.security.EhcacheSecurityContextRepository" id="apiSecurityContextRepository" />
10+
<bean class="org.example.api.security.UsernamePasswordAuthenticationProvider" id="authenticationProvider" />
11+
<security:authentication-manager alias="authenticationManager" erase-credentials="false">
12+
<security:authentication-provider ref="authenticationProvider" />
13+
</security:authentication-manager>
14+
<security:http auto-config="true" create-session="stateless" entry-point-ref="apiAuthenticationEntryPoint" security-context-repository-ref="apiSecurityContextRepository"
15+
use-expressions="true">
16+
<security:anonymous enabled="true" granted-authority="ADMIN" />
17+
</security:http>
18+
</beans>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.example.api
2+
3+
import org.example.service.UserService
4+
import org.example.transfer.UserListingRequest
5+
import org.springframework.beans.factory.annotation.Autowired
6+
import org.springframework.web.bind.annotation.RequestMapping
7+
import org.springframework.web.bind.annotation.RestController
8+
9+
/**
10+
* User Administration controller.
11+
*/
12+
@RestController
13+
class UserAdministrationController {
14+
@Autowired
15+
var userService: UserService = _
16+
17+
/**
18+
* Authenticates an API user using a username and a password.
19+
*/
20+
@RequestMapping(Array("/users"))
21+
def show = this.userService.getAll(new UserListingRequest)
22+
}

0 commit comments

Comments
 (0)