OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)

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

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

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

前两篇内容:

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

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

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第1张图片

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

2、SCOPE、ROLE、AUTH 区别

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

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第2张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第3张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第4张图片

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

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第5张图片

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

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

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

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

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

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第6张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第7张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第8张图片

4、访问控制分析

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

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

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

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

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

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第9张图片

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 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 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();
    }
}

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第10张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第11张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第12张图片

  • 访问测试

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第13张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第14张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第15张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第16张图片

6、权限设计

  • 客户端
客户端分类 被授于的 SCOPE
电脑端 SCOPE_1
手机端 SCOPE_2
内部资源服务 SCOPE_0
  • 资源端
资源分类 允许访问的 SCOPE 说明
r1/res1 SCOPE_0、SCOPE_1、SCOPE_2 资源服务器1 中的 资源1,可以被三个客户端访问
r1/res2 SCOPE_0、SCOPE_1 资源服务器1 中的 资源2,只可以被电脑端、内部资源访问
r2/res1 SCOPE_0、SCOPE_2 资源服务器2 中的 资源1,只可以被手机端、内部资源访问
r2/res2 SCOPE_1、SCOPE_2 资源服务器2 中的 资源2,只可以被电脑端、手机端访问
r3/res1 SCOPE_2 资源服务器3 中的 资源1,只可以被手机端访问
r3/res2 SCOPE_0 资源服务器3 中的 资源2,只可以被内部资源访问
  • 用户与角色
用户 角色
张三 ROLE_1
李四 ROLE_2
  • 角色与权限
权限 角色
AUTH_1 ROLE_1
AUTH_2 ROLE_2
AUTH_3 ROLE_1、ROLE_2
AUTH_4 ROLE_2

ROLE_1:包含 AUTH_1、AUTH_3

ROLE_2:包含 AUTH_2、AUTH_3、AUTH_4

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第17张图片

OAuth2在分布式微服务架构下基于角色的权限设计(RBAC)_第18张图片

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

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

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

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

你可能感兴趣的:(#,SpringSecurity,OAuth2,#,SpringCloud,架构,分布式,微服务)