OAuth2及sa-token框架实践
1、解决什么问题 1.1 故事
以上,打开尚未登录的哔哩哔哩,选择登录,会看到其他登录方式有微信、微博以及QQ。
举例微博来说,这里,就是想要使用作为用户的“你”在微博的账号信息,简单地,你可以把微博的账户名、密码告诉B站,B站登录微博,就可以获取到它想要的信息。
但是这种方式存在一些问题:
就是解决以上的问题,它能保证B站能够得到“账号信息”的权限,而又不能发微博,在你想要取消授权的时候又不用大动干戈修改密码。
1.2 下个定义
OAuth(Open ,开放授权)是一种发布和与受保护数据交互的简单方式。它是互联网中基于令牌的身份验证和授权的开放标准,它允许第三方服务(例如)使用用户的账户信息,而不用暴露用户密码。OAuth规范描述了用于获取访问令牌的5种授权方式:
简单来说,OAuth规范中,首先需要获取访问令牌,后续对用户资源的访问,都是需要将访问令牌带上的。
2、授权码授权模式
.0规范提供的5中获取访问令牌的方式中,授权码授权是最为安全、且广泛使用的。在正式介绍授权码授权之前imToken钱包,首先声明几个概念,如上的故事中:
2.1 故事续
回到的登录故事,选择“微博登录”,这时我们可以看到以下页面
扫码登录微博,同意授权
同意授权后自动跳转
因为尚未登录微博,所以需要先登录微博,整个流程是
这是一个标准的.0 授权码授权流程:
有必要对 做一下单独说明;授权之前, 需要到 关联的 注册一次, 会为 分配一个 id以及 (密码)。
2.2 授权码授权流程
通过授权码方式获取访问令牌,总体来说分为两步,一是获取授权码,而是授权码换取访问令牌。结合请求微博授权的实例说明。更多详细参考规范
2.2.1 授权请求
对应以上流程第2步。授权请求是客户端应用发送给授权服务器,获取授权码的过程。
// bilibili发给微博的授权请求是这样的
https://api.weibo.com/oauth2/authorize?
client_id=2841902482&
redirect_uri=https%3A%2F%2Fpassport.bilibili.com%2Flogin%2Fsnsback%3Fsns%3Dweibo%26state%3Ddaee0470162b11edbd071613e9886b23%26source%3Dnew_main_mini&
scope=email###
// OAuth2 rfc 规范中的授权请求
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
这些参数的意义是,
可以看到微博的授权方式,与标准的.0有一定有差异,但是大体上流程是按照流程来的。
2.2.2 授权响应(重定向)
对应以上流程第5步。怎么就直接从第2步到了第5步token 权限管理·(中国)官方网站,步骤3、4呢?其实3、4步骤产生的结果是资源拥有者同意授权,这个过程不涉及客户端应用,完全是授权服务器内部的逻辑,所以在.0规范中不涉及。
在的故事中,如果用户事先登录了微博,那么请求授权的时候就不会触发扫码登录,而是直接展示要求授权的页面,让用户同意;同样,身份认证方式也可以不是扫码,而是用户名密码登录。总之,这都是授权服务器的内部事宜,产出结果是资源拥有者同意/不同意授权。
规范中,如果资源拥有者同意了授权,授权服务器的响应是
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
注意到这是一个重定向的响应(http 302)。user-agent(通常是浏览器)获得该响应后,会重定向访问地址,规范中在重定向地址中附加了参数 code,这就是授权码,后续客户端应用可以通过code换取访问令牌。
规范中,若资源拥有者不同意授权,会进行一些说明,如下
HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz
具体参考rfc规范,实现上也不是必须的。
微博的授权响应是,可以看到与规范差异比较大,关注,嗯?好像是用隐式授权,访问令牌直接暴露了,不怀好意的第三方可以通过这个访问令牌,访问用户在微博的一些资源了,哔哩哔哩的故事到此结束。
https://passport.bilibili.com/register/snsback.html?
sns=weibo&
snsUid=7780546299&
snsAccessToken=2.00PW4YUIoe11GD940dc91c8apEMysD&
snsAccessExpires=2644318&
csrf=21e50500160811eda02a22f04cb09b9e&
source=new_main_mini&
gourl=https%3A%2F%2Fwww.bilibili.com%2F#/
2.2.3 访问令牌请求
客户端应用使用授权码,请求授权服务器获取访问令牌。
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
可以看到,最重要的参数就是授权码code。成功的响应试是这样的:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
其中 token可以用于在acess token过期后,直接向授权服务器获取新的访问令牌,而不用资源拥有者参与。
3、sa-token实践.0授权码
sa-token是一个国产的轻量级Java权限认证框架,官方文档地址是:
根据官网的指引,搭建一个.0授权服务及其简单,直接参考官方指引即可:
最后可以下载官方的完整示例
但是目前的版本(v1.30.0)中并没有直接介绍资源服务器侧的相关信息,即资源服务器如何根据访问令牌来做对应的访问权限控制。
权限控制这一块sa-token已经提供了基础的信息以及API:
3.1 实践资源服务器
访问受保护资源时,规定通过请求头X--TOKEN传递授权获得访问令牌,通过以上分析,定义一个,在中校验请求携带的访问令牌,是否具备要求的scope权限。
部分代码如下:
private void accessTokenPermissionVerify(SaRequest request) {
if (SaRouter.isMatchCurrURI("/info/**")) {
// 获取请求头中的 access-token
String accessToken = request.getHeader(AC_TOKEN_HEADER);
if (StringUtils.isEmpty(accessToken)) {
throw new NotPermissionException("未通过授权");
}
// 验证 access-token 是否有效
AccessTokenModel acTokenEntity = SaOAuth2Util.getAccessToken(accessToken);
if (null == acTokenEntity) {
throw new NotPermissionException("未通过授权");
}
SaOAuth2Util.checkScope(accessToken, "userinfo");
}
}
这里构建的权限模型是,访问url/info/**代表的资源,需要访问令牌,且具备权限。可以看到这里将url抽象为资源,匹配scope,并不能做到数据级别的权限控制,如用户A不能访问用户B的数据。数据权限是更加复杂的课题,已经不在的范围内了。
资源服务器的搭建完整示例:
重点关注类com....er对于权限模型的定义以及校验方法。
效果展示
胡乱填写的acess-token or 无权限的acess-token
GET http://localhost:8001/info/student?id=1
Accept: */*
X-ACCESS-TOKEN: ABG2ImmKfCKQXiIYlgtY9rotc2WDLojvfhzVY860aQsJk77j9GM9BKl6CtJm
###响应
{
"code": 500,
"msg": "无此权限:未通过授权",
"data": null
}
有效&有权限的acess-token
GET http://localhost:8001/info/student?id=1
Accept: */*
X-ACCESS-TOKEN: ABG2ImmKfCKQXiIYlgtY9rotc2WDLojvfhzVY860aQsJk77j9GM9BKl6CtJm
###响应
{
"id": "1",
"name": "张三",
"gender": "man",
"age": 18,
"mail": "zhangsan@qq.com"
}
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。