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

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

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

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

前两篇内容:

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功能,决定需要哪些权限,应该把权限赋于哪种角色。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土味儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值