最近刚好有小伙伴在微信上问到这个问题,松哥就来和咱们聊一聊,本文主要和小伙伴们聊一聊思路,不写代码,小伙伴们能够结合松哥之前的文章,应该能够自己写出来本文的代码。当然,思路也只是我自己的一点实践经验,不一定是最完美的计划,欢迎小伙伴们在留言中一同讨论。

1. 认证与授权

首先小伙伴们知道,不管咱们学习 Shiro 仍是 Spring Security,里面的功用不管有哪些,中心都是两个:

  1. 认证
  2. 授权

所以,咱们在微服务中处理鉴权问题,也能够从这两个方面来考虑。

1.1 认证

认证,说白了便是登录。传统的 Web 登录是 Cookie+Session 的计划,这种计划依靠于服务器本地内存,在微服务中,因为服务很多,这种计划显然不再合适。

或许会有小伙伴说用 Redis+SpringSession 做 Session 共享,这是个办法,可是不是最佳计划,因为这种计划的性能以及可扩展性都比较差。

所以,微服务中的认证,仍是主张运用令牌的方法,能够挑选 JWT 令牌,这也是现在运用较多的一种计划。可是熟悉 JWT 的小伙伴都知道,朴实的无状况登录无法完成刊出,这就很头大,所以在实际应用中,单纯的运用 JWT 是不可的,一般仍是要结合 Redis 一同,将生成的 JWT 字符串在 Redis 上也保存一份,并设置过期时刻,判别用户是否登录时,需要先去 Redis 上查看 JWT 字符串是否存在,存在的话再对 JWT 字符串做解析操作,假如能成功解析,就没问题,假如不能成功解析,就说明令牌不合法。

这样有状况登录+无状况登录混在一同的方法,虽然看起来有点不三不四,可是就当下来说,这个折衷的办法算是一个可行的计划了。

其实,上面的计划,说白了,跟传统的 Cookie+Session 没什么两样,思路简直都是完全 copy 的:传统的 Session 用 Redis 替代了;传统穿梭于服务端和浏览器之间的 jsessionId 被 JWT 字符串替代了;传统的 jsessionId 经过 Cookie 来传输,现在的 JWT 则经过开发者手动设置后经过恳求头来传输;传统的 Session 能够主动续签,现在用 JWT 便是手动续签,每次恳求抵达服务端的时分,就去看下 Redis 上令牌的过期时刻,快过期了,就从头设置一下,其他都如出一辙。

这是认证计划的挑选。

1.2 授权

微服务中授权,也能够运用 Shiro 或许 Spring Security 结构来做,省劲一些。考虑到微服务技能栈都是 Spring 宗族的产品,所以在权限结构这块也是主张咱们首选 Spring Security(假如有小伙伴对 Spring Security 还不熟悉的话,能够在微信大众号后台回复 ss,有教程)。

当然,假如觉得 Spring Security 比较复杂想自己搞的话,也是能够的。自己搞的话,也是能够借助于 Spring Security 的思路的,松哥最近的一个项目便是这样:

恳求抵达微服务之后,先找到当时用户的各种信息,包括当时用户所具有的角色和权限等信息,然后存入到和当时线程绑定的 ThreadLocal 对象中。另一方面自定义权限注解和角色注解,在切面中对这些注解进行解析,查看当时用户是否具备所需要的角色/权限等。

当然,假如你运用了 Spring Security 的话,上面这个就不需要自定义注解了,直接运用 Spring Security 中自带的即可,还能够体会 Spring Security 中更多的丰厚的安全功用。

2. 认证服务

那么认证和授权在哪里做?

先来说认证,认证咱们能够简单分为两个过程:

  1. 登录
  2. 校验

2.1 登录

一般来说,登录咱们能够独自做一个认证服务。当登录恳求抵达网关之后,咱们将之转发到认证服务上,完成认证操作。

在认证服务上,咱们就去查看用户名/暗码是否 OK,用户状况是否都 OK,都没问题的话,生成 JWT 字符串,同时再把数据存入到 Redis 上,然后把 JWT 字符串返回。

假如系统有注册功用的话,注册功用也是放在这个微服务上来完成。

2.2 校验

校验是指每一个恳求抵达的时分,校验用户是否现已登录。

这个当然能够和 2.1 放到一同去做,可是松哥不主张。问题在于,假如是一个创立订单的恳求,这个恳求原本是要经过网关转发到订单服务上的,可是,此刻就得先在网关上调用 2.1 小节的服务进行登录校验,没问题再转发到订单服务上,这样做很明显很费事,也不合理。

一个比较好的办法是直接在网关上去校验恳求的令牌是否合法,这个校验本身也比较容易,校验令牌是否合法,咱们只需要看 Redis 上是否存在这个令牌,并且这个 JWT 令牌能够被顺利解析就行,这个操作完全能够在网关上做。

以 Gateway 网关为例,咱们能够自定义大局过滤器,在大局过滤器中校验每一个恳求的令牌,校验经过了,再进行恳求的转发,不然就不转发。

校验经过之后,在转发到详细的微服务之后,咱们能够将解析出来的用户 id 以及用户名等信息放到恳求头中,然后再转发,这样抵达各个详细的微服务之后,就知道这个恳求是谁发来的,这人都有哪些角色/权限,方便做下一步的权限校验。

松哥的做法是定义了一个公共模块,一切的微服务都依靠这个公共模块,这个公共模块中定义了一个拦截器,会拦截下来每一个恳求,从恳求头中取出用户 ID,然后从 Redis 中拿到详细的用户信息,存入到 ThreadLocal 中,这样在后续的方法调用中,假如需要判别用户是否具备某一个权限,就能够经过 ThreadLocal 去获取了。

大致上便是这样一个流程。

3. 授权服务

授权无法放到网关上做,仍是得在各个微服务上去完成。

微服务上的授权咱们又能够将之大致上分为两类:

  1. 前端发送来的恳求(外部恳求)。
  2. 其他微服务发送来的恳求(内部恳求)。

3.1 外部恳求

关于外部恳求来说,就按正常的权限校验对待就行了,自定义注解亦或许运用 Spring Security 等结构都是能够的,假如是自定义注解的话,就结合 AOP 一同,定义切面自己去处理权限注解,当然,这些功用基本上每一个微服务都是需要的,所以能够将之抽取成为一个公共的模块,在不同的微服务中依靠即可。

3.2 内部恳求

关于内部的恳求来说,正常是不需要鉴权的,内部恳求能够直接处理。问题是假如运用了 OpenFeign,数据都是经过接口露出出去的,不鉴权的话,又会忧虑从外部来的恳求调用这个接口,关于这个问题,咱们也能够自定义注解+AOP,然后在内部恳求调用的时分,额外加一个头字段加以区分。

当然,内部恳求抵达微服务的时分,也是需要进行认证的,就行恳求从网关转发到每一个详细的微服务上时需要认证一样,不过很明显,咱们没必要每次运用 OpenFeign 调用其他服务的时分,都去传一堆认证信息,咱们能够经过完成 feign.RequestInterceptor 接口来定义一个 OpenFeign 的恳求拦截器,在拦截器中,一致为 OpenFeign 恳求设置恳求头信息。

好啦,关于微服务中的鉴权,咱们现在是这么做的,欢迎小伙伴们留言一同讨论。