Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Securing Restful Web Services with Spring Security and OAuth2 (Spring Security + OAuth2 + Spring Rest)

OAuth (Open Authentication) is an open standard or kind of protocol that lets one site to share its content with some other site without sharing credentials.

The source code of this application can be found on Git Hub: Source Code

In this post we will discuss how to secure Restful Web Services using Spring security and OAuth2, we will use Spring Security to validate a user on server and OAuth to manage authentication tokens to be used in communication. After applying this implementation only authenticated users and applications will get a valid access token from OAuth and using that token the user can access authorized API’s on server.


The flow of application will go something like this:

1) User sends a GET request to server with five parameters: grant_type, username, password, client_id, client_secret; something like this:
http://localhost:8080/SpringRestSecurityOauth/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=beingjavaguys&password=spring@java


2) Server validates the user with help of spring security, and if the user is authenticated, OAuth generates a access token and send sends back to user in following format.


{
"access_token": "22cb0d50-5bb9-463d-8c4a-8ddd680f553f",
"token_type": "bearer",
"refresh_token": "7ac7940a-d29d-4a4c-9a47-25a2167c8c49",
"expires_in": 119
}
Here we got access_token for further communication with server or to get some protected resourses(API’s), it mentioned a expires_in time that indicates the validation time of the token and a refresh_token that is being used to get a new token when token is expired.


3) We access protected resources by passing this access token as a parameter, the request goes something like this:
http://localhost:8080/SpringRestSecurityOauth/api/users/?access_token=8c191a0f-ebe8-42cb-bc18-8e80f2c4238e

Here http://localhost:8080/SpringRestSecurityOauth is the server path, and /api/users/Is an API URL that returns a list of users and is being protected to be accessed.


4) If the token is not expired and is a valid token, the requested resources will be returned.

5) In case the token is expired, user needs to get a new token using its refreshing token that was accepted in step(2). A new access token request after expiration looks something like this:
http://localhost:8080/SpringRestSecurityOauth/oauth/token?grant_type=refresh_token&client_id=restapp&client_secret=restapp&refresh_token=7ac7940a-d29d-4a4c-9a47-25a2167c8c49
And you will get a new access token along with a new refresh token.


Integrating Spring Security with OAuth2 to secure Restful Web Services.

In this section we will see how the application works and how to integrate Spring Security with OAuth2, so for now we have a simple Spring Rest structure that returns a list of users for authenticated user, we have a simple spring controller that’s taking the responsibility:

More on @ResponseBody : Here

\src\main\java\com\beingjavaguys\controllers\RestController.java

package com.beingjavaguys.controllers;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.beingjavaguys.models.User;
import com.beingjavaguys.services.DataService;

/**
* @author Nagesh.Chauhan
*
*/
@Controller
@RequestMapping("/api/users")
public class RestController {

@Autowired
DataService dataService;

@RequestMapping(value = "/", method = RequestMethod.GET)
@ResponseBody
public List list() {
return dataService.getUserList();

}
}

The code is self explainer, we are having an URI (/api/users/) to be accessed by a GET request and it will return a list of users as a response.


We are using simple service structure to get data, for now the data is being returned in a harcoded way but it can be kind of dynamic of from database.

More on Spring Rest Web Services : Here

\src\main\java\com\beingjavaguys\services\DataService.java

package com.beingjavaguys.services;

import java.util.List;

import com.beingjavaguys.models.User;
/**
* @author Nagesh.Chauhan
*
*/
public interface DataService {
public List getUserList();
}


\src\main\java\com\beingjavaguys\services\DataServiceImpl.javaWe have created three users and added them to a list, this list will be accessed to authenticated users only with the help of auhentication OAuth token.

package com.beingjavaguys.services;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import com.beingjavaguys.models.User;
/**
* @author Nagesh.Chauhan
*
*/
@Service
public class DataServiceImpl implements DataService {

@Override
public List getUserList() {

// preparing user list with few hard coded values
List userList = new ArrayList();

userList.add(new User(1, "user_a", "[email protected]", "9898989898"));
userList.add(new User(2, "user_b", "[email protected]", "9767989898"));
userList.add(new User(3, "user_c", "[email protected]", "9898459898"));

return userList;
}

}



Dependencies required to integrate Spring MVC , Spring Security and OAuth2


<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.beingjavaguys.sample</groupId>
<artifactId>SpringRestSecurityOauth</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringRestSecurityOauth Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- @author Nagesh.Chauhan([email protected]) -->
<properties>
<spring.version>4.0.7.RELEASE</spring.version>
<log4j.version>1.2.17</log4j.version>
<jdk.version>1.7</jdk.version>
<context.path>SpringRestSecurityOauth</context.path>
<spring.security.version>3.2.5.RELEASE</spring.security.version>
</properties>
<build>
<finalName>${pom.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>

<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>


<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.10</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>



Spring security OAuth configuration

Now this is where the actual thing is happening, we need to configure a number of thing in this security configuration file:
\src\main\webapp\WEB-INF\spring-security.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">

<!-- @author Nagesh.Chauhan([email protected]) -->
<!-- This is default url to get a token from OAuth -->
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request
parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>

<!-- This is where we tells spring security what URL should be protected
and what roles have access to them -->
<http pattern="/api/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/api/**" access="ROLE_APP" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>


<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test" />
</bean>

<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test/client" />
<property name="typeName" value="Basic" />
</bean>

<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />

<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"
xmlns="http://www.springframework.org/schema/beans">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>

<authentication-manager id="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>


<!-- This is simple authentication manager, with a hardcoded user/password
combination. We can replace this with a user defined service to get few users
credentials from DB -->
<authentication-manager alias="authenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider>
<user-service>
<user name="beingjavaguys" password="spring@java" authorities="ROLE_APP" />
</user-service>
</authentication-provider>
</authentication-manager>

<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>


<!-- This defined token store, we have used inmemory tokenstore for now
but this can be changed to a user defined one -->
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />

<!-- This is where we defined token based configurations, token validity
and other things -->
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="accessTokenValiditySeconds" value="120" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>

<bean id="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
<property name="tokenServices" ref="tokenServices" />
</bean>

<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices"
user-approval-handler-ref="userApprovalHandler">
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>

<oauth:resource-server id="resourceServerFilter"
resource-id="test" token-services-ref="tokenServices" />

<oauth:client-details-service id="clientDetails">
<!-- client -->
<oauth:client client-id="restapp"
authorized-grant-types="authorization_code,client_credentials"
authorities="ROLE_APP" scope="read,write,trust" secret="secret" />

<oauth:client client-id="restapp"
authorized-grant-types="password,authorization_code,refresh_token,implicit"
secret="restapp" authorities="ROLE_APP" />

</oauth:client-details-service>

<sec:global-method-security
pre-post-annotations="enabled" proxy-target-class="true">
<!--you could also wire in the expression handler up at the layer of the
http filters. See https://jira.springsource.org/browse/SEC-1452 -->
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>

<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>



Spring configuration file

This is nothing more that a very simple spring configuration file, it just loads Spring web app context in the container, we can configure spring related thing here:
\src\main\webapp\WEB-INF\mvc-dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- @author Nagesh.Chauhan([email protected]) -->
<context:component-scan base-package="com.beingjavaguys" />
<mvc:annotation-driven />
</beans>


Deployment descriptor

This is simple web.xml file that lets the container know that all upcoming requests will be handled by spring itself, we have passed location of configuration files and added a spring security filter over here. This filter is intercepting all requests to be authenticated by security context. \src\main\webapp\WEB-INF\web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

<display-name>Sample Spring Maven Project</display-name>
<!-- @author Nagesh.Chauhan([email protected]) -->

<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/mvc-dispatcher-servlet.xml,
/WEB-INF/spring-security.xml
</param-value>
</context-param>

<!-- Spring Security -->

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

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>


Domain classes

Here is a single domain class, whose objects are going to be returned in json format:
\src\main\java\com\beingjavaguys\models\User.java

package com.beingjavaguys.models;
/**
* @author Nagesh.Chauhan
*
*/
public class User {
private int id;
private String name;
private String email;
private String phone;

public User() {
super();
// TODO Auto-generated constructor stub
}

public User(int id, String name, String email, String phone) {
super();
this.id = id;
this.name = name;
this.email = email;
this.phone = phone;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

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

public String getEmail() {
return email;
}

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

public String getPhone() {
return phone;
}

public void setPhone(String phone) {
this.phone = phone;
}

}

This is all about, securing restful web services with spring security and oauth2, in this article we look into how to configure spring security with oauth2 to use a token based authentication mechanism. In upcoming articles we will see more about security in java EE applications.

Download complete Project : Git Hub









Thanks for reading !
Being Java Guys Team

Get Spring Security and OAuth2 Integration Source Code from Git Hub




RELATED POSTS

Spring Security Authentication and Authorization Example with Database Credentials


Spring 4 Restful Web Services With Hibernate 4


Spring Security with Hibernate using Maven - Authentication and Authorization Example


Spring MVC - JSON response with @ResponseBody annotation (Spring + JSON)


Integrate Neo4j (Graph DB) with Java – Neo4J with Java (Neo4J + Java)





This post first appeared on Java, Struts 2, Spring, Hibernate, Solr, Mahout An, please read the originial post: here

Share the post

Securing Restful Web Services with Spring Security and OAuth2 (Spring Security + OAuth2 + Spring Rest)

×

Subscribe to Java, Struts 2, Spring, Hibernate, Solr, Mahout An

Get updates delivered right to your inbox!

Thank you for your subscription

×