You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

README.adoc 8.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. = spring-boot-demo-oauth-authorization-server
  2. Doc Writer <lzy@echocow.cn>
  3. v1.0, 2019-01-07
  4. :toc:
  5. spring boot oauth2 授权服务器,
  6. - 授权码模式、密码模式、刷新令牌
  7. - 自定义 UserDetailService
  8. - 自定义 ClientDetailService
  9. - jwt 非对称加密
  10. - 自定义登录授权页面
  11. > SQL 语句
  12. >
  13. > - DDL: `src/test/resources/schema.sql`
  14. > - DML: `src/test/resources/import.sql`
  15. 测试用例使用 h2 数据库,测试数据如下:
  16. .测试客户端
  17. |===
  18. |客户端 id |客户端密钥 |资源服务器名称 |授权类型 | scopes| 回调地址
  19. |oauth2
  20. |oauth2
  21. |oauth2
  22. |authorization_code,password,refresh_token
  23. |READ,WRITE
  24. |http://example.com
  25. |test
  26. |oauth2
  27. |oauth2
  28. |authorization_code,password,refresh_token
  29. |READ
  30. |http://example.com
  31. |error
  32. |oauth2
  33. |test
  34. |authorization_code,password,refresh_token
  35. |READ
  36. |http://example.com
  37. |===
  38. .测试用户
  39. |===
  40. |用户名 |密码 |角色
  41. |admin
  42. |123456
  43. |ROLE_ADMIN
  44. |test
  45. |123456
  46. |ROLE_TEST
  47. |===
  48. == 授权码模式
  49. > 测试用例:`com.xkcoding.oauth.oauth.AuthorizationCodeGrantTests`
  50. === 获取授权码
  51. - 请求地址: http://localhost:8080/oauth/authorize?response_type=code&client_id=oauth2&redirect_uri=http://example.com&scope=READ
  52. - 用户名:admin
  53. - 密码:123456
  54. image::image/Login.png[login]
  55. === 确认授权
  56. 登录成功以后,进入确认授权页面。已经确认过的用户,不会再次要求确认。
  57. image::image/Confirm.png[confirm]
  58. 确认授权后,获取授权码
  59. image::image/Code.png[code]
  60. === 请求 token
  61. 使用以下代码可以直接请求 token
  62. [shell]
  63. ----
  64. curl --location --request POST 'http://127.0.0.1:8080/oauth/token' \
  65. --header 'Content-Type: application/x-www-form-urlencoded' \
  66. --header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
  67. --data-urlencode 'grant_type=authorization_code' \
  68. --data-urlencode 'code=GgX6QD' \
  69. --data-urlencode 'redirect_uri=http://example.com' \
  70. --data-urlencode 'client_id=oauth2' \
  71. --data-urlencode 'scope=READ WRITE'
  72. ----
  73. 得到 token
  74. [token]
  75. ----
  76. {
  77. "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiUkVBRCJdLCJleHAiOjE1NzgzODY4MTYsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZjAyMDhiNTUtYTJjYS00NjI4LTg5YjEtNzI5MzY4MzAxOWNhIiwiY2xpZW50X2lkIjoib2F1dGgyIn0.RqJpsin6bMnwI57cGpODTplLeW_gtNWHo_l4SimyRLsnxpCWm5oY1EOb4qVHpXvCbhNsUj69D462P7le13OOmexysZIQhaoGZ_CbIlEp63XsCnr5nSKeX3dgQlyTUDjOUL0WUtY2lKqLCGMeX_rpVhfmSh3b7MC0Ntxq5ao-943QMXGRIeRvJgSkvfY2HBN6-zx1H6rE0wxnUfBC1M08kUkFYlSmsFchiz-E_oTzJvE2D8lA9g-eEFU6cZ_els4Q77Vvc_O6SXUZ7o65vFyLyUjLvh9QF1825SGIUUdXTUYSZjnSAXChhRIAT5pLRHK-gthIzpOaWrgj6ebUoG02Eg",
  78. "token_type": "bearer",
  79. "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiUkVBRCJdLCJhdGkiOiJmMDIwOGI1NS1hMmNhLTQ2MjgtODliMS03MjkzNjgzMDE5Y2EiLCJleHAiOjE1NzgzODY4MTYsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiMGViNTU2MTQtYjgxYS00MTFmLTg1MTAtZThkMjZmODJmMjJhIiwiY2xpZW50X2lkIjoib2F1dGgyIn0.CBGcjirkf-3187SgbZr0ikauiCS8U9YLaoR4sNlRQjd-gaIeF5PChnIs_yAmG_VpqPFlPRdSl8DA05S2QnFpT3TkRjyP-LPDZgsVAPfczMAdVywU1zOKYZeq-gM6p9bmGEabbZoBlIxOImsjeyFSCui6UtRTZjNlj3AhGIzvs52T8bDqC796iHPDZvJ97MMgsEiRyu-mxDm1o1LMuBX9RHCx9rAkBVf52q36bqWMcYAlDOu1wYjpmhalSLZyWcmraQvClEitXGJI4eTFapTnuXQuWFIL-973V_5Shw98-bk65zZQOEheazHrUf-n4h-sYT4akehnYSVxX2UIg9XsCw",
  80. "expires_in": 5999,
  81. "scope": "READ",
  82. "jti": "f0208b55-a2ca-4628-89b1-7293683019ca"
  83. }
  84. ----
  85. == 密码模式
  86. > 测试用例:`com.xkcoding.oauth.oauth.ResourceOwnerPasswordGrantTests`
  87. `test` 用户进行授权
  88. [source]
  89. ----
  90. curl --location --request POST 'http://127.0.0.1:8080/oauth/token' \
  91. --header 'Content-Type: application/x-www-form-urlencoded' \
  92. --header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
  93. --data-urlencode 'password=123456' \
  94. --data-urlencode 'username=test' \
  95. --data-urlencode 'grant_type=password' \
  96. --data-urlencode 'scope=READ WRITE'
  97. ----
  98. == 刷新令牌
  99. 携带 `refresh_token` 去请求
  100. [source]
  101. ----
  102. curl --location --request POST 'http://127.0.0.1:8080/oauth/token' \
  103. --header 'Content-Type: application/x-www-form-urlencoded' \
  104. --header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
  105. --data-urlencode 'grant_type=refresh_token' \
  106. --data-urlencode 'refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiUkVBRCJdLCJhdGkiOiJmMDIwOGI1NS1hMmNhLTQ2MjgtODliMS03MjkzNjgzMDE5Y2EiLCJleHAiOjE1NzgzODY4MTYsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiMGViNTU2MTQtYjgxYS00MTFmLTg1MTAtZThkMjZmODJmMjJhIiwiY2xpZW50X2lkIjoib2F1dGgyIn0.CBGcjirkf-3187SgbZr0ikauiCS8U9YLaoR4sNlRQjd-gaIeF5PChnIs_yAmG_VpqPFlPRdSl8DA05S2QnFpT3TkRjyP-LPDZgsVAPfczMAdVywU1zOKYZeq-gM6p9bmGEabbZoBlIxOImsjeyFSCui6UtRTZjNlj3AhGIzvs52T8bDqC796iHPDZvJ97MMgsEiRyu-mxDm1o1LMuBX9RHCx9rAkBVf52q36bqWMcYAlDOu1wYjpmhalSLZyWcmraQvClEitXGJI4eTFapTnuXQuWFIL-973V_5Shw98-bk65zZQOEheazHrUf-n4h-sYT4akehnYSVxX2UIg9XsCw'
  107. ----
  108. == 解析令牌
  109. 携带令牌解析
  110. [source]
  111. ----
  112. curl --location --request POST 'http://127.0.0.1:8080/oauth/check_token' \
  113. --header 'Content-Type: application/x-www-form-urlencoded' \
  114. --header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
  115. --data-urlencode 'token='
  116. ----
  117. 解析结果
  118. [source]
  119. ----
  120. {
  121. "aud": [
  122. "oauth2"
  123. ],
  124. "user_name": "admin",
  125. "scope": [
  126. "READ",
  127. "WRITE"
  128. ],
  129. "active": true,
  130. "exp": 1578389936,
  131. "authorities": [
  132. "ROLE_ADMIN"
  133. ],
  134. "jti": "fe59fce9-6764-435e-8fa7-7320e11af811",
  135. "client_id": "oauth2"
  136. }
  137. ----
  138. == 退出登录
  139. 授权码模式登陆是在授权服务器上登录的,所以退出也要在授权服务器上退出。
  140. 携带回调地址进行退出,退出完成后跳转到回调地址:
  141. image::image/Logout.png[logout]
  142. 退出以后自动跳转到回调地址(要加 `http` 或 `https`)
  143. == 获取公钥
  144. 通过访问 '/oauth/token_key' 获取 JWT 公钥
  145. [source]
  146. ----
  147. curl --location --request GET 'http://127.0.0.1:8080/oauth/token_key' \
  148. --header 'Content-Type: application/x-www-form-urlencoded' \
  149. --header 'Authorization: Basic b2F1dGgyOm9hdXRoMg=='
  150. ----
  151. 获取后
  152. [source]
  153. ----
  154. {
  155. "alg": "SHA256withRSA",
  156. "value": "-----BEGIN PUBLIC KEY-----\n......\n-----END PUBLIC KEY-----"
  157. }
  158. ----
  159. == 核心配置
  160. === 授权服务器配置
  161. [Oauth2AuthorizationServerConfig]
  162. ----
  163. @Override
  164. public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
  165. endpoints.authenticationManager(authenticationManager)
  166. // 自定义用户
  167. .userDetailsService(sysUserService)
  168. // 内存存储
  169. .tokenStore(tokenStore)
  170. // jwt 令牌转换
  171. .accessTokenConverter(jwtAccessTokenConverter);
  172. }
  173. @Override
  174. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  175. // 从数据库读取我们自定义的客户端信息
  176. clients.withClientDetails(sysClientDetailsService);
  177. }
  178. @Override
  179. public void configure(AuthorizationServerSecurityConfigurer security) {
  180. security
  181. // 获取 token key 需要进行 basic 认证客户端信息
  182. .tokenKeyAccess("isAuthenticated()")
  183. // 获取 token 信息同样需要 basic 认证客户端信息
  184. .checkTokenAccess("isAuthenticated()");
  185. }
  186. ----
  187. === 安全配置
  188. [WebSecurityConfig]
  189. ----
  190. @Override
  191. protected void configure(HttpSecurity http) throws Exception {
  192. http
  193. // 开启表单登录,授权码模式的时候进行登录
  194. .formLogin()
  195. // 路径等
  196. .loginPage("/oauth/login")
  197. .loginProcessingUrl("/authorization/form")
  198. // 失败以后携带错误信息进行再次跳转登录页面
  199. .failureHandler(clientLoginFailureHandler)
  200. .and()
  201. // 退出登录相关
  202. .logout()
  203. .logoutUrl("/oauth/logout")
  204. .logoutSuccessHandler(clientLogoutSuccessHandler)
  205. .and()
  206. // 授权服务器安全配置
  207. .authorizeRequests()
  208. .antMatchers("/oauth/**").permitAll()
  209. .anyRequest()
  210. .authenticated();
  211. }
  212. ----
  213. == 参考
  214. - https://echocow.cn/articles/2019/07/14/1563096109754.html[Spring Security Oauth2 从零到一完整实践(三)授权服务器 ]