鍍金池/ 教程/ Java/ Secure Spring REST API使用OAuth2
Spring Security自定義表單登錄注釋示例
Spring Secrity與Hibernate基于角色登錄實例
Spring Security自定義表單登錄實例
Secure Spring REST API使用OAuth2
Spring Security與Hibernate整合以及XML實例
Spring Security入門程序示例
Spring Security+Hibernate密碼編碼器Bcrypt實例
Spring Security標(biāo)簽庫顯示視圖
Spring Security基于角色登錄實例
Spring Security使用@PreAuthorize,@PostAuthorize, @Secured方法安全
Secure Spring REST API使用基本認(rèn)證
Spring Security入門程序注釋示例
Spring Security+Hibernate記住我實例
Spring MVC4 + Spring Security4 + Hibernate實例
Spring Security注銷登錄實例
AngularJS+Spring Security使用基本身份驗證
Spring Security教程

Secure Spring REST API使用OAuth2

Spring REST API 這一次使用的是 OAuth2,這篇文章簡單介紹在一個 REST API 中使用 Spring OAuth2 需要什么。我們將使用兩個不同的客戶端[Postman和基于Java應(yīng)用程序的Spring RestTemplate]來訪問OAuth2保護(hù)的REST資源。
如果你已經(jīng)熟悉 OAuth2 概念,那么您可以直接跳過理論部分,直接進(jìn)入代碼實現(xiàn)。與往常一樣,完整的代碼可以在本文的末尾處下載。

OAuth2是什么?

OAuth2用戶是一個標(biāo)準(zhǔn)化的授權(quán)協(xié)議/框架。按照官方的OAuth2定義

OAuth 2.0授權(quán)框架使第三方應(yīng)用程序來獲取對HTTP服務(wù)的有限訪問機會。無論是通過編排資源所有者和HTTP服務(wù)之間的交互批準(zhǔn)的資源所有者,或通過允許第三方應(yīng)用程序來獲取自己的訪問權(quán)限。

大牌玩家像谷歌,F(xiàn)acebook和其他公司已經(jīng)開始使用自己的OAuth2相當(dāng)一段時間了。其它企業(yè)也正向著使用OAuth2的步伐快速移動。

Spring Security OAuth項目提供所有可能開發(fā)使用的Spring OAuth2用戶兼容實現(xiàn)所需的API。 Official Spring security oauth項目提供了實現(xiàn) OAuth2 一個完整的例子。這個篇文章的代碼示例是在這個官方提供的例子的基礎(chǔ)上修改。這篇文章的目的是只使用所需最低限度的功能,以演示我們的REST API,僅此而已。我也還在學(xué)習(xí),所以如果有什么不對的地方隨時糾正我。

至少你應(yīng)該知道 OAuth2 的四個關(guān)鍵概念:

1. OAuth2角色

在OAuth2中用戶定義了四個角色:
  • 資源擁有乾(resource owner):
    能夠準(zhǔn)許訪問受保護(hù)資源的實體。當(dāng)資源的所有者是一個人,它被稱為終端用戶。
  • 資源服務(wù)器(resource server):
    服務(wù)器托管受保護(hù)的資源,能夠接受和響應(yīng)使用訪問令牌保護(hù)資源的請求。
  • 客戶端(client):
    應(yīng)用程序使資源所有者的請求有授權(quán)訪問受保護(hù)資源。這可能是一個移動應(yīng)用程序要求您的權(quán)限來訪問您的Facebook訂閱源,REST客戶端試圖訪問REST API,
    如一個網(wǎng)站[Stackoverflow]提供使用Facebook帳戶或是類似QQ第三方帳號登錄來替代使用網(wǎng)站帳號登錄。
  • authorization server:
    服務(wù)器在成功認(rèn)證資源所有者和獲得授權(quán)之后發(fā)出訪問令牌給客戶端。
在我們的例子中,REST API只能通過資源服務(wù)器,這個請求訪問需要一個訪問令牌。

2.授權(quán)OAuth2給予類型

授權(quán)給予是代表資源所有者的授權(quán)(訪問其受保護(hù)的資源),用于客戶端以獲得訪問令牌的憑證。該規(guī)范定義了四種類型的給予:
  • 授權(quán)碼
  • 隱性的
  • 資源所有者密碼憑據(jù)
  • 客戶端憑據(jù)

我們將使用資源所有者密碼憑據(jù)授予類型。原因很簡單,我們沒有執(zhí)行那些重定向到一個登錄頁面視圖。

只有使用了客戶端[Postman或基于RestTemplate的Java客戶端]有資源所有者的憑證,他們提供這些憑證到授權(quán)服務(wù)器[客戶端和憑據(jù)一起]以最終獲得訪問令牌[和可選刷新令牌]
然后使用該令牌來訪問資源。
一個常見的例子是Gmail應(yīng)用[客戶]在智能手機登錄,它需要您的憑證,并用它們連接到Gmail服務(wù)器。
這也表明,"密碼憑據(jù)給予"是最適合在當(dāng)客戶端和服務(wù)器來自同一家公司的信任是存在的,
而不想提供您的憑證給第三方。

3.OAuth2令牌

令牌是實現(xiàn)指定隨機字符串,由授權(quán)服務(wù)器生成,并在客戶端請求時將它們發(fā)出。
  • Access Token : 發(fā)送的每個請求,有效期一般是一個很短的壽命[例如:一個小時]
  • Refresh Token : 主要用于獲取新的訪問令牌,而不是每個請求都發(fā)送,通常比訪問令牌生命更長。
在HTTPS個詞 : 對于任何形式的安全實現(xiàn),從基本身份驗證到一個完全成熟的OAuth2實現(xiàn),HTTPS是必須具備的。如果沒有HTTPS,不管你的實現(xiàn)是什么,安全性都是容易受到損害的。

4. OAuth2用戶訪問令牌范圍

客戶端可以要求使用范圍指定訪問權(quán)限的資源[訪問訂閱與用戶Facebook賬戶照片],授權(quán)服務(wù)器顯示的訪問權(quán)限實際上是授予客戶端[只允許資源所有者訂閱]。

讓我們來看看代碼

讓我們來使用Spring Security實現(xiàn)必要安全過濾,才能進(jìn)入REST資源來實現(xiàn)OAuth。

1.資源服務(wù)器

資源服務(wù)器承載資源[REST API],客戶端感興趣的資源位于  /user/ 。@EnableResourceServer注釋,適用在OAuth2資源服務(wù)器,實現(xiàn)了Spring Security的過濾器驗證的請求傳入OAuth2令牌。 ResourceServerConfigurerAdapter類實現(xiàn) ResourceServerConfigurer 提供的方法來調(diào)整 OAuth2安全保護(hù)的訪問規(guī)則和路徑。

package com.yiibai.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.授權(quán)服務(wù)器

如果憑據(jù) OK 授權(quán)服務(wù)器是一個負(fù)責(zé)驗證憑據(jù),提供令牌[刷新令牌以及訪問令牌]。它還包含有關(guān)注冊客戶和訪問范圍以及授權(quán)類型的信息。 令牌存儲用于存儲令牌。我們將使用內(nèi)存來存儲令牌。@EnableAuthorizationServer使一個授權(quán)服務(wù)器(即,AuthorizationEndpoint和TokenEndpoint)在當(dāng)前的應(yīng)用程序上下文。
AuthorizationServerConfigurerAdapter類實現(xiàn)AuthorizationServerConfigurer它提供了所有必要的方法來配置一個授權(quán)服務(wù)器。
package com.yiibai.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");
	}

}

上面的配置:

  • 注冊一個客戶端,客戶端ID是“my-trusted-client'和密碼為'secret',客戶端允許的角色和范圍;
  • 指定任何生成的訪問令牌的有效期只有120秒;
  • 指定任何刷新生成令牌的有效期只有600秒

3.安全配置

結(jié)合一切在一起。端點 /oauth/token 用于請求令牌[訪問或刷新]。資源所有者[bill,bob] 在這里配置。
package com.yiibai.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;
	}
	
}
此外,使用全局安全方法,則可激活@PreFilter,@PostFilter,@PreAuthorize@PostAuthorize注釋。
package com.yiibai.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.端點和它們的目的

  • 試圖訪問資源[REST API],但是這是在沒有任何授權(quán)[失敗]情況下。
    GET http://localhost:8080/SpringSecurityOAuth2Example/user/
  • 要求令牌[訪問+刷新]使用HTTP POST 在 /oauth/token 上 ,grant_type=password和資源所有者憑證作為  req-params 。此外在授權(quán)頭發(fā)送客戶端憑據(jù)。

    POST http://localhost:8080/SpringSecurityOAuth2Example/oauth/token?grant_type=password&username=bill&password=abc123

  • 要求通過有效的刷新令牌新的訪問令牌,在 /oauth/token 上使用HTTP POST,以及 grant_type=refresh_token 一并發(fā)送刷新令牌。此外在授權(quán)頭發(fā)送客戶端憑據(jù)。

    POST http://localhost:8080/SpringSecurityOAuth2Example/oauth/token?grant_type=refresh_token&refresh_token=094b7d23-973f-4cc1-83ad-8ffd43de1845

  • 使用 access_token 查詢參數(shù)和要求提供訪問資源的令牌。
    GET http://localhost:8080/SpringSecurityOAuth2Example/user/?access_token=3525d0e4-d881-49e7-9f91-bcfd18259109

5. Rest API

簡單的Spring REST API,在前面教程文章中我們使用的那樣。
package com.yiibai.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.yiibai.springmvc.model.User;
import com.yiibai.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.運行應(yīng)用程序

運行它,并使用兩種不同的客戶端進(jìn)行測試。

客戶端1: Postman

嘗試不使用任何驗證信息來直拉訪問資源:http://localhost:8080/SpringSecurityOAuth2/user/,將得到401。

現(xiàn)在我們獲取頭。選擇HTTP方法為 POST,Authorization Type:Basic Auth ,URL:http://localhost:8080/SpringSecurityOAuth2/oauth/token?grant_type=password&username=bill&password=abc123 ,然后再將客戶端憑據(jù) [my-trusted-client/secret]添加到授權(quán)頭。點擊"update request"(更新請求),發(fā)送POST請求后,您會在響應(yīng)中收到訪問令牌(access-token),以及刷新令牌(refresh-token)。如下所示 -  


保存這些令牌在需要它們時?,F(xiàn)在可以使用這個訪問令牌[有效期為2分鐘]來訪問資源?,F(xiàn)在我們再使用這個 token 來訪問資源,把它添加到URL中如:http://localhost:8080/SpringSecurityOAuth2/user/?access_token=7fbb77ae-3d8f-4d78-b8de-3222353f680b 得到結(jié)果如下所示 -

2分鐘后,訪問令牌被過期,那么進(jìn)一步的資源請求將失敗。

我們需要一個新的訪問令牌。觸發(fā)一個 post 以后用刷新令牌來獲得一個新的訪問令牌。請求URL:http://localhost:8080/SpringSecurityOAuth2/oauth/token?grant_type=refresh_token&refresh_token=fefcf12c-2683-4f1a-a446-941666dcfe23

使用這個新的訪問令牌(c8edfa2f-d2aa-4f1b-81e1-32df3fefe9a8)繼續(xù)訪問資源。把它添加到URL中如:http://localhost:8080/SpringSecurityOAuth2/user/?access_token=be5c7dec-ae17-403d-ab66-86cf5262f159 得到結(jié)果如下所示 -

新令牌(Refresh-token)也會過期[10分鐘]。在這之后,您會看到刷新請求失敗。

這意味著您需要刷新申請新的訪問令牌,如第2步中。

客戶端2:基于RestTemplate的Java應(yīng)用程序

sendTokenRequest 方法用于獲得實際令牌。訪問令牌(access-token )我們從響應(yīng)中獲得了,之后將它應(yīng)用到每個請求中。如果需要,您可以在下面的例子中很容易地實現(xiàn) refresh-token 流程。
package com.yiibai.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.yiibai.springmvc.model.AuthTokenInfo;
import com.yiibai.springmvc.model.User;
 
public class SpringRestClient {
 
    public static final String REST_SERVICE_URI = "http://localhost:8080/SpringSecurityOAuth2";
    
    public static final String AUTH_SERVER_URI = "http://localhost:8080/SpringSecurityOAuth2/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);
    }
}
上面的代碼會產(chǎn)生以下的輸出:
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----------
Location : http://localhost:8080/SpringSecurityOAuth2Example/user/5

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

工程目錄結(jié)構(gòu)




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.yiibai.springmvc</groupId>
	<artifactId>SpringSecurityOAuth2</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.vers