基础知识:
权限注解概要:
@EnableGlobalMethodSecurity(prePostEnabled = true,jsr250Enabled = true,securedEnabled = true)
prePostEnabled :启用@PreAuthorize
jsr250Enabled :启用@RolesAllowed
securedEnabled :启用@Secured
注意事项:@Secured对应的角色必须要有ROLE_前缀!
OAuth2个人理解
OAuth2 :主要用于搭建开放平台,client_id和client_secret相当于AppId和AppSecret。比如微信开放平台、支付宝开放平台,用户接入后,可得到username、password、appId、appSecret。
OAuth2之客户端模式:利用appId和appSecret向OAuth2-server认证授权服务器发送请求,获取Access Token,申请获取访问资源服务器的权限。
OAuth2之密码模式:利用appId和appSecre,再加上用户名和密码,向OAuth2-server认证授权服务器发送请求,获取Access Token,申请获取访问资源服务器的权限。
因为,密码模式获取到的Access Token包含了用户信息,而客户端模式,并不包含,所以,资源服务器仅能粗粒度控制权限(这里的资源指整个项目),而密码模式则能利用用户信息,认证成功后(此处的认证指的是对于Access Token校验是否拥有对于访问的资源服务器的权限)进一步利用Spring-Security框架的注解和OAuth2的注解进行接口甚至于方法级别的细粒度的控制。
spring-oauth-server 数据库表说明,参考链接
注意事项:本项目只用到了oauth_client_details表和自定义的member表,用户权限已写死为ROLE_MEMBER。
oauth_client_details表关键字段说明
字段 | 说明 |
---|---|
resource_ids | 资源ID标识 |
client_id | 相当于AppId |
client_secret | 相当于AppSecret |
authorities | 访问资源所需要的权限 |
scope | 指定客户端申请的权限范围,可选值包括read,write,trust;若有多个权限范围用逗号(,)分隔,如: "read,write". |
技术架构:
Spring Boot + Spring Security Oauth2 + Mysql + Redis
Mysql:存储 ClientDetails 和 UserDetails 信息
Redis:存储 AccessToken、RefreshToken、Authentication等凭证信息。
注意:OAuth2的Access Token的存储可以多种多样,比如数据库、缓存、内存甚至是无存储(即JWT实现),本项目使用的是Redis,后续将继续JWT的实现。
项目结构:
OAuth2的认证授权服务器:oauth2-server,简称OS,提供 /oauth/token 接口获取Access Token
OAuth2的资源服务器:oauth2-resource,简称OR, 对Access Token进行权限检测控制
OAuth2的客户端:oauth2-client,简称OC, 调用 spring-security-oauth2提供的封装代码,向OS获取AccessToken。
实践是检验真理的唯一标准
1.1、获取Access Token操作步骤如下:
1、导入初始化数据,2个表:member和oauth_client_details表数据,数据库和redis服务请先启动或者改成自己本地的。
2、启动oauth2-server、oauth2-client、oauth2-client服务
3、输入oauth2-client服务的swagger2地址,http://localhost:8052/swagger-ui.html
4、使用案例数据,发起调用,将成功获取到Access Token。
1.1.1、密码模式获取Access Token 的swagger2请求结果如下:
Curl
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \
"appId": "password_auth_mode", \
"appSecret": "123456", \
"password": "e10adc3949ba59abbe56e057f20f883e", \
"username": "username" \
}' 'http://localhost:8052/oauth2/token/passwordMode'
请求URL
http://localhost:8052/oauth2/token/passwordMode
Request Headers
{
"Accept": "*/*"
}
响应体
{
"access_token": "cdd3e77e-da0d-4740-8770-867214a381fe",
"token_type": "bearer",
"refresh_token": "3adf9259-4c32-4dc6-9a87-332784302dec",
"expires_in": 2591998,
"scope": "read"
}
1.1.2、客户端模式获取Access Token 的swagger2请求结果如下:
Curl
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \
"appId": "client_auth_mode", \
"appSecret": "123456" \
}' 'http://localhost:8052/oauth2/token/clientMode'
请求URL
http://localhost:8052/oauth2/token/clientMode
Request Headers
{
"Accept": "*/*"
}
响应体
{
"access_token": "27533af7-6d72-4ffa-a6ff-c2a0a7c7c8e8",
"token_type": "bearer",
"expires_in": 2591998,
"scope": "read write"
}
2.2、使用Access Token访问资源服务器的资源
1、打开oauth2-resource的swagger2地址,http://localhost:8051/swagger-ui.html
2、点击右上角的Authorization按钮,在api_key提示的输入框的中输入 Bear字符串,加空格,再加上步骤一中获取的Access Token的值,点击Authorize,
则会在后续访问每个接口的时候,添加头信息,key=Authorization,value = Bearer+ 空格 +AccessTokenValue (即Bearer a05da964-43b1-4427-a27f-9f2b674e6b6d)
3、点击要访问的接口,试一下,即可体验效果。
2.2.1、用户名为username所拥有的权限为ROLE_MEMBER
A、访问需权限ROLE_ADMIN的接口/authenticated/authorities/admin,swagger2效果如下:
Curl
curl -X GET --header 'Accept: application/json' --header 'Authorization: Bearer a05da964-43b1-4427-a27f-9f2b674e6b6d' 'http://localhost:8051/authenticated/authorities/admin'
请求URL
http://localhost:8051/authenticated/authorities/admin
Request Headers
{
"Accept": "*/*"
}
响应体
{
"error": "access_denied",
"error_description": "不允许访问"
}
B、访问需权限ROLE_MEMBER的接口/authenticated/authorities/admin,swagger2效果如下:
Curl
curl -X GET --header 'Accept: text/plain' --header 'Authorization: Bearer a05da964-43b1-4427-a27f-9f2b674e6b6d' 'http://localhost:8051/authenticated/authorities/member'
请求URL
http://localhost:8051/authenticated/authorities/member
Request Headers
{
"Accept": "*/*"
}
响应体
success
3、项目oauth2-redis-mysql-example源码地址
4、拓展:
4.1、资源服务器、认证授权服务器、客户端可以独立也可以合而唯一,本项目采用了独立的形式(分别为oauth2-client、oauth2-resource、oauth2-server)。
4.2、访问形式一:当前文章使用的是前端通过oauth2-client中转向oauth2-sever获取Access Token,然后向资源服务器oauth2-resource发起访问。
4.3、访问形式二:直接通过前端构造请求,向oauth2-server获取AccessToken,然后向资源服务器oauth2-resource发起访问.postman接口测试文件在此
5、项目不足
1、用户权限其实可以自定义拓展,当前代码写死了,后续改进
2、Redis存储Token可以使用JWT方式
3、DAO层改用mybatis-plus方式