Let’s secure our Spring REST API
using OAuth2
this time, a simple guide showing what is required to secure a REST API using Spring OAuth2
. Our use-case fits well with Resource-owner Password Grant
flow of OAUth2 specification. We will use two different clients [Postman and a Spring RestTemplate
based java application] to access our OAuth2 protected REST resources.
If you are already familiar with OAuth2 concepts, you may want to skip the theory, and jump right into code. As always, complete code can be found in attachment at the end of this article. Let’s get started.
What is OAuth2
OAuth2 is an standardized authorization protocol/framework. As per Official OAuth2 Specification:
The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf.
Big players like Google, Facebook and others are already using their own OAuth2 implementations for quite some time. Enterprises too are moving fast towards OAuth2 adoption.
I found OAuth2 specification rather simple to follow. Yet if you want to start even quickly, an excellent article on OAuth2 fundamentals can be found here which gives a deep insight in OAUth2 theoretical concepts.
Spring Security OAuth project provides all the necessary API we might need in order to develop an OAuth2 compliant implementation using Spring. Official Spring security oauth project provides a comprehensive example for implementing OAuth2. The code samples of this post is inspired by that examples itself. The intention of this post is to just use bare-minimum functionality required in order to secure our REST API, nothing more.
At minimum, you should be aware of four key concepts in OAuth2:
1. OAuth2 Roles
OAuth2 defines four roles:
-
resource owner
:
Could be you. An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user. -
resource server
:
The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens. -
client
:
An application making protected resource requests on behalf of the resource owner and with its authorization. It could be a mobile app asking your permission to access your Facebook feeds, a REST client trying to access REST API, a web site [Stackoverflow e.g.] providing an alternative login option using Facebook account. -
authorization server
:
The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.
In our example, our REST API can only be accessed via Resource server which will require an access token to be present with request
2. OAuth2 Authorization Grant types
An authorization grant is a credential representing the resource owner’s authorization (to access its protected resources) used by the client to obtain an access token. The specification defines four grant types:
-
authorization code
-
implicit
-
resource owner password credentials
-
client credentials
We will be using resource owner password credentials
grant type. The reason is simple, we are not implementing a view which redirects us to a login page. Only the usage where a client [Postman or RestTemplate based Java client e.g.] have the Resource owner’s credentials and they provide those credential [along with client credentials] to authorization server in order to eventually receive the access-token[and optionally refresh token], and then use that token to actually access the resources.
A common example is the GMail app
[a client] on your smartphone which takes your credentials and use them to connect to GMail servers
. It also shows that ‘Password Credentials Grant’ is best suited when both the client and the servers are from same company as the trust is there, you don’t want to provide your credentials to a third party.
3. OAuth2 Tokens
Tokens are implementation specific random strings, generated by the authorization server and are issued when the client requests them.
-
Access Token
: Sent with each request, usually valid for a very short life time [an hour e.g.] -
Refresh Token
: Mainly used to get a new access token, not sent with each request, usually lives longer than access token.
HTTPS
is a must have. Without HTTPS, no matter what your implementation is, security is vulnerable to be compromised.
4. OAuth2 Access Token Scope
Client can ask for the resource with specific access rights using scope [want to access feeds & photos of this users facebook account], and authorization server in turn return scope showing what access rights were actually granted to the client [Resource owner only allowed feeds access, no photos e.g.].
Let’s Get into Code
Let’s implement the necessary building blocks to implement OAuth using Spring Security, in order to access our REST resources.
1. Resource Server
Resource Server hosts the resources [our REST API] the client is interested in. Resources are located on /user/
.@EnableResourceServer
annotation, applied on OAuth2 Resource Servers, enables a Spring Security filter that authenticates requests using an incoming OAuth2 token. Class ResourceServerConfigurerAdapter
implementsResourceServerConfigurer
providing methods to adjust the access rules and paths that are protected by OAuth2 security.
package
com.websystique.springmvc.security;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import
org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import
org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import
org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
@Configuration
@EnableResourceServer
public
class
ResourceServerConfiguration
extends
ResourceServerConfigurerAdapter {
private
static
final
String RESOURCE_ID =
"my_rest_api"
;
@Override
public
void
configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(
false
);
}
@Override
public
void
configure(HttpSecurity http)
throws
Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers(
"/user/**"
)
.and().authorizeRequests()
.antMatchers(
"/user/**"
).access(
"hasRole('ADMIN')"
)
.and().exceptionHandling().accessDeniedHandler(
new
OAuth2AccessDeniedHandler());
}
}
|
2. Authorization Server
Authorization server is the one responsible for verifying credentials and if credentials are OK, providing the tokens[refresh-token as well as access-token]. It also contains information about registered clients and possible access scopes and grant types. The token store is used to store the token. We will be using an in-memory token store.@EnableAuthorizationServer
enables an Authorization Server (i.e. an AuthorizationEndpoint and a TokenEndpoint) in the current application context. Class AuthorizationServerConfigurerAdapter
implementsAuthorizationServerConfigurer
which provides all the necessary methods to configure an Authorization server.
package
com.websystique.springmvc.security;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.beans.factory.annotation.Qualifier;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.security.authentication.AuthenticationManager;
import
org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import
org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import
org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import
org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import
org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import
org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration
@EnableAuthorizationServer
public
class
AuthorizationServerConfiguration
extends
AuthorizationServerConfigurerAdapter {
private
static
String REALM=
"MY_OAUTH_REALM"
;
@Autowired
private
TokenStore tokenStore;
@Autowired
private
UserApprovalHandler userApprovalHandler;
@Autowired
@Qualifier
(
"authenticationManagerBean"
)
private
AuthenticationManager authenticationManager;
@Override
public
void
configure(ClientDetailsServiceConfigurer clients)
throws
Exception {
clients.inMemory()
.withClient(
"my-trusted-client"
)
.authorizedGrantTypes(
"password"
,
"authorization_code"
,
"refresh_token"
,
"implicit"
)
.authorities(
"ROLE_CLIENT"
,
"ROLE_TRUSTED_CLIENT"
)
.scopes(
"read"
,
"write"
,
"trust"
)
.secret(
"secret"
)
.accessTokenValiditySeconds(
120
).
//Access token is only valid for 2 minutes.
refreshTokenValiditySeconds(
600
);
//Refresh token is only valid for 10 minutes.
}
@Override
public
void
configure(AuthorizationServerEndpointsConfigurer endpoints)
throws
Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
@Override
public
void
configure(AuthorizationServerSecurityConfigurer oauthServer)
throws
Exception {
oauthServer.realm(REALM+
"/client"
);
}
}
|
Above configuration
- Registers a client with client-id ‘my-trusted-client’ and password ‘secret’ and roles & scope he is allowed for.
- Specifies that any generated access token will be valid for only 120 seconds
- Specifies that any generated refresh token will be valid for only 600 seconds
3. Security Configuration
Gluing everything together. Endpoint /oauth/token
is used to request a token [access or refresh]. Resource owners [bill,bob] are configured here itself.
package
com.websystique.springmvc.security;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.context.annotation.Bean;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.security.authentication.AuthenticationManager;
import
org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityConfigurerAdapter;
import
org.springframework.security.oauth2.provider.ClientDetailsService;
import
org.springframework.security.oauth2.provider.approval.ApprovalStore;
import
org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import
org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import
org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import
org.springframework.security.oauth2.provider.token.TokenStore;
import
org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
@Configuration
@EnableWebSecurity
public
class
OAuth2SecurityConfiguration
extends
WebSecurityConfigurerAdapter {
@Autowired
private
ClientDetailsService clientDetailsService;
@Autowired
public
void
globalUserDetails(AuthenticationManagerBuilder auth)
throws
Exception {
auth.inMemoryAuthentication()
.withUser(
"bill"
).password(
"abc123"
).roles(
"ADMIN"
).and()
.withUser(
"bob"
).password(
"abc123"
).roles(
"USER"
);
}
@Override
protected
void
configure(HttpSecurity http)
throws
Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers(
"/oauth/token"
).permitAll();
}
@Override
@Bean
public
AuthenticationManager authenticationManagerBean()
throws
Exception {
return
super
.authenticationManagerBean();
}
@Bean
public
TokenStore tokenStore() {
return
new
InMemoryTokenStore();
}
@Bean
@Autowired
public
TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler =
new
TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(
new
DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return
handler;
}
@Bean
@Autowired
public
ApprovalStore approvalStore(TokenStore tokenStore)
throws
Exception {
TokenApprovalStore store =
new
TokenApprovalStore();
store.setTokenStore(tokenStore);
return
store;
}
}
|
Additionally, enable Global method security which will activate @PreFilter, @PostFilter, @PreAuthorize @PostAuthorize annotations if we want to use them.
package
com.websystique.springmvc.security;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import
org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import
org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import
org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
@Configuration
@EnableGlobalMethodSecurity
(prePostEnabled =
true
, proxyTargetClass =
true
)
public
class
MethodSecurityConfig
extends
GlobalMethodSecurityConfiguration {
@Autowired
private
OAuth2SecurityConfiguration securityConfig;
@Override
protected
MethodSecurityExpressionHandler createExpressionHandler() {
return
new
OAuth2MethodSecurityExpressionHandler();
}
}
|
4. Endpoints and their purpose
- Attempt to access resources [REST API] without any authorization [will fail of-course].
GET http://localhost:8080/SpringSecurityOAuth2Example/user/
- Ask for tokens[access+refresh] using HTTP POST on
/oauth/token
, with grant_type=password,and resource owners credentials as req-params. Additionally, send client credentials in Authorization header.POST http://localhost:8080/SpringSecurityOAuth2Example/oauth/token?grant_type=password&username=bill&password=abc123
- Ask for a new access token via valid refresh-token, using HTTP POST on
/oauth/token
, with grant_type=refresh_token,and sending actual refresh token. Additionally, send client credentials in Authorization header.POST http://localhost:8080/SpringSecurityOAuth2Example/oauth/token?grant_type=refresh_token&refresh_token=094b7d23-973f-4cc1-83ad-8ffd43de1845
- Access the resource by providing an access token using
access_token
query param with request.
GET http://localhost:8080/SpringSecurityOAuth2Example/user/?access_token=3525d0e4-d881-49e7-9f91-bcfd18259109
5. Rest API
The simple Spring REST API i used in most of my posts.
package
com.websystique.springmvc.controller;
import
java.util.List;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.http.HttpHeaders;
import
org.springframework.http.HttpStatus;
import
org.springframework.http.MediaType;
import
org.springframework.http.ResponseEntity;
import
org.springframework.web.bind.annotation.PathVariable;
import
org.springframework.web.bind.annotation.RequestBody;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RequestMethod;
import
org.springframework.web.bind.annotation.RestController;
import
org.springframework.web.util.UriComponentsBuilder;
import
com.websystique.springmvc.model.User;
import
com.websystique.springmvc.service.UserService;
@RestController
public
class
HelloWorldRestController {
@Autowired
UserService userService;
//Service which will do all data retrieval/manipulation work
//-------------------Retrieve All Users--------------------------------------------------------
@RequestMapping
(value =
"/user/"
, method = RequestMethod.GET)
public
ResponseEntity<List<User>> listAllUsers() {
List<User> users = userService.findAllUsers();
if
(users.isEmpty()){
return
new
ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);
//You many decide to return HttpStatus.NOT_FOUND
}
return
new
ResponseEntity<List<User>>(users, HttpStatus.OK);
}
//-------------------Retrieve Single User--------------------------------------------------------
@RequestMapping
(value =
"/user/{id}"
, method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE})
public
ResponseEntity<User> getUser(
@PathVariable
(
"id"
)
long
id) {
System.out.println(
"Fetching User with id "
+ id);
User user = userService.findById(id);
if
(user ==
null
) {
System.out.println(
"User with id "
+ id +
" not found"
);
return
new
ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
return
new
ResponseEntity<User>(user, HttpStatus.OK);
}
//-------------------Create a User--------------------------------------------------------
@RequestMapping
(value =
"/user/"
, method = RequestMethod.POST)
public
ResponseEntity<Void> createUser(
@RequestBody
User user, UriComponentsBuilder ucBuilder) {
System.out.println(
"Creating User "
+ user.getName());
if
(userService.isUserExist(user)) {
System.out.println(
"A User with name "
+ user.getName() +
" already exist"
);
return
new
ResponseEntity<Void>(HttpStatus.CONFLICT);
}
userService.saveUser(user);
HttpHeaders headers =
new
HttpHeaders();
headers.setLocation(ucBuilder.path(
"/user/{id}"
).buildAndExpand(user.getId()).toUri());
return
new
ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
//------------------- Update a User --------------------------------------------------------
@RequestMapping
(value =
"/user/{id}"
, method = RequestMethod.PUT)
public
ResponseEntity<User> updateUser(
@PathVariable
(
"id"
)
long
id,
@RequestBody
User user) {
System.out.println(
"Updating User "
+ id);
User currentUser = userService.findById(id);
if
(currentUser==
null
) {
System.out.println(
"User with id "
+ id +
" not found"
);
return
new
ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
currentUser.setName(user.getName());
currentUser.setAge(user.getAge());
currentUser.setSalary(user.getSalary());
userService.updateUser(currentUser);
return
new
ResponseEntity<User>(currentUser, HttpStatus.OK);
}
//------------------- Delete a User --------------------------------------------------------
@RequestMapping
(value =
"/user/{id}"
, method = RequestMethod.DELETE)
public
ResponseEntity<User> deleteUser(
@PathVariable
(
"id"
)
long
id) {
System.out.println(
"Fetching & Deleting User with id "
+ id);
User user = userService.findById(id);
if
(user ==
null
) {
System.out.println(
"Unable to delete. User with id "
+ id +
" not found"
);
return
new
ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
userService.deleteUserById(id);
return
new
ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
//------------------- Delete All Users --------------------------------------------------------
@RequestMapping
(value =
"/user/"
, method = RequestMethod.DELETE)
public
ResponseEntity<User> deleteAllUsers() {
System.out.println(
"Deleting All Users"
);
userService.deleteAllUsers();
return
new
ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
}
|
6. Running the application
Run it and test it using two different clients.
Client 1: Postman
Try to access a resource without any auth info, wil get a 401.
Let’s get the tokens. First add an authorization header with client credentials [my-trusted-client/secret].
Click on update request, verify the header in header-tab.
Send the Post request, you should receive the response containing access-token
as well as refresh-token
.
Save these tokens somewhere, you will need them. Now you can use this access-token [valid for 2 minutes] to access resources.
After 2 minutes, access-token gets expired, your further resource requests will fail.
We need a new access-token. Fire a post to with refresh-token to get a brand-new access-token.
Use this new access-token to access the resources.
Refresh-token expires too[10 minutes]. After that, you should see your refresh request getting failed.
It means you need to request a new refresh+access-token, as in step 2.
Client 2: RestTemplate based java application
Method sendTokenRequest is used to actually get the tokens. The access-token we got in response is then used with each request. If required, You can implement the refresh-token flow easily in below example.
package
com.websystique.springmvc;
import
java.net.URI;
import
java.util.Arrays;
import
java.util.LinkedHashMap;
import
java.util.List;
import
org.apache.commons.codec.binary.Base64;
import
org.springframework.http.HttpEntity;
import
org.springframework.http.HttpHeaders;
import
org.springframework.http.HttpMethod;
import
org.springframework.http.MediaType;
import
org.springframework.http.ResponseEntity;
import
org.springframework.util.Assert;
import
org.springframework.web.client.RestTemplate;
import
com.websystique.springmvc.model.AuthTokenInfo;
import
com.websystique.springmvc.model.User;
public
class
SpringRestClient {
public
static
final
String AUTH_SERVER_URI =
"http://localhost:8080/SpringSecurityOAuth2Example/oauth/token"
;
public
static
final
String QPM_PASSWORD_GRANT =
"?grant_type=password&username=bill&password=abc123"
;
public
static
final
String QPM_ACCESS_TOKEN =
"?access_token="
;
/*
* Prepare HTTP Headers.
*/
private
static
HttpHeaders getHeaders(){
HttpHeaders headers =
new
HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
return
headers;
}
/*
* Add HTTP Authorization header, using Basic-Authentication to send client-credentials.
*/
private
static
HttpHeaders getHeadersWithClientCredentials(){
String plainClientCredentials=
"my-trusted-client:secret"
;
String base64ClientCredentials =
new
String(Base64.encodeBase64(plainClientCredentials.getBytes()));
HttpHeaders headers = getHeaders();
headers.add(
"Authorization"
,
"Basic "
+ base64ClientCredentials);
return
headers;
}
/*
* Send a POST request [on /oauth/token] to get an access-token, which will then be send with each request.
*/
@SuppressWarnings
({
"unchecked"
})
private
static
AuthTokenInfo sendTokenRequest(){
RestTemplate restTemplate =
new
RestTemplate();
HttpEntity<String> request =
new
HttpEntity<String>(getHeadersWithClientCredentials());
ResponseEntity<Object> response = restTemplate.exchange(AUTH_SERVER_URI+QPM_PASSWORD_GRANT, HttpMethod.POST, request, Object.
class
);
LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>)response.getBody();
AuthTokenInfo tokenInfo =
null
;
if
(map!=
null
){
tokenInfo =
new
AuthTokenInfo();
tokenInfo.setAccess_token((String)map.get(
"access_token"
));
tokenInfo.setToken_type((String)map.get(
"token_type"
));
tokenInfo.setRefresh_token((String)map.get(
"refresh_token"
));
tokenInfo.setExpires_in((
int
)map.get(
"expires_in"
));
tokenInfo.setScope((String)map.get(
"scope"
));
System.out.println(tokenInfo);
//System.out.println("access_token ="+map.get("access_token")+", token_type="+map.get("token_type")+", refresh_token="+map.get("refresh_token")
//+", expires_in="+map.get("expires_in")+", scope="+map.get("scope"));;
}
else
{
System.out.println(
"No user exist----------"
);
}
return
tokenInfo;
}
/*
* Send a GET request to get list of all users.
*/
@SuppressWarnings
({
"unchecked"
,
"rawtypes"
})
private
static
void
listAllUsers(AuthTokenInfo tokenInfo){
Assert.notNull(tokenInfo,
"Authenticate first please......"
);
System.out.println(
"\nTesting listAllUsers API-----------"
);
RestTemplate restTemplate =
new
RestTemplate();
HttpEntity<String> request =
new
HttpEntity<String>(getHeaders());
ResponseEntity<List> response = restTemplate.exchange(REST_SERVICE_URI+
"/user/"
+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(),
HttpMethod.GET, request, List.
class
);
List<LinkedHashMap<String, Object>> usersMap = (List<LinkedHashMap<String, Object>>)response.getBody();
if
(usersMap!=
null
){
for
(LinkedHashMap<String, Object> map : usersMap){
System.out.println(
"User : id="
+map.get(
"id"
)+
", Name="
+map.get(
"name"
)+
", Age="
+map.get(
"age"
)+
", Salary="
+map.get(
"salary"
));;
}
}
else
{
System.out.println(
"No user exist----------"
);
}
}
/*
* Send a GET request to get a specific user.
*/
private
static
void
getUser(AuthTokenInfo tokenInfo){
Assert.notNull(tokenInfo,
"Authenticate first please......"
);
System.out.println(
"\nTesting getUser API----------"
);
RestTemplate restTemplate =
new
RestTemplate();
HttpEntity<String> request =
new
HttpEntity<String>(getHeaders());
ResponseEntity<User> response = restTemplate.exchange(REST_SERVICE_URI+
"/user/1"
+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(),
HttpMethod.GET, request, User.
class
);
User user = response.getBody();
System.out.println(user);
}
/*
* Send a POST request to create a new user.
*/
private
static
void
createUser(AuthTokenInfo tokenInfo) {
Assert.notNull(tokenInfo,
"Authenticate first please......"
);
System.out.println(
"\nTesting create User API----------"
);
RestTemplate restTemplate =
new
RestTemplate();
User user =
new
User(
0
,
"Sarah"
,
51
,
134
);
HttpEntity<Object> request =
new
HttpEntity<Object>(user, getHeaders());
URI uri = restTemplate.postForLocation(REST_SERVICE_URI+
"/user/"
+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(),
request, User.
class
);
System.out.println(
"Location : "
+uri.toASCIIString());
}
/*
* Send a PUT request to update an existing user.
*/
private
static
void
updateUser(AuthTokenInfo tokenInfo) {
Assert.notNull(tokenInfo,
"Authenticate first please......"
);
System.out.println(
"\nTesting update User API----------"
);
RestTemplate restTemplate =
new
RestTemplate();
User user =
new
User(
1
,
"Tomy"
,
33
,
70000
);
HttpEntity<Object> request =
new
HttpEntity<Object>(user, getHeaders());
ResponseEntity<User> response = restTemplate.exchange(REST_SERVICE_URI+
"/user/1"
+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(),
HttpMethod.PUT, request, User.
class
);
System.out.println(response.getBody());
}
/*
* Send a DELETE request to delete a specific user.
*/
private
static
void
deleteUser(AuthTokenInfo tokenInfo) {
Assert.notNull(tokenInfo,
"Authenticate first please......"
);
System.out.println(
"\nTesting delete User API----------"
);
RestTemplate restTemplate =
new
RestTemplate();
HttpEntity<String> request =
new
HttpEntity<String>(getHeaders());
restTemplate.exchange(REST_SERVICE_URI+
"/user/3"
+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(),
HttpMethod.DELETE, request, User.
class
);
}
/*
* Send a DELETE request to delete all users.
*/
private
static
void
deleteAllUsers(AuthTokenInfo tokenInfo) {
Assert.notNull(tokenInfo,
"Authenticate first please......"
);
System.out.println(
"\nTesting all delete Users API----------"
);
RestTemplate restTemplate =
new
RestTemplate();
HttpEntity<String> request =
new
HttpEntity<String>(getHeaders());
restTemplate.exchange(REST_SERVICE_URI+
"/user/"
+QPM_ACCESS_TOKEN+tokenInfo.getAccess_token(),
HttpMethod.DELETE, request, User.
class
);
}
public
static
void
main(String args[]){
AuthTokenInfo tokenInfo = sendTokenRequest();
listAllUsers(tokenInfo);
getUser(tokenInfo);
createUser(tokenInfo);
listAllUsers(tokenInfo);
updateUser(tokenInfo);
listAllUsers(tokenInfo);
deleteUser(tokenInfo);
listAllUsers(tokenInfo);
deleteAllUsers(tokenInfo);
listAllUsers(tokenInfo);
}
}
|
Above code will produce following output:
AuthTokenInfo [access_token=fceed386-5923-4bf8-b193-1d76f95da4c4, token_type=bearer, refresh_token=29d28ee2-9d09-483f-a2d6-7f93e7a31667, expires_in=71, scope=read write trust]
Testing listAllUsers API-----------
User : id=1, Name=Sam, Age=30, Salary=70000.0
User : id=2, Name=Tom, Age=40, Salary=50000.0
User : id=3, Name=Jerome, Age=45, Salary=30000.0
User : id=4, Name=Silvia, Age=50, Salary=40000.0
Testing getUser API----------
User [id=1, name=Sam, age=30, salary=70000.0]
Testing create User API----------
Testing listAllUsers API-----------
User : id=1, Name=Sam, Age=30, Salary=70000.0
User : id=2, Name=Tom, Age=40, Salary=50000.0
User : id=3, Name=Jerome, Age=45, Salary=30000.0
User : id=4, Name=Silvia, Age=50, Salary=40000.0
User : id=5, Name=Sarah, Age=51, Salary=134.0
Testing update User API----------
User [id=1, name=Tomy, age=33, salary=70000.0]
Testing listAllUsers API-----------
User : id=1, Name=Tomy, Age=33, Salary=70000.0
User : id=2, Name=Tom, Age=40, Salary=50000.0
User : id=3, Name=Jerome, Age=45, Salary=30000.0
User : id=4, Name=Silvia, Age=50, Salary=40000.0
User : id=5, Name=Sarah, Age=51, Salary=134.0
Testing delete User API----------
Testing listAllUsers API-----------
User : id=1, Name=Tomy, Age=33, Salary=70000.0
User : id=2, Name=Tom, Age=40, Salary=50000.0
User : id=4, Name=Silvia, Age=50, Salary=40000.0
User : id=5, Name=Sarah, Age=51, Salary=134.0
Testing all delete Users API----------
Testing listAllUsers API-----------
No user exist----------
|
Project Structure
pom.xml
<
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
>com.websystique.springmvc</
groupId
>
<
artifactId
>SpringSecurityOAuth2Example</
artifactId
>
<
version
>1.0.0</
version
>
<
packaging
>war</
packaging
>
<
name
>SpringSecurityOAuth2Example</
name
>
<
properties
>
<
project.build.sourceEncoding
>UTF-8</
project.build.sourceEncoding
>
<
springframework.version
>4.3.1.RELEASE</
springframework.version
>
<
springsecurity.version
>4.1.1.RELEASE</
springsecurity.version
>
<
springsecurityoauth2.version
>2.0.10.RELEASE</
springsecurityoauth2.version
>
<
jackson.library
>2.7.5</
jackson.library
>
</
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
>
<!-- Spring Security OAuth2-->
<
dependency
>
<
groupId
>org.springframework.security.oauth</
groupId
>
<
artifactId
>spring-security-oauth2</
artifactId
>
<
version
>${springsecurityoauth2.version}</
version
>
</
dependency
>
<!-- Jackson libraries -->
<
dependency
>
<
groupId
>com.fasterxml.jackson.core</
groupId
>
<
artifactId
>jackson-databind</
artifactId
>
<
version
>${jackson.library}</
version
>
</
dependency
>
<
dependency
>
<
groupId
>com.fasterxml.jackson.dataformat</
groupId
>
<
artifactId
>jackson-dataformat-xml</
artifactId
>
<
version
>${jackson.library}</
version
>
</
dependency
>
<
dependency
>
<
groupId
>javax.servlet</
groupId
>
<
artifactId
>javax.servlet-api</
artifactId
>
<
version
>3.1.0</
version
>
</
dependency
>
</
dependencies
>
<
build
>
<
plugins
>
<
plugin
>
<
groupId
>org.apache.maven.plugins</
groupId
>
<
artifactId
>maven-compiler-plugin</
artifactId
>
<
version
>3.2</
version
>
<
configuration
>
<
source
>1.7</
source
>
<
target
>1.7</
target
>
</
configuration
>
</
plugin
>
<
plugin
>
<
groupId
>org.apache.maven.plugins</
groupId
>
<
artifactId
>maven-war-plugin</
artifactId
>
<
version
>2.4</
version
>
<
configuration
>
<
warSourceDirectory
>src/main/webapp</
warSourceDirectory
>
<
warName
>SpringSecurityOAuth2Example</
warName
>
<
failOnMissingWebXml
>false</
failOnMissingWebXml
>
</
configuration
>
</
plugin
>
</
plugins
>
<
finalName
>SpringSecurityOAuth2Example</
finalName
>
</
build
>
</
project
>
|
Download Source Code
References
- OAuth2 Specification
- Spring OAuth2 Official reference
- Spring Security 4 Project Page
- Spring Security 4 Reference Manual