Skip to content

Commit dcfeb4f

Browse files
committed
✨ Add spring-boot-demo-oauth-authorization-server.
1 parent 0378ad0 commit dcfeb4f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2256
-23
lines changed

pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@
8686
<user.agent.version>1.20</user.agent.version>
8787
</properties>
8888

89+
<repositories>
90+
<repository>
91+
<id>aliyun</id>
92+
<name>aliyun</name>
93+
<url>https://maven.aliyun.com/repository/public</url>
94+
<releases>
95+
<enabled>true</enabled>
96+
</releases>
97+
<snapshots>
98+
<enabled>false</enabled>
99+
</snapshots>
100+
</repository>
101+
</repositories>
102+
89103
<dependencyManagement>
90104
<dependencies>
91105
<dependency>

spring-boot-demo-oauth/pom.xml

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55

66
<artifactId>spring-boot-demo-oauth</artifactId>
77
<version>1.0.0-SNAPSHOT</version>
8-
<packaging>jar</packaging>
8+
<modules>
9+
<module>spring-boot-demo-oauth-authorization-server</module>
10+
</modules>
11+
<packaging>pom</packaging>
912

1013
<name>spring-boot-demo-oauth</name>
1114
<description>Demo project for Spring Boot</description>
@@ -28,10 +31,49 @@
2831
<artifactId>spring-boot-starter-web</artifactId>
2932
</dependency>
3033

34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-starter-security</artifactId>
37+
</dependency>
38+
39+
<dependency>
40+
<groupId>org.springframework.boot</groupId>
41+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
42+
</dependency>
43+
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot-starter-data-jpa</artifactId>
47+
</dependency>
48+
49+
<dependency>
50+
<groupId>org.springframework.security.oauth.boot</groupId>
51+
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
52+
<version>${spring.boot.version}</version>
53+
</dependency>
54+
55+
<dependency>
56+
<groupId>mysql</groupId>
57+
<artifactId>mysql-connector-java</artifactId>
58+
<scope>runtime</scope>
59+
</dependency>
60+
61+
<dependency>
62+
<groupId>com.h2database</groupId>
63+
<artifactId>h2</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
3167
<dependency>
3268
<groupId>org.springframework.boot</groupId>
3369
<artifactId>spring-boot-starter-test</artifactId>
3470
<scope>test</scope>
71+
<exclusions>
72+
<exclusion>
73+
<groupId>junit</groupId>
74+
<artifactId>junit</artifactId>
75+
</exclusion>
76+
</exclusions>
3577
</dependency>
3678

3779
<dependency>
@@ -44,6 +86,14 @@
4486
<artifactId>lombok</artifactId>
4587
<optional>true</optional>
4688
</dependency>
89+
90+
<dependency>
91+
<groupId>org.junit.jupiter</groupId>
92+
<artifactId>junit-jupiter</artifactId>
93+
<version>5.5.2</version>
94+
<scope>test</scope>
95+
</dependency>
96+
4797
</dependencies>
4898

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

0 commit comments

Comments
 (0)