oauth-resource-server.adoc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. [[recipe-creating-an-oauth-resource-server]]
  2. = Recipe: Creating an OAuth Resource Server
  3. NOTE: We use "OAuth" and "OAuth2" interchangeably. Spring Security has moved away from the first version of OAuth.
  4. The previous recipe, <<oauth-client-recipe>>, detailed how to create a client that is secured by OAuth2.
  5. To really understand this recipe, you should probably read the <<understanding-oauth>> section within that recipe.
  6. This recipe describes how to create an OAuth2 Resource Server.
  7. According to the https://tools.ietf.org/html/rfc6749[RFC that defines OAuth2], "In OAuth, the client requests access to resources controlled by the resource owner and hosted by the resource server."
  8. In other words, https://www.oauth.com/oauth2-servers/the-resource-server/[as the folks at oauth.com write], an OAuth2 "resource server handles authenticated requests after the application has obtained an access token."
  9. NOTE: The code and much of the description for this recipe comes from the Spring Security samples at https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2resourceserver.
  10. You can find many other https://github.com/spring-projects/spring-security/tree/master/samples[Spring Security samples] in that project.
  11. == OAuth Resources
  12. OAuth2 is defined by https://tools.ietf.org/html/rfc6749[IETF RFC (Request for Comment) 6749].
  13. Two highly regarded and closely related web sites offer more detail.
  14. Those sites are https://oauth.net/ and https://oauth.com/.
  15. https://oauth.net/ is organized as a wiki. https://oauth.com/ is organized as a book.
  16. Both are worth reading if you need to understand OAuth2 in depth.
  17. == Writing the Resource Server
  18. To begin, you need a build file. In this case, we use a `build.gradle` file, as follows:
  19. ====
  20. [source,java]
  21. ----
  22. apply plugin: 'io.spring.convention.spring-sample-boot'
  23. dependencies {
  24. compile project(':spring-security-config')
  25. compile project(':spring-security-oauth2-jose')
  26. compile project(':spring-security-oauth2-resource-server')
  27. compile 'org.springframework.boot:spring-boot-starter-web'
  28. compile 'com.squareup.okhttp3:mockwebserver'
  29. testCompile project(':spring-security-test')
  30. testCompile 'org.springframework.boot:spring-boot-starter-test'
  31. }
  32. ----
  33. ====
  34. Then you can create a Spring Boot application class, as follows:
  35. ====
  36. [source,java]
  37. ----
  38. package sample;
  39. import org.springframework.boot.SpringApplication;
  40. import org.springframework.boot.autoconfigure.SpringBootApplication;
  41. /**
  42. * @author Josh Cummings
  43. */
  44. @SpringBootApplication
  45. public class OAuth2ResourceServerApplication {
  46. public static void main(String[] args) {
  47. SpringApplication.run(OAuth2ResourceServerApplication.class, args);
  48. }
  49. }
  50. ----
  51. ====
  52. The application does nothing until you add more classes.
  53. In this case, we need two more classes:
  54. * `OAuth2ResourceServerSecurityConfiguration` to hold the configuration.
  55. * `OAuth2ResourceServerController` to handle requests.
  56. The following listing shows the `OAuth2ResourceServerSecurityConfiguration` class:
  57. ====
  58. [source,java]
  59. ----
  60. package sample;
  61. import org.springframework.beans.factory.annotation.Value;
  62. import org.springframework.context.annotation.Bean;
  63. import org.springframework.http.HttpMethod;
  64. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  65. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  66. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  67. import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
  68. import org.springframework.security.oauth2.jwt.JwtDecoder;
  69. import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
  70. /**
  71. * @author Josh Cummings
  72. */
  73. @EnableWebSecurity
  74. public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
  75. @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}") String jwkSetUri;
  76. @Override
  77. protected void configure(HttpSecurity http) throws Exception {
  78. // @formatter:off
  79. http
  80. .authorizeRequests((authorizeRequests) ->
  81. authorizeRequests
  82. .antMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message:read")
  83. .antMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message:write")
  84. .anyRequest().authenticated()
  85. )
  86. .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
  87. // @formatter:on
  88. }
  89. @Bean
  90. JwtDecoder jwtDecoder() {
  91. return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
  92. }
  93. }
  94. ----
  95. ====
  96. The following listing shows the `OAuth2ResourceServerController` class:
  97. ====
  98. [source,java]
  99. ----
  100. package sample;
  101. import org.springframework.security.core.annotation.AuthenticationPrincipal;
  102. import org.springframework.security.oauth2.jwt.Jwt;
  103. import org.springframework.web.bind.annotation.GetMapping;
  104. import org.springframework.web.bind.annotation.PostMapping;
  105. import org.springframework.web.bind.annotation.RequestBody;
  106. import org.springframework.web.bind.annotation.RestController;
  107. /**
  108. * @author Josh Cummings
  109. */
  110. @RestController
  111. public class OAuth2ResourceServerController {
  112. @GetMapping("/")
  113. public String index(@AuthenticationPrincipal Jwt jwt) {
  114. return String.format("Hello, %s!", jwt.getSubject());
  115. }
  116. @GetMapping("/message")
  117. public String message() {
  118. return "secret message";
  119. }
  120. @PostMapping("/message")
  121. public String createMessage(@RequestBody String message) {
  122. return String.format("Message was created. Content: %s", message);
  123. }
  124. }
  125. ----
  126. ====
  127. == Running the Resource Server
  128. The application class lets you run the resource server with the following command (provided you run it from the directory that holds the build file):
  129. ====
  130. [source,bash]
  131. ----
  132. ./gradlew bootRun
  133. ----
  134. ====
  135. Once the application is running, you can define a token for it to use, as follows:
  136. ====
  137. [source,bash]
  138. ----
  139. export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQ1ODgwLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiMDFkOThlZWEtNjc0MC00OGRlLTk4ODAtYzM5ZjgyMGZiNzVlIiwiY2xpZW50X2lkIjoibm9zY29wZXMiLCJzY29wZSI6WyJub25lIl19.VOzgGLOUuQ_R2Ur1Ke41VaobddhKgUZgto7Y3AGxst7SuxLQ4LgWwdSSDRx-jRvypjsCgYPbjAYLhn9nCbfwtCitkymUKUNKdebvVAI0y8YvliWTL5S-GiJD9dN8SSsXUla9A4xB_9Mt5JAlRpQotQSCLojVSKQmjhMpQWmYAlKVjnlImoRwQFPI4w3Ijn4G4EMTKWUYRfrD0-WNT9ZYWBeza6QgV6sraP7ToRB3eQLy2p04cU40X-RHLeYCsMBfxsMMh89CJff-9tn7VDKi1hAGc_Lp9yS9ZaItJuFJTjf8S_vsjVB1nBhvdS_6IED_m_fOU52KiGSO2qL6shxHvg
  140. ----
  141. ====
  142. Then you can use curl to make a request, as follows:
  143. ====
  144. [source,bash]
  145. ----
  146. curl -H "Authorization: Bearer $TOKEN" localhost:8080
  147. ----
  148. ====
  149. The application responds as follows:
  150. ====
  151. [source,bash]
  152. ----
  153. Hello, subject!
  154. ----
  155. ====
  156. `subject` is the value of the `sub` field in the JWT returned by the Authorization Server.
  157. === Handling GET Requests
  158. You can make the resource server handle get request by using a different token.
  159. To do, set the token as follows:
  160. ====
  161. [source,bash]
  162. ----
  163. export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQ1NjQ4LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiY2I1ZGMwNDYtMDkyMi00ZGJmLWE5MzAtOGI2M2FhZTYzZjk2IiwiY2xpZW50X2lkIjoicmVhZGVyIiwic2NvcGUiOlsibWVzc2FnZTpyZWFkIl19.Pre2ksnMiOGYWQtuIgHB0i3uTnNzD0SMFM34iyQJHK5RLlSjge08s9qHdx6uv5cZ4gZm_cB1D6f4-fLx76bCblK6mVcabbR74w_eCdSBXNXuqG-HNrOYYmmx5iJtdwx5fXPmF8TyVzsq_LvRm_LN4lWNYquT4y36Tox6ZD3feYxXvHQ3XyZn9mVKnlzv-GCwkBohCR3yPow5uVmr04qh_al52VIwKMrvJBr44igr4fTZmzwRAZmQw5rZeyep0b4nsCjadNcndHtMtYKNVuG5zbDLsB7GGvilcI9TDDnUXtwthB_3iq32DAd9x8wJmJ5K8gmX6GjZFtYzKk_zEboXoQ
  164. ----
  165. ====
  166. Then you can use curl to make a GET request, as follows:
  167. ====
  168. [source,bash]
  169. ----
  170. curl -H "Authorization: Bearer $TOKEN" localhost:8080/message
  171. ----
  172. ====
  173. The resource server responds as follows:
  174. ====
  175. [source,bash]
  176. ----
  177. secret message
  178. ----
  179. ====
  180. === Handling POST Requests
  181. You can make the resource server handle get request by using a different token.
  182. To do, set the token as follows:
  183. ====
  184. [source,bash]
  185. ----
  186. export TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJqZWN0IiwiZXhwIjoyMTY0MjQzOTA0LCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiZGI4ZjgwMzQtM2VlNy00NjBjLTk3NTEtMDJiMDA1OWI5NzA4IiwiY2xpZW50X2lkIjoid3JpdGVyIiwic2NvcGUiOlsibWVzc2FnZTp3cml0ZSJdfQ.USvpx_ntKXtchLmc93auJq0qSav6vLm4B7ItPzhrDH2xmogBP35eKeklwXK5GCb7ck1aKJV5SpguBlTCz0bZC1zAWKB6gyFIqedALPAran5QR-8WpGfl0wFqds7d8Jw3xmpUUBduRLab9hkeAhgoVgxevc8d6ITM7kRnHo5wT3VzvBU8DquedVXm5fbBnRPgG4_jOWJKbqYpqaR2z2TnZRWh3CqL82Orh1Ww1dJYF_fae1dTVV4tvN5iSndYcGxMoBaiw3kRRi6EyNxnXnt1pFtZqc1f6D9x4AHiri8_vpBp2vwG5OfQD5-rrleP_XlIB3rNQT7tu3fiqu4vUzQaEg
  187. ----
  188. ====
  189. Then you can use curl to make a POST request, as follows:
  190. ====
  191. [source,bash]
  192. ----
  193. curl -H "Authorization: Bearer $TOKEN" -d "my message" localhost:8080/message
  194. ----
  195. ====
  196. The resource server responds as follows:
  197. ====
  198. [source,bash]
  199. ----
  200. Message was created. Content: my message
  201. ----
  202. ====
  203. If you want to see more ways of running this resource server, The https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2resourceserver[Spring Security sample from which this code comes] has both integration and unit tests.