Spring REST API 这一次使用的是 OAuth2,这篇文章简单介绍在一个 REST API 中使用 Spring OAuth2 需要什么。我们将使用两个不同的客户端[Postman和基于Java应用程序的Spring RestTemplate]来访问OAuth2保护的REST资源。
如果你已经熟悉 OAuth2 概念,那么您可以直接跳过理论部分,直接进入代码实现。与往常一样,完整的代码可以在本文的末尾处下载。

其它你可能会喜欢的文章:
  • AngularJS+Spring Security使用基本认证
  • Secure Spring REST API使用基本验证

OAuth2是什么?

OAuth2用户是一个标准化的授权协议/框架。按照官方的 OAuth2定义:

OAuth 2.0授权框架使第三方应用程序来获取对HTTP服务的有限访问机会。无论是通过编排资源所有者和HTTP服务之间的交互批准的资源所有者,或通过允许第三方应用程序来获取自己的访问权限。

大牌玩家像谷歌,Facebook和其他公司已经开始使用自己的OAuth2相当一段时间了。其它企业也正向着使用OAuth2的步伐快速移动。

Spring Security OAuth项目提供所有可能开发使用的Spring OAuth2用户兼容实现所需的API。 Official Spring security oauth项目提供了实现 OAuth2 一个完整的例子。这个篇文章的代码示例是在这个官方提供的例子的基础上修改。这篇文章的目的是只使用所需最低限度的功能,以演示我们的REST API,仅此而已。我也还在学习,所以如果有什么不对的地方随时纠正我。

至少你应该知道 OAuth2 的四个关键概念:

1. OAuth2角色

在OAuth2中用户定义了四个角色:
  • 资源拥有乾(resource owner):

    能够准许访问受保护资源的实体。当资源的所有者是一个人,它被称为终端用户。
  • 资源服务器(resource server):
    服务器托管受保护的资源,能够接受和响应使用访问令牌保护资源的请求。
  • 客户端(client):
    应用程序使资源所有者的请求有授权访问受保护资源。这可能是一个移动应用程序要求您的权限来访问您的Facebook订阅源,REST客户端试图访问REST API,
    如一个网站[Stackoverflow]提供使用Facebook帐户或是类似QQ第三方帐号登录来替代使用网站帐号登录。
  • authorization server:
    服务器在成功认证资源所有者和获得授权之后发出访问令牌给客户端。
在我们的例子中,REST API只能通过资源服务器,这个请求访问需要一个访问令牌。
2.授权OAuth2给予类型
授权给予是代表资源所有者的授权(访问其受保护的资源),用于客户端以获得访问令牌的凭证。该规范定义了四种类型的给予:
  • 授权码
  • 隐性的
  • 资源所有者密码凭据
  • 客户端凭据

我们将使用资源所有者密码凭据授予类型。原因很简单,我们没有执行那些重定向到一个登录页面视图。

只有使用了客户端[Postman或基于RestTemplate的Java客户端]有资源所有者的凭证,他们提供这些凭证到授权服务器[客户端和凭据一起]以最终获得访问令牌[和可选刷新令牌]
然后使用该令牌来访问资源。
一个常见的例子是Gmail应用[客户]在智能手机登录,它需要您的凭证,并用它们连接到Gmail服务器。
这也表明,"密码凭据给予"是最适合在当客户端和服务器来自同一家公司的信任是存在的,
而不想提供您的凭证给第三方。
3.OAuth2令牌
令牌是实现指定随机字符串,由授权服务器生成,并在客户端请求时将它们发出。
  • Access Token : 发送的每个请求,有效期一般是一个很短的寿命[例如:一个小时]
  • Refresh Token : 主要用于获取新的访问令牌,而不是每个请求都发送,通常比访问令牌生命更长。
在HTTPS个词 : 对于任何形式的安全实现,从基本身份验证到一个完全成熟的OAuth2实现,HTTPS是必须具备的。如果没有HTTPS,不管你的实现是什么,安全性都是容易受到损害的。
4. OAuth2用户访问令牌范围
客户端可以要求使用范围指定访问权限的资源[访问订阅与用户Facebook账户照片],授权服务器显示的访问权限实际上是授予客户端[只允许资源所有者订阅]。

让我们来看看代码
让我们来使用Spring Security实现必要安全过滤,才能进入REST资源来实现OAuth。
1.资源服务器

资源服务器承载资源[REST API],客户端感兴趣的资源位于  /user/ 。@EnableResourceServer注释,适用在OAuth2资源服务器,实现了Spring Security的过滤器验证的请求传入OAuth2令牌。 ResourceServerConfigurerAdapter类实现 ResourceServerConfigurer 提供的方法来调整 OAuth2安全保护的访问规则和路径。

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";@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.resourceId(RESOURCE_ID).stateless(false);}@Overridepublic 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.授权服务器
如果凭据 OK 授权服务器是一个负责验证凭据,提供令牌[刷新令牌以及访问令牌]。它还包含有关注册客户和访问范围以及授权类型的信息。 令牌存储用于存储令牌。我们将使用内存来存储令牌。@EnableAuthorizationServer使一个授权服务器(即,AuthorizationEndpoint和TokenEndpoint)在当前的应用程序上下文。
AuthorizationServerConfigurerAdapter类实现AuthorizationServerConfigurer它提供了所有必要的方法来配置一个授权服务器。
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";@Autowiredprivate TokenStore tokenStore;@Autowiredprivate UserApprovalHandler userApprovalHandler;@Autowired@Qualifier("authenticationManagerBean")private AuthenticationManager authenticationManager;@Overridepublic 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.}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler).authenticationManager(authenticationManager);}@Overridepublic void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {oauthServer.realm(REALM+"/client");}}

上面的配置:

  • 注册一个客户端,客户端ID是“my-trusted-client'和密码为'secret',客户端允许的角色和范围;
  • 指定任何生成的访问令牌的有效期只有120秒;
  • 指定任何刷新生成令牌的有效期只有600秒
3.安全配置
结合一切在一起。端点 /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 {@Autowiredprivate ClientDetailsService clientDetailsService;@Autowiredpublic void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("ADMIN").and().withUser("bob").password("abc123").roles("USER");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().anonymous().disable().authorizeRequests().antMatchers("/oauth/token").permitAll();}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic TokenStore tokenStore() {return new InMemoryTokenStore();}@Bean@Autowiredpublic TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();handler.setTokenStore(tokenStore);handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));handler.setClientDetailsService(clientDetailsService);return handler;}@Bean@Autowiredpublic 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 {@Autowiredprivate OAuth2SecurityConfiguration securityConfig;@Overrideprotected MethodSecurityExpressionHandler createExpressionHandler() {return new OAuth2MethodSecurityExpressionHandler();}
}
4.端点和它们的目的
  • 试图访问资源[REST API],但是这是在没有任何授权[失败]情况下。

    GET http://localhost:8080/SpringSecurityOAuth2Example/user/

  • 要求令牌[访问+刷新]使用HTTP POST 在 /oauth/token 上 ,grant_type=password和资源所有者凭证作为  req-params 。此外在授权头发送客户端凭据。

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

  • 要求通过有效的刷新令牌新的访问令牌,在 /oauth/token 上使用HTTP POST,以及 grant_type=refresh_token 一并发送刷新令牌。此外在授权头发送客户端凭据。

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

  • 使用 access_token 查询参数和要求提供访问资源的令牌。

    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 {@AutowiredUserService 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.运行应用程序
运行它,并使用两种不同的客户端进行测试。

客户端1: Postman

尝试不使用任何验证信息来直拉访问资源: http://localhost:8080/SpringSecurityOAuth2/user/,将得到401。

现在我们获取头。选择HTTP方法为 POST,Authorization Type:Basic Auth ,URL:http://localhost:8080/SpringSecurityOAuth2/oauth/token?grant_type=password&username=bill&password=abc123 ,然后再将客户端凭据 [my-trusted-client/secret]添加到授权头。点击"update request"(更新请求),发送POST请求后,您会在响应中收到访问令牌(access-token),以及刷新令牌(refresh-token)。如下所示 -

保存这些令牌在需要它们时。现在可以使用这个访问令牌[有效期为2分钟]来访问资源。现在我们再使用这个 token 来访问资源,把它添加到URL中如:http://localhost:8080/SpringSecurityOAuth2/user/?access_token=7fbb77ae-3d8f-4d78-b8de-3222353f680b 得到结果如下所示 -

2分钟后,访问令牌被过期,那么进一步的资源请求将失败。

我们需要一个新的访问令牌。触发一个 post 以后用刷新令牌来获得一个新的访问令牌。请求URL:http://localhost:8080/SpringSecurityOAuth2/oauth/token?grant_type=refresh_token&refresh_token=fefcf12c-2683-4f1a-a446-941666dcfe23

使用这个新的访问令牌(c8edfa2f-d2aa-4f1b-81e1-32df3fefe9a8)继续访问资源。把它添加到URL中如:http://localhost:8080/SpringSecurityOAuth2/user/?access_token=be5c7dec-ae17-403d-ab66-86cf5262f159 得到结果如下所示 -

新令牌(Refresh-token)也会过期[10分钟]。在这之后,您会看到刷新请求失败。

这意味着您需要刷新申请新的访问令牌,如第2步中。
客户端2:基于RestTemplate的Java应用程序
sendTokenRequest 方法用于获得实际令牌。访问令牌(access-token )我们从响应中获得了,之后将它应用到每个请求中。如果需要,您可以在下面的例子中很容易地实现 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);}
}
上面的代码会产生以下的输出:
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.0Testing getUser API----------
User [id=1, name=Sam, age=30, salary=70000.0]Testing create User API----------
Location : http://localhost:8080/SpringSecurityOAuth2Example/user/5Testing 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.0Testing 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.0Testing 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.0Testing all delete Users API----------Testing listAllUsers API-----------
No user exist----------

工程目录结构



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.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>SpringSecurityOAuth2</warName><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin></plugins><finalName>SpringSecurityOAuth2</finalName></build>
</project>

下载源代码

16 - SpringSecurityOAuth2.zip

参考

  • OAuth2 规范
  • Spring OAuth2 官方参考
  • Spring Security 4 工程主页
  • Spring Security 4 参考手册

spring oauth2相关推荐

  1. spring oauth2+JWT后端自动刷新access_token

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 |   Braska 来源 | cnblogs.com/br ...

  2. spring oauth2 的error_description本地提示中文,线上提示英文。

    spring oauth2 的error_description本地提示中文,线上提示英文. 自由的灵魂需要一颗勇敢的心,要勇敢的自发去成全自己. 解决方案 在yml或properties文件中进行如 ...

  3. spring oauth2学习笔记

    一 主要参考资料 https://echocow.cn/articles/2019/07/14/1563082088646.html 1.1 博客地址: echocow.cn[重点关注,对应的代码路径 ...

  4. spring oauth2 实现用户名密码登录、手机号验证码登录返回token

    文章目录 介绍 实现功能 用户名密码登录 步骤 编写成功处理器 配置成功处理器 手机号验证码登录 步骤 重写SmsCodeAuthenticationSecurityConfig 测试 用户名密码登录 ...

  5. 带有JWT示例的​​Spring Oauth2

    有时以前,我们发表了一篇文章,分享了一种在云环境中实现无状态会话的自定义方法. 今天,让我们探讨为Spring Boot应用程序设置Oauth2身份验证的另一个流行用例. 在此示例中,我们将使用JSO ...

  6. 芋道 spring security oauth2 入门_Spring官方宣布:新的Spring OAuth2.0授权服务器已经来了

    1. 前言 记不记得之前发过一篇文章Spring 官方发起Spring Authorization Server 项目.该项目是由Spring Security主导的一个社区驱动的.独立的孵化项目.由 ...

  7. Spring OAuth2 授权服务器配置详解

    前两篇文章分别体验了Spring Authorization Server的使用和讲解了其各个过滤器的作用.今天来讲讲Spring Authorization Server授权服务器的配置.强烈建议自 ...

  8. 实战讲解Spring Oauth2.0密码模式和授权码模式(内存inMemory+持久化jdbc配置)

    1 缘起 先吐槽, 在搜索关于Oauth2.0授权码方式认证时, 遇到的问题比较多,一句话,按照其分享的步骤一步一步来,最终,无法成功, 本想,抄近路,看一些前人分享的应用案例,直接使用, 近路不通, ...

  9. spring oauth2 social实现QQ登录

    文章目录 介绍 实现功能 开发步骤 开发流程图 引入jar包 数据库sql QQ用户信息实体类 QQapi 重写OAuth2Template 编写自己的QQServiceProvider 编写自己的Q ...

最新文章

  1. 2016-1-4作业
  2. 数据库事务隔离级别(转)
  3. spring ioc原理解析
  4. 番石榴的ListenableFuture
  5. 用C语言实现津巴布韦这道算法题?
  6. K8S精华问答 | 应用和运行时平台是怎样解耦的?
  7. Jeecg-Boot 快速开发平台,新手入门教程
  8. linux开发读取外部存储,Android 获取外接储存的设备路径(如挂载的U盘),android挂载...
  9. Shadow Mapping续
  10. matlab 浮动波动率,Matlab计算股票价格波动率
  11. 大数据Hadoop之——Cloudera Hadoop(CM 6.3.1+CDH 6.3.2环境部署)
  12. 一WAN多拨(一号多拨)实验
  13. 02. 禁止修改 IP 上网 ❀ 飞塔 (Fortinet5.4) 防火墙
  14. 通道注意力机制keras_在TensorFlow+Keras环境下使用RoI池化一步步实现注意力机制
  15. 苹果推iOS游戏手柄,掌上游戏主机格局变天?
  16. vue中实现Excel导入导出功能
  17. 【iOS沉思录】Objective-C语言消息传递机制三道防线:消息转发机制详解
  18. 药房管理系统源码 药房源码
  19. Fliqlo屏/保mac使用方法
  20. dbg-macro:C++代码调试小工具

热门文章

  1. LayaAir 2.12.2新版本已发布,即将进入3.0时代
  2. layaair发布html5,全面了解H5微信小游戏 LayaAir引擎支持一键发布
  3. 分类变量编码python处理
  4. 首次公开,阿里技术团队编写的“大厂面试参考指南”v1.0版本
  5. 做人,少记仇,多记好
  6. MDT 2013 从入门到精通之AD域环境准备
  7. Linux命令之dhclient,Linux中dhclient命令起什么作用呢?
  8. php后门绕过eval关键字,一些变态的PHP一句话后门收集
  9. 利用cmd执行adb(os.popen、os.system)
  10. css修改图片形状,用css绘制各种形状