IT技术之家

首页 > iOS

iOS

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_土味儿~_微服务 权限管理 设计

发布时间:2022-10-24 17:06:05 iOS 0次 标签:架构 分布式 微服务
在前两节的基础上,对权限控制作进一步分析的分析与设计。RBAC(Role-Base Access Control,基于角色的访问控制)本篇内容基于个人理解,不当之处,欢迎批评指正。前两篇内容:【图文详解】搭建 Spring Authorization Server + Resource + Client 完整Demo【oauth2 客户端模式】Spring Authorization Server + Resource + Client 资源服务间的相互访问1、OAuth2中用户访问的基....

在前两节的基础上,对权限控制作进一步的分析与设计。

RBAC(Role-Base Access Control,基于角色的访问控制)

本篇内容基于个人理解,不当之处,欢迎批评指正。

前两篇内容:

【图文详解】搭建 Spring Authorization Server + Resource + Client 完整Demo【oauth2 客户端模式】Spring Authorization Server + Resource + Client 资源服务间的相互访问

1、OAuth2中用户访问的基本流程

用户经过认证/授权后,进入客户端(认证中心给客户端发放令牌),客户端携带令牌访问对应的资源。客户端是用户和资源之外的第三方,要想访问资源必须得到用户的允许。用户拥有资源,通过客户端去访问,把访问权限赋于给了客户端。

2、SCOPE、ROLE、AUTH 区别

SCOPE:范围;指用户授权客户端可以访问的范围。客户端只能在这个范围内去访问。是针对客户端来说的。ROLE:角色;是用户的身份。是针对用户来说的。AUTH:权限;是角色所拥有的。角色与权限是多对多的关系;一个角色可以有多个权限,一个权限也可以同时被多个角色所拥有。权限也可以直接针对于用户,如果用户不指定角色,可以直接把权限赋于用户。
区别含义面向对象
SCOPE范围客户端
ROLE角色用户
AUTH权限角色 或 用户

3、server、resource、client 中访问主体的区别

从图中可以看出,在每个系统中的访问主体及权限是不同的(这里的权限是统称,包括SCOPE、ROLE、AUTH,不仅仅指AUTH)

当用户登录后,在认证中心内,访问主体就是 第三方用户它的权限是他在认证中心中的权限,和我方系统无关

在客户端中,访问主体还是 第三方用户,权限包括:用户授于客户端的 SCOPE,以及 ROLE_USER

ROLE_USER 表示这是一个经过认证的用户,不管第三方用户在第三方系统中是什么身份,只要进入到我方系统中,就是 ROLE_USER 身份;对应于 ROLE_ANONYMOUS(未认证用户)

客户端携带令牌访问资源,在资源服务器中访问主体就是 客户端,权限只有:SCOPE;因为客户端是在用户授权下去访问的,所以在认证中心生成令牌时,只包括了用户授于的 SCOPE,不可能把用户的身份ROLE也赋于客户端。

4、访问控制分析

通过上面的分析可以发现,资源端只有 SCOPE,不可能用 ROLEAUTH 去控制用户的访问。认证中心不负责访问资源,要想通过 ROLEAUTH 去控制用户访问资源,只能在 客户端 去操作。资源API在客户端有对应的接口,要想控制资源API,就控制客户端的对应接口就可以了。只要用户能访问客户端的某个API接口,它就能访问与之对应的资源API。

资源API 面向 SCOPE 开放客户端API 面向 ROLE 或 AUTH 开放

但是,所有第三方用户,进入我方系统后,都具有 ROLE_USER 身份,身份是一样的,如何在客户端中通过 ROLEAUTH 去控制用户访问资源呢?

解决方案添加本地用户,赋于不同的 ROLEAUTH ;第三方用户与本地用户实现绑定;通过本地用户的 ROLEAUTH 去控制用户访问资源。这是三方登录的一个通用做法。

那第三方用户进入我方系统后,如何改变他的身份?把本地的 ROLEAUTH 赋给他呢?办法就是权限提升

5、客户端权限提升

第三方用户进入我方系统后,从 SecurityContextHolder 中获取第三方用户的 nameauthorities根据第三方用户的 name ,查询绑定的本地用户,进而得到本地用户的 authorities把本地 authorities 加入到 第三方用户的 authorities 中重新生成新的 Authentication注入 SecurityContextHolder中,替换原来的 authorities,完成权限提升
public class IndexController {
    @Autowired
    UserDetailsService userDetailsService;

    @GetMapping("/")
    public String user(Model model) {
        // 从安全上下文中获取登录信息,返回给model
        Map<String, Object> map = new HashMap<>(5);

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        String username = auth.getName();
        map.put("当前用户", username);
        map.put("原来权限", auth.getAuthorities());

        // 使用Set,不使用List;List可以存重复元素;登录后,在首页刷新,List会重复添加
        //List<GrantedAuthority> authorities = new ArrayList<>(auth.getAuthorities());
        Set<GrantedAuthority> authorities = new HashSet<>(auth.getAuthorities());

        // 根据三方用户查绑定的本地用户
        String localUser = getLocalUser(username);
        UserDetails userDetails = userDetailsService.loadUserByUsername(localUser);
        map.put("本地用户", localUser);
        // 本地用户权限
        //List<GrantedAuthority> authorities1 = new ArrayList<>(userDetails.getAuthorities());
        Set<GrantedAuthority> authorities1 = new HashSet<>(userDetails.getAuthorities());
        map.put("本地用户权限", authorities1);
        // 把本地用户权限加入原来权限集中
        authorities.addAll(authorities1);
        map.put("新的权限", authorities);
        // 生成新的认证信息
        Authentication newAuth = new OAuth2AuthenticationToken((OAuth2User) auth.getPrincipal(),authorities,"myClient");
        // 重置认证信息
        SecurityContextHolder.getContext().setAuthentication(newAuth);
        model.addAttribute("user", map);
        return "index";
    }

    /**
     * 模拟通过第三方用户,得到本地用户
     * @param remoteUsername
     * @return
     */
    private String getLocalUser(String remoteUsername){
        String u = "";
        // 模拟通过三方用户查本地用户
        if(StringUtils.isNotEmpty(remoteUsername)){
            u = "local_admin";
        }
        return u;
    }
}
@Configuration
public class SecurityConfiguration {
    /**
     * 虚拟一个本地用户
     *
     * @return UserDetailsService
     */
    @Bean
    UserDetailsService userDetailsService() {
        return username -> User.withUsername("local_admin")
                .password("123456")
                .roles("TEST","ABC")
                //.authorities("ROLE_ADMIN", "ROLE_USER")
                .build();
    }
}

访问测试

6、权限设计

客户端
客户端分类被授于的 SCOPE
电脑端SCOPE_1
手机端SCOPE_2
内部资源服务SCOPE_0
资源端
资源分类允许访问的 SCOPE说明
r1/res1SCOPE_0、SCOPE_1、SCOPE_2资源服务器1 中的 资源1,可以被三个客户端访问
r1/res2SCOPE_0、SCOPE_1资源服务器1 中的 资源2,只可以被电脑端、内部资源访问
r2/res1SCOPE_0、SCOPE_2资源服务器2 中的 资源1,只可以被手机端、内部资源访问
r2/res2SCOPE_1、SCOPE_2资源服务器2 中的 资源2,只可以被电脑端、手机端访问
r3/res1SCOPE_2资源服务器3 中的 资源1,只可以被手机端访问
r3/res2SCOPE_0资源服务器3 中的 资源2,只可以被内部资源访问
用户与角色
用户角色
张三ROLE_1
李四ROLE_2
角色与权限
权限角色
AUTH_1ROLE_1
AUTH_2ROLE_2
AUTH_3ROLE_1、ROLE_2
AUTH_4ROLE_2

ROLE_1:包含 AUTH_1、AUTH_3

ROLE_2:包含 AUTH_2、AUTH_3、AUTH_4

客户端与资源的访问绑定关系是一一对应的,应该相应稳定。客户端能访问某个资源就提供一个接口。不能随时修改。

用户通过角色访问客户端中的服务API,这个关系比较灵活, 相对松散。客户端中只需指定某个接口可以被哪些AUTH访问即可。

角色ROLE与权限AUTH的关系相对稳定,但可以比客户端和资源的关系灵活,可以修改编辑。

在项目设计阶段,应该首先确定客户端的种类,再基本确认项目中所涉及的角色。根据资源API功能,决定需要哪些权限,应该把权限赋于哪种角色。