集大成

  1. 客户端携带用户名密码发送登录请求到网关。
  2. 网关收到请求之后,将请求路由到统一认证中心。
  3. 统一认证中心确认用户的身份没有问题之后,将返回一个 access_token 给网关。
  4. 网关将 access_token 转发到客户端。
  5. 客户端将获取到的 access_token 放在请求头中去请求真正的微服务,当然这个操作依然会被网关拦下。
  6. 网关将客户端的请求路由到微服务上,接下来微服务需要根据 access_token 鉴定用户身份。
  7. 微服务可以调用统一认证中心去检验用户身份,如果我们采用了 JWT 的话,这一步实际上可以省略。
  8. 微服务确认了用户身份和权限之后,就可以根据实际情况返回数据给用户了。

TIP

在上面的架构图中,网关还有另外一个身份就是资源服务器,当请求到达网关之后,如果是去往统一认证中心的请求,则直接转发即可;如果请求是去往普通微服务的请求,网关可以先做初步校验,就是校验客户端身份,如果没有问题,则将请求路由到不同的微服务上,各个微服务再根据自身的业务和权限情况,进行响应。

对于一个超大型的微服务项目而言,涉及到的子系统可能非常多,权限控制也是非常复杂,网关不可能了解所有业务系统的逻辑,如果把所有的鉴权操作都放在网关上做,很明显会加大网关的复杂度,让网关变得非常臃肿。另一方面,不同的微服务可能是由不同的团队开发的,如果把每个微服务的鉴权系统放在网关上做,又会增加开发的难度,所以,我们可以先在网关对用户身份做初步校验,没问题的话,再把请求路由到不同的微服务,做具体的校验。

在这个过程中,我们可以使用普通的 access_token,就是那种一个 UUID 字符串的,如果使用了这种格式的 access_token,我们可以通过调用授权服务器来确定用户身份,也就是上图中的第七步不可以省略,这对于分布式系统来说显然不是最佳方案。结合 JWT 就可以很好的解决这个问题,JWT 中保存了用户的所有信息,微服务拿到 JWT 字符串之后,就可以很好的解析出用户的信息了

  • 微服务架构是一种分布式系统,在分布式系统中,我们经常需要将用户的信息从一个微服务传递到另外一个微服务中去,
  • 传统的 SecurityContext 这种基于 ThreadLocal 基于内存的方式显然就不太合适,因为这种方式无法灵活的在分布式系统之间传递用户信息,也无法很好的支持单点登录。

现在的微服务之间调用,例如 A 调用 B,如果是基于 Spring Cloud 架构的话,可能以 Open Feign 调用为主,这种情况下,我们可以自定义一个请求拦截器,当请求要发出的时候,自动拦截请求,然后自动向请求头中添加认证信息。

然后可以定义一个公共的注解,这个注解专门用来做校验工作,该注解可以从从请求头中提取出 A 传递来的信息进行校验。

在 B 中使用这个公共的注解即可。

当然 B 中也可以不使用注解,而是通过路径来校验,但是在这个场景下,注解反而灵活一些。

在微服务上拿到 JWT 字符串之后,是不是可以自己解析?(JWT 解析参考:Spring Security 结合 Jwt 实现无状态登录open in new window)这样就不需要 Spring Security 了?

虽然自己解析并不存在技术上的难点,但是我还是不建议自己解析,建议继续在 Spring Security 的基础上完成剩余操作。

我们拿到 JWT 之后,通过 Spring Cloud Security 来自动解析 JWT 字符串,获取用户信息,然后自动将用户信息注入 SecurityContext 中,相当于自动完成一次登录操作,然后继续后面的操作,这样自己要省事很多,而且 Spring Security 中的各种路径拦截规则我们都还可以继续使用。