公司各种各样的系统太多,不同系统不同的账号密码,用户需要记很多账号密码,体验太差
现准备建立一个用户管理的系统,把用户管理都放在 oauth 授权服务器集中管理,准备用 oauth2 实现单点登录,但是如果用 oauth2,那么我如何控制各自系统的更细粒度的权限?比如控制各自系统的菜单权限、数据权限,是不是 授权服务器仅作基本的用户管理以及单点登录,各自业务系统之中也要维护一个 RBAC 权限控制的表?各自系统的更细粒度权限由业务系统自己实现?
有没有做过类似需求的老哥给点建议
1
mengdodo 2022-11-02 10:12:41 +08:00 1
是的呢,我那个业务不怎么复杂的项目是这样,oauth2 只需要授权返回用户信息
|
2
boris93 2022-11-02 10:27:57 +08:00 via iPhone
我司是在 LDAP 里面建各种组,对应各个应用的权限级别
然后把授权的组放在 JWT 的 scope 里面,应用读 scope 看有没有权限 缺点就是,如果管理不善,权限太多的话,scope 会巨长,曾经因为这个,有个人就死活登录不进应用,删了俩不用的 scope 才解决 |
3
chendy 2022-11-02 10:31:40 +08:00
用户管理系统只提供”用户是谁“,”用户有啥权限“由业务系统自行判断
有用户系统前:登陆业务系统 -> 业务系统做权限判断 有用户系统后:登陆用户系统 -> 单点登录登陆业务系统 -> 业务系统做权限判断 |
4
wuxqing 2022-11-02 10:31:51 +08:00
oauth2 不管权限的事情。权限可以通过 AOP 或 API 网关。
|
5
adoal 2022-11-02 10:33:51 +08:00 2
authentication 和 authorization 本来就是两件事
|
6
ChenSino OP @chendy 看你这回复我好像有点明白了,我一直把 OAuth 里面的 resource 授权理解成了普通系统里面的权限管理,我开始以为菜单(权限)就是 resource ,所以所有的客户端系统菜单权限应该放到 Oauth 管理,这里我理解应该是有误。
|
7
ChenSino OP @adoal 这两个概念我还是知道的,我纠结的点在于 oauth 这个东西本身就是授权也就是 authorization ,所以要不要把业务系统的授权放在 oauth 授权服务器这里
|
10
wuxqing 2022-11-02 10:49:25 +08:00
@ChenSino 你可以把 OAuth 看成公司的门禁卡,它只是授权你能不能进入公司大门。公司内的权限细则,需要另一套体系,毕竟每个部门情况不一,单独处理更合理些。
|
11
fkdog 2022-11-02 10:50:29 +08:00
权限控制是各个业务自己去负责维护的。
你搞 oAuth 难道还要细到负责管食堂餐厅货物上下架权限么? 做个认证就够了。 而且,公司内网账号管理这种一般都用 LDAP ,大部分类似 gitlab 、jenkins 、jira 一类的工具都有现成 LDAP 插件集成,都不需要你对接。 |
14
fkdog 2022-11-02 10:58:55 +08:00
你的系统除了全局统一认证外,还有个全局单点登录。
单点登录也是需要独立实现的,这块花样也很多。 |
15
wangxiaoaer 2022-11-02 11:01:04 +08:00
楼主这个问题我考虑很久了,也一直再找方案,但是目前都貌似都没有成品可以直接用。
说说我得想法吧,不一定对: 1 集中认证+分散授权+分散鉴权 这种模式需要一个统一的认证系统,各个子系统通过 OAuth2 进行接入认证。认证通过后在各个子系统内部实现授权+鉴权,可以通过认证系统返回的 OpenID 之类绑定。这种模式感觉对各个子系统改动最小,但是每个子系统需要做各自的授权管理。 2 集中认证+集中授权+分散鉴权 认证同上,同时在这个认证系统里面对用户授权,可能是比较简单的角色绑定之类。认证通过后在各个子系统通过认证系统返回的权限(角色)信息进行鉴权。这种模式的问题在于鉴权和授权分开了,所以权限有哪些类型、命名之类需要同步。 3 集中认证+集中授权+集中鉴权 认证同上。授权和鉴权可以合并成一个服务, 跟认证独立。这种模式下的难点是鉴权,尤其是做到数据粒度的访问控制。我的思路是借鉴 ABAC 这种模式,所有的权限都通过规则进行控制,这个规则可以结合请求头、请求参数、上下文环境、当前用户等等,规则和路由绑定。问题的焦点就在于针对某个请求,在一个脱离了具体业务的共用服务中,是否可以通过规则充分表达各种复杂的权限要求。我觉得要看情况: 3.1 前置操作可以:类似`put /user/1`这种目标资源( user )明确、操作(增删改)明确的前置请求都可以通过规则控制。 3.2 后置操作有难度:类似`get /user`这种查询我称之为后置操作,因为我们有可能需要对查询的结果做过滤,考虑到分页等,这个过滤最好还是不要放在鉴权这里做。所以这种情况,权限控制规则不仅要能够控制访问,还要考虑往请求头添加额外的控制参数,然后连同原本请求参数一起转发到原始服务。 总体上来看,第 3 中是比较理想的,但是要求规则有一定的表达能力。 拙见,不一定对。 |
16
luomao 2022-11-02 11:10:58 +08:00
公司目前用户授权中心就是我做的( java 实现),我大概说一下思路:
1 、授权中心只做一件事,那就是授权。 2 、提供多种授权方式:账号密码、短信、微信等 3 、授权完成后使用 JWT 封装用户信息:id 、租户等 4 、对外开放一个 SDK 主要做校验、JWT 解析,如果涉及到服务间调用( feign 之类),还需要处理 token 的服务间传递、子线程共享等(可以封装成一个对象,在拦截器中解析后放到 threadLocal 中)。校验主要有两种方案,oauth2 自带的 restful 接口校验,如果你知道自己的 token 加密方式也可以封装到 SDK 中进行校验 5 、SDK 提供鉴权注解(类 shiro 那种形式的注解),具体注解切面实现鉴权逻辑由接入 SDK 的系统处理 6 、如果你们系统对外有 gateway ,JWT 校验可以放到 gateway 做。 |
17
ChenSino OP @wangxiaoaer 认真看了你的思路以及楼上各位的回复,我感觉还是你的第一种方案最符合我的需求,1. 此方案实现较简单,对现有系统改动最小; 2. 我司的业务过于复杂,不便于把业务数据权限和菜单权限与授权中心服务进行耦合,我的想法是授权中心一旦稳定后就不能再动了,不能因为又新增了其他服务再来授权服务器修改授权这部分逻辑。
所以综合来第一种是最具性价比的方法 |
18
adoal 2022-11-02 11:25:35 +08:00
@ChenSino oauth 发明出来是用于给用户自行决定是否需要把自己在一个业务系统中所拥有的资源授权给自己在另一个业务系统中的帐号来跨系统访问的。后来被简化了用其中的一些 API 子集做跨系统认证。单个系统内部的访问控制逻辑是业务内部自己的逻辑。
|
19
ChenSino OP @luomao 感谢老哥提供思路,我也是 java 实现,目前考虑 Nacos 、Gateway 、Security 、OAUTH2 这一套来实现,希望少遇到点坑
|
20
ChenSino OP @adoal `是否需要把自己在一个业务系统中所拥有的资源授权给自己在另一个业务系统中的帐号来跨系统访问的`看到你这句话我好像明白了。。。。
|
22
zzl22100048 2022-11-02 11:53:13 +08:00
用 oauth2 那套做登录标准流程是 OIDC ( OpenID Connect ),一般细粒度权限不在这里做
|
23
kaing 2022-11-02 15:39:23 +08:00
可以搜一下业权一体化相关的内容
|
24
siaronwang 2022-11-02 17:41:26 +08:00
casbin,casdoor
|
25
ChenSino OP @luomao 老哥老哥,授权完成后,拿到 JWT ,这个 jwt 应该是不包含业务系统的权限吧,那业务系统的数据权限和菜单是如何处理呢?解析 jwt 拿到用户名,再根据用户名查询自己业务系统的权限吗?
|
26
luomao 2022-11-10 14:49:29 +08:00
@ChenSino 对,这里只有授权中心的用户信息。权限之类的完全可以在你的业务系统中拦截器中做,jwt 解析后获取到用户与租户信息,去业务系统中的缓存中查找是否有权限数据,没有就缓存一份到 Redis 中(提供一个接口给前端获取菜单渲染问题解决了),然后取缓存的权限写一份包含用户权限等的用户信息到 threadLocal 中,并设置子线程共享,这样在你的调用链路中任何位置的 threadLocal 都有用户信息。
权限也是基于 threadLocal 中的用户权限,实现几个注解(检验当前用户是否有角色、权限),然后用 aop 或者拦截器校验被注解方法上的权限值、角色值与 threadLocal 中的用户信息,不符合条件的就异常出去 |
28
ChenSino OP 非常感谢 @luomao 老哥提供的思路,按照老哥的思路,要重写 OAuth2AuthenticationProcessingFilter 过滤器其中获取 Authentication 这部分逻辑就可以了,
```java public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { final boolean debug = logger.isDebugEnabled(); final HttpServletRequest request = (HttpServletRequest) req; final HttpServletResponse response = (HttpServletResponse) res; try { //重写这里,改成业务模块获取 Authentication 的逻辑,用 token 的话就是要重写 org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor#extract 方法了 Authentication authentication = tokenExtractor.extract(request); if (authentication == null) { if (stateless && isAuthenticated()) { if (debug) { logger.debug("Clearing security context."); } SecurityContextHolder.clearContext(); } if (debug) { logger.debug("No token in request, will continue chain."); } } …… ``` 我准备按照这个思路实践以下,java 小伙伴如果有类似问题可以做个参考,此贴终结了吧。 |