12 | 架构案例:基于OAuth 2.0/JWT的微服务参考架构

你好,我是王新栋。

在前面几讲,我们一起学习了OAuth 2.0 在开放环境中的使用过程。那么OAuth 2.0 不仅仅可以用在开放的场景中,它可以应用到我们任何需要授权/鉴权的地方,包括微服务。

因此今天,我特别邀请了我的朋友杨波,来和你分享一个基于OAuth 2.0/JWT的微服务参考架构。杨波,曾先后担任过携程框架部的研发总监和拍拍贷基础架构部的研发总监,在微服务和OAuth 2.0有非常丰富的实践经验。

其中,在携程工作期间,他负责过携程的API网关产品的研发工作,包括它和携程的令牌服务的集成;在拍拍贷工作期间,他负责过拍拍贷的令牌服务的研发和运维工作。这两家公司的令牌服务和OAuth 2.0类似,但要更简单些。

接下来,我们就开始学习杨波老师给我们带来的内容吧。

你好,我是杨波。

从单体到微服务架构的演进,是当前企业数字化转型的一大趋势。OAuth 2.0是当前业界标准的授权协议,它的核心是若干个针对不同场景的令牌颁发和管理流程;而JWT是一种轻量级、自包含的令牌,可用于在微服务间安全地传递用户信息。

据我目前了解到的情况,虽然有不少企业已经部分或全部转型到微服务架构,但是在授权认证机制方面,它们一般都是定制自研的,比方说携程和拍拍贷的令牌服务。之所以定制自研,主要原因在于标准的OAuth 2.0协议相对比较复杂,门槛也比较高。定制自研固然可以暂时解决企业的问题,但是不具备通用性,也可能有很多潜在的安全风险。

那么,到底应该如何将行业标准的OAuth 2.0/JWT和微服务集成起来呢,又有没有可落地的参考架构呢?

针对这个问题,今天我就和你分享一种可落地的参考架构。不过,我要提前说明的是,这个架构的思想源于MICRO-SERVICES ARCHITECTURE WITH OAUTH2 AND JWT – PART 1 – OVERVIEW这篇文章。根据原作者Thijs的描述,他提出的架构已经在企业落地架构了。如果你还想获得关于原架构的更多细节,建议进一步参考“What is PKCE?”这篇文章。

我认为,Thijs给出的架构确实具有可落地性和参考价值,但是他的架构里面对某些微服务层次的命名,例如BFF和Facade层,和目前主流的微服务架构不符,还有他的架构应该是手绘,不够清晰,也不容易理解。为此,我专门用今天这一讲,来改进Thijs给出的架构,并补充针对不同场景的流程。

为了方便理解,在接下来的讲述中,我会假定有这样一家叫ACME的新零售公司,它已经实现了数字化转型,微服务电商平台是支持业务运作的核心基础设施。

在业务架构方面,ACME有近千家线下门店,这些门店通过POS系统和电商平台对接。公司还有一些物流发货中心,拣选(Order Picking)系统也要和电商平台对接。另外,公司还有很多送货司机,通过App和电商平台对接。当然,ACME还有一些电商网站,做线上营销和销售,这些网站是电商平台的主要流量源。

虽然支持ACME公司业务运作的技术平台很复杂,但是它的核心可以用一个简化的微服务架构图来描述:

可以看出,这个微服务架构是运行在Kubernetes集群中的。当然了,这个架构实际上并不一定需要Kubernetes环境,用传统数据中心也可以。另外,它的整体认证授权架构是基于OAuth 2.0/JWT实现的。

接下来,我按这个微服务架构的分层方式,依次和你分析下它的每一层,以及应用认证/授权和服务调用的相关流程。这样,你不仅可以理解一个典型的微服务架构该如何分层,还可以弄清楚OAuth 2.0/JWT该如何与微服务进行集成。

微服务分层架构

ACME公司的微服务架构,大致可以分为Nginx反向代理层、Web应用层、Gateway网关层、BEF层和领域服务层,还包括一个IDP服务。总体上讲,这是一种目前主流的微服务架构分层方式,每一层职责单一、清晰。

接下来,我们具体看看每一层的主要功能。

Nginx反向代理层

首先,Nginx集群是整个平台的流量入口。Nginx是7层HTTP反向代理,主要功能是实现反向路由,也就是将外部流量根据HOST主机头或者PATH,路由到不同的后端,比方说路由到Web应用,或者直接到网关Gateway。

在Kubernetes体系中,Nginx是和Ingress Controller(入口控制器)配合工作的(总称为Nginx Ingress),Ingress Controller支持通过Ingress Rules,配置Nginx的路由规则。

Web应用层

这一层主要是一些Web应用,html/css/js等资源就住在这一层。

Web服务层通常采用传统的Web MVC + 模版引擎方式处理,可以实现服务器端渲染,也可以采用单页SPA方式。这一层主要由公司的前端团队负责,通常会使用Node.js技术栈来实现,也可以采用Spring MVC技术栈实现。具体怎么实现,要看公司的前端团队更擅长哪种技术。当这一层需要后台数据时,可以通过网关调用后台服务获取数据。

Gateway网关层

这一层是微服务调用流量的入口。网关的主要职责是反向路由,也就是将前端请求根据HOST主机头、或者PATH、或者查询参数,路由到后端目标微服务(比如,图中的IDP/BFF或者直接到领域服务)。

另外,网关还承担两个重要的安全职责:

  • 一个是令牌的校验和转换,将前端传递过来的OAuth 2.0访问令牌,通过调用IDP进行校验,并转换为包含用户和权限信息的JWT令牌,再将JWT令牌向后台微服务传递。
  • 另外一个是权限校验,网关的路由表可以和OAuth 2.0的Scope进行关联。这样,网关根据请求令牌中的权限范围Scope,就可以判断请求是否具有调用后台服务的权限。

关于安全相关的场景和流程,我会在下一章节做进一步解释。

另外,网关还需承担集中式限流、日志监控,以及支持CORS等功能。

对于网关层的技术选型,当前主流的API网关产品,像Netflix开源的Zuul、Spring Cloud Gateway等,都可以考虑。

IDP服务

IDP是Identity Provider的简称,主要负责OAuth 2.0授权协议处理,OAuth 2.0和JWT令牌颁发和管理,以及用户认证等功能。IDP使用后台的Login-Service进行用户认证。

对于IDP的技术选型,当前主流的Spring Security OAuth,或者RedHat开源的KeyCloak,都可以考虑。其中,Spring Security OAuth是一个OAuth 2.0的开发框架,适合企业定制。KeyCloak则是一个开箱即用的OAuth 2.0/OIDC产品。

BFF层

BFF是Backend for Frontend的简称,主要实现对后台领域服务的聚合(Aggregation,有点类似数据库的Join)功能,同时为不同的前端体验(PC/Mobile/开放平台等)提供更友好的API和数据格式。

BFF中可以包含一些业务逻辑,甚至还可以有自己的数据库存储。通常,BFF要调用两个或两个以上的领域服务,甚至还可能调用其它的BFF(当然一般并不建议这样调用,因为这样会让调用关系变得错综复杂,无法理解)。

如果BFF需要获取调用用户或者OAuth 2.0 Scope相关信息,它可以从传递过来的JWT令牌中直接获取。

BFF服务可以用Node.js开发,也可以用Java/Spring等框架开发。

领域服务层

领域服务层在整个微服务架构的底层。这些服务包含业务逻辑,通常有自己独立的数据库存储,还可以根据需要调用外部的服务。

根据微服务分层原则,领域服务禁止调用其它的领域服务,更不允许反向调用BFF服务。这样做是为了保持微服务职责单一(Single Responsibility)和有界上下文(Bounded Context),避免复杂的领域依赖。领域服务是独立的开发、测试和发布单位。在电商领域,常见的领域服务有用户服务、商品服务、订单服务和支付服务等。

和BFF一样,如果领域服务需要获取调用用户或者OAuth 2.0 Scope相关信息,它可以从传递过来的JWT令牌中直接获取。

可以看到,领域服务和BFF服务都是无状态的,它们本身并不存储用户状态,而是通过传递过来的JWT数据获取用户信息。所以在整个架构中,微服务都是无状态、可以按需水平扩展的,状态要么存在用户端(浏览器或者手机App中),要么存在集中的数据库中。

OAuth 2.0/JWT如何与微服务进行集成?

以上,就是ACME公司的整个微服务架构的层次了。这个分层架构,对于大部分的互联网业务系统场景都适用。因此,如果你是一家企业的架构师,需要设计一套微服务架构,完全可以参考它来设计。接下来,我再演示几个典型的应用认证场景,以及相应的服务调用流程,来帮助你理解OAuth 2.0/JWT是如何和微服务进行集成的。

场景1:第一方Web应用+资源拥有者凭据模式

这个场景是用户访问ACME公司自己的电商网站,假设这个电商网站是用Spring MVC开发的。考虑到这是一个第一方场景(也就是公司自己开发的网站应用),我们可以选OAuth 2.0的资源拥有者凭据许可(Resource Owner Password Credentials Grant),也可以选更安全的授权码许可(Authorization Code Grant)。因为这里没有第三方的概念,所以我们就选相对简单的资源拥有者凭据许可。

下面是一个认证授权流程样例。注意,这个只是突出了关键步骤,实际生产的话,还有很多需要完善和优化的地方。另外,为描述简单,这里假定一个成功流程。

在上面的图中,用户对应OAuth 2.0中的资源拥有者,ACME IDP对应OAuth 2.0中的授权服务。另外,前面架构图中的后台微服务(包括BFF和基础领域服务),对应OAuth 2.0中的受保护资源。

下面是流程说明:

  1. 用户通过浏览器访问ACME公司的电商网站,点击登录链接。
  2. Web应用返回登录界面(这个登录页可以是网站自己定制开发)。
  3. 用户输入用户名、密码进行认证。
  4. Web应用将用户名、密码,通过网关转发到IDP的令牌获取端点(POST /oauth2/token,grant_type=password)。
  5. IDP通过Login Service对用户进行认证。
  6. IDP认证通过,返回有效访问令牌(根据需要也可以返回刷新令牌)。
  7. Web应用接收到访问令牌,创建用户Session,并将OAuth 2.0令牌保存其中,然后返回登录成功到用户端。
  8. 用户浏览器中记录Session Cookie,登录成功。

那接下来,我们再来看看认证授权之后的服务调用流程。同样,这里也只是突出了关键步骤,并假定是一个成功流程。

  1. 用户登录后,在网站上点击查看自己的购物历史记录。
  2. Web应用通过网关调用后台API(查询用户的购物历史记录),请求HTTP header中带上OAuth 2.0令牌(来自用户Session)。
  3. 网关截取OAuth 2.0令牌,去IDP进行校验。
  4. IDP校验令牌通过,再通过令牌查询用户和Scope信息,构建JWT令牌,返回。
  5. 网关获得JWT令牌,校验Scope是否有权限调用API,如果有就转发到后台API进行调用。
  6. 后台BFF(或者领域服务)通过传递过来的JWT获取用户信息,根据用户ID查询购物历史记录,返回。
  7. Web应用获得用户的购物历史数据,可以根据需要缓存在Session中,再返回用户端。
  8. 购物历史数据返回到用户浏览器端。

注意,这个服务调用流程,也可以应用在其他场景中,比如我们接下来要学习的“第一方移动应用+授权码许可模式”和“第三方Web应用+授权码许可模式”。基本上只要你理解了这个流程原理,就可以根据实际场景灵活套用。

场景2:第一方移动应用+授权码许可模式

第二个场景是用户通过手机访问ACME公司自己的电商App。这是第一方的原生应用(Native App)场景,通常考虑选用OAuth 2.0的用户名密码模式,但是并不安全(参考MICRO-SERVICES ARCHITECTURE WITH OAUTH2 AND JWT – PART 3 – IDP的Security Consideration部分),所以业界建议采用授权码模式,而且是要支持PKCE扩展的授权码模式。

那接下来,我们来看看这个认证授权的流程。同样,这里只是突出了关键步骤,并假定是一个成功流程。

  1. 用户访问电商App,点击登录。
  2. App生成PKCE相关的code verifier + challenge。
  3. App以内嵌方式启动手机浏览器,访问IDP的统一认证页(GET /authorize),请求带上PKCE的code challenge相关参数。
  4. IDP返回统一认证页。
  5. 用户认证和授权。
  6. IDP通过Login Service对用户进行认证。
  7. IDP返回授权码到App浏览器。
  8. App截取浏览器带回的授权码,将授权码+PKCE code verifer,通过网关转发到IDP的令牌获取端点(POST /oauth2/token, grant_type=authorization-code)。
  9. IDP校验PKCE和授权码,校验通过则返回有效访问令牌。
  10. App获取令牌,本地存储,登录成功。

之后,App如果需要和后台交互,可直接通过网关调用后台微服务,请求HTTP header中带上OAuth 2.0访问令牌即可。后续的服务调用流程,和“第一方应用+资源拥有者凭据模式”类似。

场景3:第三方Web应用+授权码模式

第三个场景是某第三方合作厂商开发了一个Web网站,要访问ACME公司的电商开放平台API。这是一个第三方Web应用场景,通常选用OAuth 2.0的授权码许可模式。

那接下来,我们来看看这个认证授权的流程。同样,这里只是突出了关键步骤,并假设是一个成功流程。

  1. 用户访问这个第三方Web应用,点击登录链接。

  2. Web应用后台向ACME公司的IDP服务发送申请授权码请求(GET /authorize)。

  3. 用户被重定向到ACME公司的IDP统一登录页面。

  4. 用户进行认证和授权。

  5. IDP通过Login Service对用户进行认证。

  6. 认证和授权通过,IDP返回授权码。

  7. Web应用获得授权码,再向IDP服务的令牌获取端点发起请求(POST /oauth2/token, grant_type=authorization-code)。

  8. IDP校验授权码,校验通过则返回有效OAuth 2.0令牌(根据需要也可以返回刷新令牌)。

  9. Web应用创建用户Session,将OAuth 2.0令牌保存在Session中,然后返回登录成功到用户端。

  10. 用户浏览器中记录Session Cookie,登录成功。

之后,第三方Web应用如果需要和ACME电商平台交互,可直接通过网关调用微服务,请求HTTP header中带上OAuth 2.0访问令牌即可。后续的服务调用流程,和前面的“第一方应用+资源拥有者凭据模式”类似。

额外说明

除了上面的三个主要场景和流程,我还要和你分享6点。这6点是对上面基本流程的补充,也是企业级的OAuth 2.0应用要额外考虑的。

第一点是,IDP的API要支持从OAuth 2.0访问令牌到JWT令牌的互转。今天我们提到的集成架构采用OAuth 2.0 访问令牌 + JWT令牌的混合模式,中间需要实现OAuth 2.0访问令牌到JWT令牌的互转。这个互转API并非OAuth 2.0的标准,有些IDP产品(比方Spring Security OAuth)可能并不支持,因此需要用户定制扩展。

第二点是,关于单页SPA应用场景。关于单页SPA应用场景,简单做法是采用隐式许可,但是这个模式是OAuth 2.0中比较不安全的,所以一般不建议采用。对于纯单页SPA应用,业界推荐的做法是:

  • 如果浏览器支持Web Crypto for PKCE,则可以考虑使用类似“第一方移动应用”场景下的授权码许可+PKCE扩展流程;
  • 否则,考虑SPA+传统Web混合(hybrid)模式,前端页面可以住在客户浏览器端中,但登录认证还是由后台Web站点配合实现,走类似“第一方Web应用”场景的资源拥有者凭据模式,或者“第三方Web应用”场景下的授权码许可模式。

第三点是,关于SSO单点登录场景。为了简化描述,上面的流程没有考虑SSO单点登录场景。如果要支持Web SSO,那么各种应用场景都必须通过浏览器+IDP登录页集中登录,并且IDP要支持Session,用于维护登录态。如果IDP以集群方式部署的话,还要考虑粘性Sticky Session或者集中式Session。

这样,当用户通过一个Web应用登录后,后续如果再用其它Web应用登录的话,只要IDP上的Session还存在,那么这个登录就可以自动完成,相当于单点登录。

当然,如果要支持SSO,IDP的Session Cookie要种在Web应用的根域上,也就是说不同Web应用的根域必须相同,否则会有跨域问题。

第四点是关于IDP和网关的部署方式。前面的几张架构图中,IDP虽然躲在网关后面,但实际上IDP可以直接通过Nginx对外暴露,不经过网关。或者,IDP的登录授权页面,可以通过Nginx直接暴露,API接口则走网关。

第五点是关于刷新令牌。为了简化描述,上面的流程没有详细说明刷新令牌的集成方式。企业根据场景需要,可以启用刷新令牌,来延长用户的登录时间,具体的集成方式需要考虑安全性的需求。

第六点是关于Web Session。为了简化描述,在上面的流程中,Web应用登录成功后假设启用Web Session,也就是服务器端Session。在实际场景中,Web Session并非唯一选择,也可以采用简单的客户端Session方式,也称无状态Session,也就是在客户端浏览器Cookie中保存OAuth 2.0访问令牌。

小结

好了,以上就是今天的主要内容了。今天,我和你分享了如何将行业标准的OAuth 2.0/JWT和微服务集成起来,你需要记住以下四点。

第一,目前主流的微服务架构大致可以分为5层,分别是:Nginx流量接入层->Web应用层->API网关层->BFF聚合层->领域服务层。这个架构可以住在云原生的Kubernetes环境中,也可以住在传统数据中心里头。

第二,API网关是微服务调用的入口,承担重要的安全认证和鉴权功能。主要的安全操作包括:一,通过IDP校验OAuth 2.0访问令牌,并获取带用户和权限信息的JWT令牌;二,基于OAuth 2.0的Scope对API调用进行鉴权。

第三,在微服务架构体系下,通常需要一个集中的IDP服务,它相当于一个Authentication & Authorization as a Service角色,负责令牌颁发/校验/管理,还有用户认证。

第四,在今天这一讲提出的架构中,Web应用层(网关之前)的安全机制主要基于OAuth 2.0访问令牌实现(它是一种透明令牌或者称引用令牌),微服务层(网关之后)的安全机制主要基于JWT令牌实现(它是一种不透明自包含令牌)。网关层在中间实现两种令牌的转换。这是一种OAuth 2.0访问令牌+JWT令牌的混合模式。

之所以这样设计,是因为Web层靠近用户端,如果采用JWT令牌,会暴露用户信息,有一定的安全风险,所以采用OAuth 2.0访问令牌,它是一个无意义随机字符串。而在网关之后,安全风险相对低,同时很多服务需要用户信息,所以采用自包含用户信息的JWT令牌更合适。

当然,如果企业内网没有特别的安全考量,也可以直接传递完全透明的用户信息(例如使用JSON格式)。

思考题

  1. 除了今天我们讲到的OAuth 2.0访问令牌+JWT令牌的混合模式,实践中也可以全程采用OAuth 2.0访问令牌,或者全程采用JWT令牌。对比混合模式,如果全程采用OAuth 2.0访问令牌,或者全程采用JWT令牌,你觉得有哪些利弊呢?
  2. 你可以说说自己对基于传统Web应用的认证授权机制的理解吗?并对比今天讲到的现代微服务的认证授权机制,你可以说说它们之间的本质差异和相似点吗?

欢迎你在留言区分享你的观点,也欢迎你把今天的内容分享给其他朋友,我们一起交流。

精选留言

  • 西

    2020-07-25 11:32:58

    全程使用oauth2令牌,那么网关之后的每个微服务都需要自己根据令牌token去idp或者redis或者mysql中去查询用户信息,如果全程使用jwt令牌,主要是还是由于jwt自包含用户信息,存在暴露用户信息的安全风险。(如果jwt中只存在用户名,不存在其他相关信息也可以考虑全程使用jwt令牌)
    作者回复

    不错⛽️

    2020-08-04 23:44:30

  • 良胜

    2020-08-22 11:47:02

    王老师,现在我正在搭建一个微服务框架,计划在网关层做集中认证,那网关是否应该时作为OAUTH2的client组建?
    另外你画的微服务的架构图,IDP 是作为什么角色?授权服务器还是client,另外Login Svc 又是承担什么工作?

    我目前遇到困难,我们期望不管是web侧还是app侧,统一使用token进行认证,并且登录使用手机号+验证码的方式,现在如果访问受限资源,默认会跳到login页面,不是返回json数据,请老师指点
    作者回复

    回复你的一些问题:

    1. IDP就是基于OAuth2的授权服务,比方说可以采用Spring Security OAuth2来实现IDP。IDP可以自己实现登录认证的逻辑,也可以把登录认证的逻辑委派给其它服务,比方说独立的Login Service来实现。Spring Security OAuth2的登录认证逻辑就是可以plugin的,可以委派给其它服务来实现。

    2. 网关和IDP没有直接的client关系,只是IDP需要支持一些API端点,网关可以通过这些端点来实现:1)令牌校验,2)获取对应的JWT令牌。

    3. 登录使用手机号+验证代码,这是一种具体的基于双因素的认证方式,其实OAuth2本身只是要求用户在授权获取令牌前先进行登录认证,具体登录认证的方式它并不关心。Spring Security OAuth2的登录认证方式也是可以plugin的,你可以用默认的用户名密码方式,也可以定制为手机号+验证码方式。实际上Spring Security OAuth2就是基于Spring Security开发的,你只需要定制Spring Security来实现基于手机号+验证码的登录认证方式就可以。

    2020-08-24 00:24:02

  • kylexy_0817

    2020-08-02 22:03:17

    其实全程使用JWT,也没啥问题,只是如果想其较为安全,就要把公钥放在客户端,让其加密后再传输(或者获取到令牌后就直接加密存放在cookies或localstorage),但JWT的内容较多,即使加密后的内容也会较长,公网环境传输效率不高。而访问令牌,就是为了解决公网传输效率问题。不知有没理解对。
    作者回复

    JWT是自包含令牌,里头可以包含一部分用户信息,全程使用JWT也可以,JWT内部的数据本身只是加签防篡改,本来就是客户端可见的,一般服务器端启用HTTPS就可以了,再做一次加密意义并不大。

    相比JWT,普通访问令牌机制还有一个好处,就是可以集中吊销令牌,而JWT一般需要等到自然过期,因为它是自校验的。

    2020-08-05 00:43:02

  • 永旭

    2020-11-18 10:15:38

    老师你好, 查了下BFF相关文章
    有些架构图里把BFF放在API 网关层左边, 挨着web层.也有放在API 网关层里边的
    能分析分析这两种的有什么优势和劣势 , 区别吗 ?
    作者回复

    BFF虽然是前端开发,但是它也是一种聚合API,所以它理应放在API网关后面,这样就可以统一通过网关实现反向路由,限流熔断,日志监控等功能。

    所以BFF一般不放在API网关层前面,静态资源一般放在API网关层前面。

    也有一些企业的做法是BFF聚合逻辑直接写在网关上的,这种在企业规模不大、BFF逻辑不复杂时也可以采用,可以节省硬件资源。

    2020-11-18 23:42:43

  • 永旭

    2020-11-06 15:52:42

    老师, 以前我接触的架构图里有DMG层, 会部署前端.
    现在课程中 微服务架构图里已经没有了 DMG层了.
    要是有的话能部署什么呢 ?
    作者回复

    你说的应该是DMZ非军事区网络吧,在传统数据中心,在外网和内部受信网络之间一般有一个DMZ网络,这里头部署防火墙/反向代理/网关等可以增加各种安全防范措施。参考:
    https://en.wikipedia.org/wiki/DMZ_(computing)

    DMZ可以是物理的,也可以是逻辑的,在上文的架构图中,防火墙+Ingress+Gateway也可以算是在DMZ区内,可以增加各种安全措施。

    2020-11-10 21:31:10

  • inrtyx

    2020-07-25 21:14:47

    第六点是关于 Web Session,这个不太明白。token信息肯定要放浏览器啊!不然岂不是每次请求都要登录?
    作者回复

    网站会话有几种做法,一种是Session数据都存在服务器端,客户端浏览器cookie中只存sesssionID,我把这种称为Web Session,这种是服务器端有状态的Session技术。另外一种是Session数据都存在浏览器cookie中,我把这种称为客户端Session,这种是服务器端无状态的Session技术。

    2020-08-04 23:53:03

  • 往事随风,顺其自然

    2020-07-25 06:57:36

    全程采用JWt,用户信息容易暴露,对于安全级别高,最好不使用,对于oauth在安全级别更高,但是实现用户信息更复杂,混合之后,权衡了安全性和易用性,个人理解
    作者回复

    不错

    2020-08-04 23:43:36

  • Geek_7c4953

    2020-11-27 17:56:15

    有一个问题没有理解,APP用password模式登陆和APP上的浏览器打开统一认证页用用户名密码登录,这两者之间在安全层面上有什么区别?
    作者回复

    APP用用户名密码模式,通过native界面登录,这是一个一步流程,并且APP上要存clientId+secret,黑客可以破解app获取clientId+secret,然后通过猜测轮训你的post登录接口偷取用户密码。

    APP上的浏览器方式走的是一个3-legged完全OAuth2授权码流程,比较安全,很难被黑客利用,而且浏览器方式容易增加验证码和双因素等更安全校验机制。

    请参考下文的第7点:Security Consideration
    https://www.kaper.com/cloud/micro-services-architecture-with-oauth2-and-jwt-part-3-idp/



    2020-12-01 23:31:38

  • Giggle

    2020-10-15 20:10:17

    请问老师,若只是一个移动端app,想让其实现Oauth协议,提供对其他app的授权登录,这种app的后台Oauth协议的实现是不是不适合使用SpringSecurityOauth这个框架,因为我看框架源码里面授权码模式或者简易模式都是基于浏览器重定向机制实现返回授权码code或者token,用户未授权情况下还会返回相应的授权页面,我是不是可以理解SpringSecurityOauth其实是解决网站和网站的第三方授权的场景,假如我是一个移动端的后台,就不适合使用这个框架。
    作者回复

    移动端原生App也可以采用OAuth2的授权码模式(建议采用支持PKCE的增强型授权码模式),这时候可以在App中以嵌入方式启动手机浏览器,走授权码申请流程,等IDP 返回授权码到 App 浏览器之后,App 可以截取浏览器带回的授权码,然后App可以继续走后面令牌获取的流程。

    所以这个流程App需要借助手机上的浏览器这个中介,来走完OAuth2的授权码流程,这个是目前针对原生App采用OAuth2协议的推荐做法。

    具体参考本文的[场景 2:第一方移动应用 + 授权码许可模式]的描述。

    额外参考:
    https://auth0.com/blog/oauth-2-best-practices-for-native-apps/
    https://auth0.com/docs/flows/authorization-code-flow-with-proof-key-for-code-exchange-pkce

    2020-10-17 00:21:35

  • Tim Zhang

    2020-07-27 15:37:16

    网关获得 JWT 令牌,校验 Scope 是否有权限调用 API,如果有就转发到后台 API 进行调用。

    校验是如何校验的? 并且scope 和 authority有啥区别么
    作者回复

    企业根据具体场景,需要定义一个scope和API之间的权限关系表,这个表需要单独维护,并且可以缓存在网关上,加快网关的校验。所谓校验,就去查这张表,看某个scope能否访问某个API。

    scope和authority并没有所谓官方定义,你可以根据上下文赋予它们具体的语义。

    2020-08-05 00:19:06

  • 在路上

    2020-07-26 17:31:46

    全程采用JWT令牌模式,包含很多用户信息,权限信息,有泄露风险;jwt信息如果被修改,有权限越界的风险。采用OAuth令牌模式用户信息安全无问题,在领域层每次都要通过令牌查询用户级权限信息,加大的领域服务的开销,影响性能。
    作者回复

    基本正确,但是jwt信息只是可以被客户端看到,一般也是没办法篡改的,只要secret key或者私钥不泄密。

    2020-08-04 23:55:38

  • gesanri

    2022-02-02 21:15:50

    这里网关层的根据access token生成jwt,是不是只有第一次才会这样,完后就把这个映射关系存起来了,后面access token再来就直接取jwt了,否则每次都生成的话就没意义了
  • LYF_Mr

    2021-01-08 11:06:42

    王老师,我想问下,如果各个domain 服务之间有依赖,该如何处理?例如 A domain 进行添加数据时,其中某字段需要从 B domain 中,获取该值,那么该业务逻辑,是不是得封装在BFF层,而不应该在domain服务中实现?
    作者回复

    两个办法:
    1. BFF层做聚合(aggregation)调用。
    2. B domain数据变更时,通过消息机制同步到A domain,也就是通过数据复制冗余解决。

    2021-01-12 22:14:10

  • 长脖子树

    2020-10-19 15:19:26

    有个问题, 场景 1:第一方 Web 应用 + 资源拥有者凭据模式 这张图里https://static001.geekbang.org/resource/image/b6/bd/b658befe1da937fa3685b55522487dbd.jpg
    第4步 Web 应用将用户名、密码,通过网关转发到 IDP 的令牌获取端点(POST /oauth2/token,grant_type=password)。
    这里发送的参数包含了 client_id , client_secret, username, password 都传给了网关, 但这样不是暴露了 单个应用唯一的 client_secret (app_secert) 了么?
    还是说这里的web应用, 还包含了其后台系统? 是通过后台系统直接访问内网的 IDP 服务的?
    作者回复

    网关和OAuth2服务都是属于第一方的服务,可以认为它们处在同一个受信域内,所以可以通过网关透传一些参数。

    即使不通过网关,OAuth2服务前面一般也有前置反向代理(例如nginx),这些参数也会穿过反向代理。

    Web应用指传统的具有服务器端的MVC应用,例如用Spring开发的Web应用,它可以直接访问IDP。

    2020-10-22 22:35:20

  • .

    2020-08-27 23:51:35

    session存在cookie中安全吗
    作者回复

    web session可以集中存在服务器端,cookie中只需存一个sessionId。

    2020-08-28 19:30:49

  • Younger Ku

    2020-08-20 16:29:30

    我们前端用的VUE,直接就被nginx静态代理了,nginx直接转发到用spring gateway实现的网关。那么微服务架构图中的 web 对应我们系统中的那一块呢?
    作者回复

    你们的vue静态资源直接住在nginx反向代理上?如果是的话,那么这是一种简化的部署方式,如果vue应用不多,可以这么弄。但是如果单页应用很多,而且还有传统web mvc应用的话,就要考虑采用本文的独立web层(在nginx和网关之间)部署方案,这种方案会具有更好的扩展性和灵活性。

    2020-08-24 00:28:53

  • Younger Ku

    2020-08-20 14:26:18

    IDP 校验令牌通过后,将令牌和用户信息对应关系存储在redis中,同时返回给网关OAuth2.0令牌而不是包含用户信息的JWT。网关校验scope信息有权调用API就转发,API中再根据OAuth2.0令牌从redis中获取用户信息而不是解析JWT。请问这种方式可行不?另外验证scope是在网关中做还是网关在调用其他服务?
    作者回复

    理论上可以,但是你的很多服务都会集中依赖redis,这个有耦合性和扩展性问题。scope校验可以网关集中做,也可以每个服务自己再根据需要单独做。

    2020-08-24 00:41:07

  • Geek_334e32

    2020-07-29 08:48:45

    场景 2:第一方移动应用 + 授权码许可模式。 这个不合适吧?现在app登陆都是用户名和密码或者验证码模式。
    作者回复

    请看我在文中给出的解释连接,用户名/密码模式对于无线App并不安全:
    "We have learned the hard way, so let’s warn everyone else here ������ DO NOT USE grant_type=password in any place where a hacker can de-compile your app or find your client_id plus client_secret. If you create a mobile app, you MUST use the full 3-legged Oauth via an embedded web form (with captcha or similar protection on it). Never ever create a native app login screen, which can ask your server via a simple post if the used credentials are correct (e.g. like the password grant type). Having this allows someone to start massively guessing for passwords. We know, app-developers do not like non-native things, but believe me, this is the only simple way to prevent attacks AND maintain super fast flexibility in changing the login procedure without having to roll out a new app version and trying to kill off old versions of the app. Having the web form login allows us to easily add for example 2-factor authentication when we want or need to."

    2020-08-05 00:23:41

  • 阿kai(aeo

    2022-11-08 07:04:34

    "也可以采用简单的客户端 Session 方式,也称无状态 Session,也就是在客户端浏览器 Cookie 中保存 OAuth 2.0 访问令牌。" - 没有很理解老师说的这句话,如果OAuth 2.0 访问令牌放在cookie里,那不是可能会被造成访问令牌泄漏吗?
  • ascend

    2022-03-11 21:22:30

    跨域的SSO怎么处理?