相关知识推荐
scope 定义:Scope 是资源拥有者(服务端)用来授予客户端特定权限的一个参数
- 客户端:申请不同的 scope
- 服务端:
- 验证申请的 scope 是否支持
- 验证该 scope 是否对该客户端开放(在付费使用的场景下)
- 最后返回授权码
从以上的思路来看:验证 scope 需要我们自己实现,因为涉及到了付费业务场景。
那么 scope 和具体资源是什么关联上的?
服务端需要为不同的资源指定不同的 Scope,即告知申请当前资源需要具有某种 Scope 的权限。
客户端请求时,需要在权限判定的时机去处理这个 scope 的判定,即:获取到该资源对应的 scope,并检查访问的 token 中申请的 scope 是否该资源的 scope 对应
开放平台思路
由于没有做过开放平台的系统,不太清楚和普通系统有什么区别,这里差不多猜想下:
- 普通系统:一般使用 用户 使用用户名和密码登录,登录后可以进行正常的业务操作
- 开放平台:也是一个独立的系统,也有自己的用户模块
这里的用户主要是配置 应用,也就是使用 OATH2 标准流程的 clientId 和 secretId 相关信息,对于应用能获取/更改的数据有两部分:
- 第一种:能操作多个用户的信息,这些信息普通用户是没有这个功能的,这就是开放 API,站在第三方的角色来处理东西
- 第二种:普通系统,对接到开放平台,普通用户登录后,可以对应用授权一个 token,可以对该 token 授权不同的 API 权限,这些 API 一般是普通系统中用户所拥有的所有 功能权限。
这样一来,普通系统其实可以充当一个第三方,在开放平台申请了一个超大权限的 应用,不然怎么能提供一些重复的功能呢?比如微信支付,开发平台还提供统一下单的功能,然后进行扫码支付。这个有点不是很明白 开放平台和普通系统是怎么结合的。
查阅了一部分开放平台思想思路之后,基本上的做法是:
- 业务系统的 API 不会直接当成 openAPI 提供,原因:
- 业务系统的前后端默认信任,虽然笔者暂时不知道是通过什么机制控制的(普通的项目,只要用户登录成功,拿到 token 任意机器都能调用前端页面访问的接口)
- 而 open api 还需要提供一些安全控制、流量控制、API 调用量等手段,就需要前面有一层入口:这个入口最重要的作用是需要透传请求到业务系统,或则重新提供接口去适配业务系统的逻辑(可能没法全部重用业务系统的逻辑,因为和 透传不一样)
- 开放系统自己有一套开发者账户信息,然后需要创建 app,以此得到 appid 和 appkey,这个信息获取到的 token 只能访问 open api 提供的接口,不能访问业务系统提供的 api。
- open api 是专门提供给第三方使用的,与业务 api 是隔离的。
对于小型系统来说
没有那么高的安全级别,也没有那么高的流量限制,为了实现功能,可以做如下的折中实现:
- 一套 oath2 授权和鉴权服务,都在资源服务器上
- 提供的 controller 上绑定角色 或则 scope,以标识哪些 token 能访问此 API。只要 open api 和 业务系统的 api 的 路径 + scope 不一致,那么就能达到隔离的目的
问题一:那么 open api 和 业务系统 的用户信息如何隔离?
一套 oath2 程序,只能运行一种逻辑,但是像 userdetail 这种用户是可以自己提供实现逻辑的,可以这样做:
- 业务系统:正常传递用户名密码,使用 非标准的 用户名密码 发放前后端分离需要的 token 信息
- open api 系统:前面专门提供一个开发者登录注册的入口,登录是肯定也走的与业务系统同一套非标准的 用户名密码登录
可以在用户名上做手脚,比如在用户名前面增加一个特殊的前缀 ___a
,userdetail 中判断是这种标识,就去开发者账户库中去获取信息,后续的资源检查都是同一个逻辑了。
那么可以提供的功能如下:
- 非标准 oath2 等授权:用户名密码登录,发放 token 供前端使用
- 标准 oath2 授权码授权:appid + appkey + scope 跳转到开放平台的用户名密码登录
业务场景:
- 不代替用户操作:就是资源没有特定的拥有者:注册开发者账户、走客户端凭证机制;这个 token 不能调用前端业务系统,只能调用特定的 开放平台 API
- 帮用户操作的:走授权码机制,需要用户登录业务系统后,同意应用申请的授权,完成授权后,这个 token 和前端业务系统调用后端一样的接口。
- 提供自定义创建 token 业务:用户自己创建 token 信息给任意人使用,和 2 类似,都是走一样的接口,只是权限不同
http basic 如何触发?
当你访问一个地址,浏览器弹出下面这张图时,这就是需要 http basic 认证
先说它是如何实现的,原理很简单,后端响应,需要满足两个条件:
- 401 http 状态码
WWW-Authenticate
响应头
// 跳转到用户授权页面 /oauth/authorize-approve 此页面用户需要登录
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// WWW-Authenticate: <type> realm=<realm>
response.setHeader("WWW-Authenticate", "Basic realm=\"authorize\"");
- type: Basic 是通用的类型,会弹出登录框
- realm:是保护域,应该可以理解为 oath2 中的 scope,就是需要什么权限才能访问的含义;只是一个字符串描述符
要完整实现这个 http basic 认证步骤:
- 你需要拦截这个当前请求是否有登录
- 如果没有登录,则按上面的要求响应 Basic。浏览器就会弹出这个登录框
- 输入用户名密码后,登录,当前请求会重新发起,不过在请求头中多了一个头
Authorization: Basic MTIzNDU5NjoxMjM0NTY=
,Basic 后面就是用户名密码被 basic64 加密的字符串。内容格式为用户名:密码
到这里,有一个难题要解决:如何在请求到业务逻辑前,让给定的用户名密码变成登录状态?
如果你的系统是基于 token 来处理的,比如使用了拦截器,那么就需要在拦截器前面对 http basic 进行处理,内部登录成功后,拿到 token 设置到请求里面