Are you sure you want to delete this task? Once this task is deleted, it cannot be recovered.
EchoCow 93a9b8b0d9 | 4 years ago | |
---|---|---|
.. | ||
image | 4 years ago | |
src | 4 years ago | |
README.adoc | 4 years ago | |
pom.xml | 4 years ago |
= spring-boot-demo-oauth-authorization-server
Doc Writer <lzy@echocow.cn>
v1.0, 2019-01-07
:toc:
spring boot oauth2 授权服务器,
- 授权码模式、密码模式、刷新令牌
- 自定义 UserDetailService
- 自定义 ClientDetailService
- jwt 非对称加密
- 自定义登录授权页面
> SQL 语句
>
> - DDL: `src/test/resources/schema.sql`
> - DML: `src/test/resources/import.sql`
测试用例使用 h2 数据库,测试数据如下:
.测试客户端
|===
|客户端 id |客户端密钥 |资源服务器名称 |授权类型 | scopes| 回调地址
|oauth2
|oauth2
|oauth2
|authorization_code,password,refresh_token
|READ,WRITE
|http://example.com
|test
|oauth2
|oauth2
|authorization_code,password,refresh_token
|READ
|http://example.com
|error
|oauth2
|test
|authorization_code,password,refresh_token
|READ
|http://example.com
|===
.测试用户
|===
|用户名 |密码 |角色
|admin
|123456
|ROLE_ADMIN
|test
|123456
|ROLE_TEST
|===
== 授权码模式
> 测试用例:`com.xkcoding.oauth.oauth.AuthorizationCodeGrantTests`
=== 获取授权码
- 请求地址: http://localhost:8080/oauth/authorize?response_type=code&client_id=oauth2&redirect_uri=http://example.com&scope=READ
- 用户名:admin
- 密码:123456
image::image/Login.png[login]
=== 确认授权
登录成功以后,进入确认授权页面。已经确认过的用户,不会再次要求确认。
image::image/Confirm.png[confirm]
确认授权后,获取授权码
image::image/Code.png[code]
=== 请求 token
使用以下代码可以直接请求 token
[shell]
----
curl --location --request POST 'http://127.0.0.1:8080/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=GgX6QD' \
--data-urlencode 'redirect_uri=http://example.com' \
--data-urlencode 'client_id=oauth2' \
--data-urlencode 'scope=READ WRITE'
----
得到 token
[token]
----
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiUkVBRCJdLCJleHAiOjE1NzgzODY4MTYsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiZjAyMDhiNTUtYTJjYS00NjI4LTg5YjEtNzI5MzY4MzAxOWNhIiwiY2xpZW50X2lkIjoib2F1dGgyIn0.RqJpsin6bMnwI57cGpODTplLeW_gtNWHo_l4SimyRLsnxpCWm5oY1EOb4qVHpXvCbhNsUj69D462P7le13OOmexysZIQhaoGZ_CbIlEp63XsCnr5nSKeX3dgQlyTUDjOUL0WUtY2lKqLCGMeX_rpVhfmSh3b7MC0Ntxq5ao-943QMXGRIeRvJgSkvfY2HBN6-zx1H6rE0wxnUfBC1M08kUkFYlSmsFchiz-E_oTzJvE2D8lA9g-eEFU6cZ_els4Q77Vvc_O6SXUZ7o65vFyLyUjLvh9QF1825SGIUUdXTUYSZjnSAXChhRIAT5pLRHK-gthIzpOaWrgj6ebUoG02Eg",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiUkVBRCJdLCJhdGkiOiJmMDIwOGI1NS1hMmNhLTQ2MjgtODliMS03MjkzNjgzMDE5Y2EiLCJleHAiOjE1NzgzODY4MTYsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiMGViNTU2MTQtYjgxYS00MTFmLTg1MTAtZThkMjZmODJmMjJhIiwiY2xpZW50X2lkIjoib2F1dGgyIn0.CBGcjirkf-3187SgbZr0ikauiCS8U9YLaoR4sNlRQjd-gaIeF5PChnIs_yAmG_VpqPFlPRdSl8DA05S2QnFpT3TkRjyP-LPDZgsVAPfczMAdVywU1zOKYZeq-gM6p9bmGEabbZoBlIxOImsjeyFSCui6UtRTZjNlj3AhGIzvs52T8bDqC796iHPDZvJ97MMgsEiRyu-mxDm1o1LMuBX9RHCx9rAkBVf52q36bqWMcYAlDOu1wYjpmhalSLZyWcmraQvClEitXGJI4eTFapTnuXQuWFIL-973V_5Shw98-bk65zZQOEheazHrUf-n4h-sYT4akehnYSVxX2UIg9XsCw",
"expires_in": 5999,
"scope": "READ",
"jti": "f0208b55-a2ca-4628-89b1-7293683019ca"
}
----
== 密码模式
> 测试用例:`com.xkcoding.oauth.oauth.ResourceOwnerPasswordGrantTests`
`test` 用户进行授权
[source]
----
curl --location --request POST 'http://127.0.0.1:8080/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
--data-urlencode 'password=123456' \
--data-urlencode 'username=test' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'scope=READ WRITE'
----
== 刷新令牌
携带 `refresh_token` 去请求
[source]
----
curl --location --request POST 'http://127.0.0.1:8080/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiUkVBRCJdLCJhdGkiOiJmMDIwOGI1NS1hMmNhLTQ2MjgtODliMS03MjkzNjgzMDE5Y2EiLCJleHAiOjE1NzgzODY4MTYsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iXSwianRpIjoiMGViNTU2MTQtYjgxYS00MTFmLTg1MTAtZThkMjZmODJmMjJhIiwiY2xpZW50X2lkIjoib2F1dGgyIn0.CBGcjirkf-3187SgbZr0ikauiCS8U9YLaoR4sNlRQjd-gaIeF5PChnIs_yAmG_VpqPFlPRdSl8DA05S2QnFpT3TkRjyP-LPDZgsVAPfczMAdVywU1zOKYZeq-gM6p9bmGEabbZoBlIxOImsjeyFSCui6UtRTZjNlj3AhGIzvs52T8bDqC796iHPDZvJ97MMgsEiRyu-mxDm1o1LMuBX9RHCx9rAkBVf52q36bqWMcYAlDOu1wYjpmhalSLZyWcmraQvClEitXGJI4eTFapTnuXQuWFIL-973V_5Shw98-bk65zZQOEheazHrUf-n4h-sYT4akehnYSVxX2UIg9XsCw'
----
== 解析令牌
携带令牌解析
[source]
----
curl --location --request POST 'http://127.0.0.1:8080/oauth/check_token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic b2F1dGgyOm9hdXRoMg==' \
--data-urlencode 'token='
----
解析结果
[source]
----
{
"aud": [
"oauth2"
],
"user_name": "admin",
"scope": [
"READ",
"WRITE"
],
"active": true,
"exp": 1578389936,
"authorities": [
"ROLE_ADMIN"
],
"jti": "fe59fce9-6764-435e-8fa7-7320e11af811",
"client_id": "oauth2"
}
----
== 退出登录
授权码模式登陆是在授权服务器上登录的,所以退出也要在授权服务器上退出。
携带回调地址进行退出,退出完成后跳转到回调地址:
image::image/Logout.png[logout]
退出以后自动跳转到回调地址(要加 `http` 或 `https`)
== 获取公钥
通过访问 '/oauth/token_key' 获取 JWT 公钥
[source]
----
curl --location --request GET 'http://127.0.0.1:8080/oauth/token_key' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic b2F1dGgyOm9hdXRoMg=='
----
获取后
[source]
----
{
"alg": "SHA256withRSA",
"value": "-----BEGIN PUBLIC KEY-----\n......\n-----END PUBLIC KEY-----"
}
----
== 核心配置
=== 授权服务器配置
[Oauth2AuthorizationServerConfig]
----
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager)
// 自定义用户
.userDetailsService(sysUserService)
// 内存存储
.tokenStore(tokenStore)
// jwt 令牌转换
.accessTokenConverter(jwtAccessTokenConverter);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 从数据库读取我们自定义的客户端信息
clients.withClientDetails(sysClientDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security
// 获取 token key 需要进行 basic 认证客户端信息
.tokenKeyAccess("isAuthenticated()")
// 获取 token 信息同样需要 basic 认证客户端信息
.checkTokenAccess("isAuthenticated()");
}
----
=== 安全配置
[WebSecurityConfig]
----
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 开启表单登录,授权码模式的时候进行登录
.formLogin()
// 路径等
.loginPage("/oauth/login")
.loginProcessingUrl("/authorization/form")
// 失败以后携带错误信息进行再次跳转登录页面
.failureHandler(clientLoginFailureHandler)
.and()
// 退出登录相关
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(clientLogoutSuccessHandler)
.and()
// 授权服务器安全配置
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.anyRequest()
.authenticated();
}
----
== 参考
- https://echocow.cn/articles/2019/07/14/1563096109754.html[Spring Security Oauth2 从零到一完整实践(三)授权服务器 ]
一个用来深度学习并实战 spring boot 的项目,目前总共包含 66 个集成demo,已经完成 55 个。
Java SVG CSS JavaScript SQL other