相关知识推荐

scope 定义:Scope 是资源拥有者(服务端)用来授予客户端特定权限的一个参数

  • 客户端:申请不同的 scope
  • 服务端:
    1. 验证申请的 scope 是否支持
    2. 验证该 scope 是否对该客户端开放(在付费使用的场景下)
    3. 最后返回授权码

从以上的思路来看:验证 scope 需要我们自己实现,因为涉及到了付费业务场景。

那么 scope 和具体资源是什么关联上的?
服务端需要为不同的资源指定不同的 Scope,即告知申请当前资源需要具有某种 Scope 的权限。
客户端请求时,需要在权限判定的时机去处理这个 scope 的判定,即:获取到该资源对应的 scope,并检查访问的 token 中申请的 scope 是否该资源的 scope 对应

开放平台思路

由于没有做过开放平台的系统,不太清楚和普通系统有什么区别,这里差不多猜想下:

  • 普通系统:一般使用 用户 使用用户名和密码登录,登录后可以进行正常的业务操作
  • 开放平台:也是一个独立的系统,也有自己的用户模块

这里的用户主要是配置 应用,也就是使用 OATH2 标准流程的 clientId 和 secretId 相关信息,对于应用能获取/更改的数据有两部分:

  1. 第一种:能操作多个用户的信息,这些信息普通用户是没有这个功能的,这就是开放 API,站在第三方的角色来处理东西
  2. 第二种:普通系统,对接到开放平台,普通用户登录后,可以对应用授权一个 token,可以对该 token 授权不同的 API 权限,这些 API 一般是普通系统中用户所拥有的所有 功能权限。

这样一来,普通系统其实可以充当一个第三方,在开放平台申请了一个超大权限的 应用,不然怎么能提供一些重复的功能呢?比如微信支付,开发平台还提供统一下单的功能,然后进行扫码支付。这个有点不是很明白 开放平台和普通系统是怎么结合的。

查阅了一部分开放平台思想思路之后,基本上的做法是:

  1. 业务系统的 API 不会直接当成 openAPI 提供,原因:
    1. 业务系统的前后端默认信任,虽然笔者暂时不知道是通过什么机制控制的(普通的项目,只要用户登录成功,拿到 token 任意机器都能调用前端页面访问的接口)
    2. 而 open api 还需要提供一些安全控制、流量控制、API 调用量等手段,就需要前面有一层入口:这个入口最重要的作用是需要透传请求到业务系统,或则重新提供接口去适配业务系统的逻辑(可能没法全部重用业务系统的逻辑,因为和 透传不一样)
  2. 开放系统自己有一套开发者账户信息,然后需要创建 app,以此得到 appid 和 appkey,这个信息获取到的 token 只能访问 open api 提供的接口,不能访问业务系统提供的 api。
  3. open api 是专门提供给第三方使用的,与业务 api 是隔离的。

对于小型系统来说

没有那么高的安全级别,也没有那么高的流量限制,为了实现功能,可以做如下的折中实现:

  1. 一套 oath2 授权和鉴权服务,都在资源服务器上
  2. 提供的 controller 上绑定角色 或则 scope,以标识哪些 token 能访问此 API。只要 open api 和 业务系统的 api 的 路径 + scope 不一致,那么就能达到隔离的目的

问题一:那么 open api 和 业务系统 的用户信息如何隔离?
一套 oath2 程序,只能运行一种逻辑,但是像 userdetail 这种用户是可以自己提供实现逻辑的,可以这样做:

  1. 业务系统:正常传递用户名密码,使用 非标准的 用户名密码 发放前后端分离需要的 token 信息
  2. open api 系统:前面专门提供一个开发者登录注册的入口,登录是肯定也走的与业务系统同一套非标准的 用户名密码登录

可以在用户名上做手脚,比如在用户名前面增加一个特殊的前缀 ___a,userdetail 中判断是这种标识,就去开发者账户库中去获取信息,后续的资源检查都是同一个逻辑了。

那么可以提供的功能如下:

  1. 非标准 oath2 等授权:用户名密码登录,发放 token 供前端使用
  2. 标准 oath2 授权码授权:appid + appkey + scope 跳转到开放平台的用户名密码登录

业务场景:

    1. 不代替用户操作:就是资源没有特定的拥有者:注册开发者账户、走客户端凭证机制;这个 token 不能调用前端业务系统,只能调用特定的 开放平台 API
    1. 帮用户操作的:走授权码机制,需要用户登录业务系统后,同意应用申请的授权,完成授权后,这个 token 和前端业务系统调用后端一样的接口。
    1. 提供自定义创建 token 业务:用户自己创建 token 信息给任意人使用,和 2 类似,都是走一样的接口,只是权限不同

http basic 如何触发?

当你访问一个地址,浏览器弹出下面这张图时,这就是需要 http basic 认证
image.png
先说它是如何实现的,原理很简单,后端响应,需要满足两个条件:

  1. 401 http 状态码
  2. WWW-Authenticate 响应头
  1. // 跳转到用户授权页面 /oauth/authorize-approve 此页面用户需要登录
  2. response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
  3. // WWW-Authenticate: <type> realm=<realm>
  4. response.setHeader("WWW-Authenticate", "Basic realm=\"authorize\"");
  • type: Basic 是通用的类型,会弹出登录框
  • realm:是保护域,应该可以理解为 oath2 中的 scope,就是需要什么权限才能访问的含义;只是一个字符串描述符

要完整实现这个 http basic 认证步骤:

  1. 你需要拦截这个当前请求是否有登录
  2. 如果没有登录,则按上面的要求响应 Basic。浏览器就会弹出这个登录框
  3. 输入用户名密码后,登录,当前请求会重新发起,不过在请求头中多了一个头 Authorization: Basic MTIzNDU5NjoxMjM0NTY=,Basic 后面就是用户名密码被 basic64 加密的字符串。内容格式为 用户名:密码

到这里,有一个难题要解决:如何在请求到业务逻辑前,让给定的用户名密码变成登录状态?

如果你的系统是基于 token 来处理的,比如使用了拦截器,那么就需要在拦截器前面对 http basic 进行处理,内部登录成功后,拿到 token 设置到请求里面