本篇文章给大家带来了关于鉴权的相关知识,其中主要介绍了微服务中鉴权的实现思路是什么?又有什么好的方案来实现呢?下面一起来看一下,希望对大家有帮助。
最近刚好有小伙伴在微信上问到这个问题,我就来和大家聊一聊,本文主要和小伙伴们聊一聊思路,不写代码,小伙伴们可以结合我之前的文章,应该能够自己写出来本文的代码。当然,思路也只是我自己的一点实践经验,不一定是最完美的方案,欢迎小伙伴们在留言中一起探讨。
1. 认证与授权首先小伙伴们知道,无论我们学习 shiro 还是 spring security,里边的功能无论有哪些,核心都是两个:
认证
授权
所以,我们在微服务中处理鉴权问题,也可以从这两个方面来考虑。
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. 认证服务那么认证和授权在哪里做?
先来说认证,认证我们可以简单分为两个步骤:
登录
校验
2.1 登录一般来说,登录我们可以单独做一个认证服务。当登录请求到达网关之后,我们将之转发到认证服务上,完成认证操作。
在认证服务上,我们就去检查用户名/密码是否 ok,用户状态是否都 ok,都没问题的话,生成 jwt 字符串,同时再把数据存入到 redis 上,然后把 jwt 字符串返回。
如果系统有注册功能的话,注册功能也是放在这个微服务上来完成。
2.2 校验校验是指每一个请求到达的时候,校验用户是否已经登录。
这个当然可以和 2.1 放到一起去做,但是松哥不建议。问题在于,假如是一个创建订单的请求,这个请求原本是要经过网关转发到订单服务上的,但是,此时就得先在网关上调用 2.1 小节的服务进行登录校验,没问题再转发到订单服务上,这样做很明显很费事,也不合理。
一个比较好的办法是直接在网关上去校验请求的令牌是否合法,这个校验本身也比较容易,校验令牌是否合法,我们只需要看 redis 上是否存在这个令牌,并且这个 jwt 令牌能够被顺利解析就行,这个操作完全可以在网关上做。
以 gateway 网关为例,我们可以自定义全局过滤器,在全局过滤器中校验每一个请求的令牌,校验通过了,再进行请求的转发,否则就不转发。
校验通过之后,在转发到具体的微服务之后,我们可以将解析出来的用户 id 以及用户名等信息放到请求头中,然后再转发,这样到达各个具体的微服务之后,就知道这个请求是谁发来的,这人都有哪些角色/权限,方便做下一步的权限校验。
松哥的做法是定义了一个公共模块,所有的微服务都依赖这个公共模块,这个公共模块中定义了一个拦截器,会拦截下来每一个请求,从请求头中取出用户 id,然后从 redis 中拿到具体的用户信息,存入到 threadlocal 中,这样在后续的方法调用中,如果需要判断用户是否具备某一个权限,就可以通过 threadlocal 去获取了。
大致上就是这样一个流程。
3. 授权服务授权没法放到网关上做,还是得在各个微服务上去完成。
微服务上的授权我们又可以将之大致上分为两类:
前端发送来的请求(外部请求)。
别的微服务发送来的请求(内部请求)。
3.1 外部请求对于外部请求来说,就按正常的权限校验对待就行了,自定义注解亦或者使用 spring security 等框架都是可以的,如果是自定义注解的话,就结合 aop 一起,定义切面自己去处理权限注解,当然,这些功能基本上每一个微服务都是需要的,所以可以将之抽取成为一个公共的模块,在不同的微服务中依赖即可。
3.2 内部请求对于内部的请求来说,正常是不需要鉴权的,内部请求可以直接处理。问题是如果使用了 openfeign,数据都是通过接口暴露出去的,不鉴权的话,又会担心从外部来的请求调用这个接口,对于这个问题,我们也可以自定义注解+aop,然后在内部请求调用的时候,额外加一个头字段加以区分。
当然,内部请求到达微服务的时候,也是需要进行认证的,就行请求从网关转发到每一个具体的微服务上时需要认证一样,不过很明显,我们没必要每次使用 openfeign 调用别的服务的时候,都去传一堆认证信息,我们可以通过实现 feign.requestinterceptor 接口来定义一个 openfeign 的请求拦截器,在拦截器中,统一为 openfeign 请求设置请求头信息。
好啦,关于微服务中的鉴权,我们目前是这么做的,欢迎小伙伴们留言一起探讨。
推荐学习:《小程序视频教程》《java视频教程》
以上就是一文详解怎么实现微服务鉴权的详细内容。