业务系统中基于角色的访问控制RBAC_基于角色的访问控制-rbac - wpsshop博客
当前位置:   article > 正文

业务系统中基于角色的访问控制RBAC_基于角色的访问控制-rbac

基于角色的访问控制-rbac

RBAC 基础知识

业务系统权限控制的基本形式是基于角色的访问控制RBAC)。简化后,模型如下所示:

 

  • 主题(系统用户)有多个角色。
  • 一个角色由一组权限组成。
  • 权限由一组操作组成

具体来说,让我们看一下Redmine中的一个例子。

  • 默认情况下,可以为用户分配“管理员”、“开发者”和“报告者”角色。
  • “报告者”角色具有“添加问题”权限。
  • 具有“添加问题”权限的用户可以“创建新问题”。

该模型在 Redmine 中表示如下。

 

Redmine 允许将单个用户分配给具有不同角色的多个项目,因此模型是将上面的 Member 实体夹在中间。通过少量应用 RBAC 原则,您可以以有组织的思维方式适应复杂的业务需求。
我没有看到权限表,但它们在角色表的权限列中以逗号分隔。这就是J-Walk所谓的 SQL 反模式设计。我想这是Redmine插件端可以自由添加权限的借口。

RBAC 看起来是一个非常合理的设计,但似乎很少有人真正看到这样设计的业务系统。相信一定有人对一个让组织=角色或角色=权限的设计有想法。此外,似乎很少有框架能够正确支持 RBAC。

不针对 RBAC 进行设计时的问题

组织=角色

您可以想象为什么这不是一个好主意,组织经常变化。如果这是一个角色,每次都会需要大量的数据维护。客户经常通过组织名称向我们询问类似角色的要求,但请务必设计一个单独的角色并将其与组织映射。

角色 = 特权

没有滚动等效的模式。为用户分配权限可能会让人不知所措。由于我们还没有找到一组称为角色的业务权限,如果我们想要满足 C2 覆盖,我们将需要测试尽可能多的权限组合。

特权=操作

一个权限可以控制多个操作。如后面将要描述的,拥有一定的权限A不仅允许访问某个URL,而且它还可以用于不同的链接和按钮来到达那个URL。让我们将您想要控制权限的操作分组并将它们设计为权限。

用Java实现

Java 在 Servlet API 中也有一个角色机制,可以在业务应用程序中使用。但是...角色通常需要灵活的操作。另一个常见的要求是管理员希望能够创建和删除新的。然后,像 JSR250 和 Servlet API 一样,将角色名称附加到注解中,或者像 isUserInRole 这样将其作为参数传递,ロール名过于死板和不方便。根据 RBAC 的定义,自然能够传递“特权”。

因此,我创建了一个基于 Ninjaframework 的应用程序示例,它使用 JSR250 注解并使用权限名称而不是角色名称作为要传递给它们的参数。

示例说明

作为初始数据,准备了三种角色,并赋予以下权限。

权威
行政人员读问题、写问题、管理用户
开发商读问题,写问题
来宾阅读问题

登录时获取授权

一个现实的设计是在登录时从用户的角色中获取权限,并将它们与用户信息一起存储。因此,会话作为登录信息保存的 DTO 实现如下。

  1. @Data
  2. @RequiredArgsConstructor
  3. public class UserPrincipal implements Principal {
  4. @NonNull
  5. private String name;
  6. @NonNull
  7. private Set<String> permissions;
  8. }

在登录时,这将转换为权限并提供给会话。

  1. User user = em
  2. .createQuery("SELECT u FROM User u LEFT JOIN FETCH u.roles r LEFT JOIN FETCH r.permissions p WHERE u.account = :account", User.class)
  3. .setParameter("account", account)
  4. .getSingleResult();
  5. session.put("account", account);
  6. Set<Permission> permissions = new HashSet<>();
  7. user.getRoles().stream()
  8. .map(Role::getPermissions)
  9. .forEach(permissionsInRole -> permissions.addAll(permissionsInRole));
  10. session.put("permissions", permissions.stream().map(Permission::getName)
  11. .collect(Collectors.joining(",")));

Ninjaframework 会话存储在 cookie 中,因此应该只输入诸如 ↑ 之类的字符串(这就是不使用 Ninjaframework 的原因)。

方法权限

然后,在调度到控制器方法时,我们检查我们拥有的权限以及附加到操作方法的权限。

从身份验证过滤器中的会话恢复 UserPrincipal。

  1. Session session = context.getSession();
  2. if (session.get("account") == null) {
  3. return Results.redirect("/login");
  4. }
  5. Set<String> permissions = new HashSet<>();
  6. if (session.get("permissions") != null) {
  7. permissions = ImmutableSet.copyOf(Splitter.on(',').split(session.get("permissions")));
  8. }
  9. context.setAttribute("principal", new UserPrincipal(session.get("account"), permissions));

然后授权过滤器可以检查 UserPrincipal 是否有权访问该方法。假设需要的权限写在控制器方法中如下所示的JSR250注解中。

  1. @UnitOfWork
  2. @RolesAllowed("readIssue")
  3. public Result list(Context context) {
  4. EntityManager em = entityManagerProvider.get();
  5. List<Issue> issues = em
  6. .createQuery("SELECT i FROM Issue i", Issue.class)
  7. .getResultList();
  8. Map<String, Object> bindings = VariablesHelper.create(context);
  9. bindings.put("issues", issues);
  10. return Results.html().render(bindings);
  11. }
  12. @UnitOfWork
  13. @RolesAllowed("writeIssue")
  14. public Result newIssue(Context context) {
  15. return Results.html().render(VariablesHelper.create(context));
  16. }

授权检查如下所示:

  1. Method method = context.getRoute().getControllerMethod();
  2. RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
  3. PermitAll permitAll = method.getAnnotation(PermitAll.class);
  4. UserPrincipal principal = (UserPrincipal) context.getAttribute("principal");
  5. if (permitAll != null ||
  6. (rolesAllowed != null && contains(principal.getPermissions(), rolesAllowed.value()))) {
  7. return filterChain.next(context);
  8. } else {
  9. return Results.forbidden().html().template("views/403forbidden.ftl.html");
  10. }

现在您所要做的就是如上所述注释您的控制器方法。

在视图层按权限分发

这不是使用权限的唯一方法,但它也将用于区分菜单和按钮。您还可以检查您是否拥有来自 UserPrincipal 的必要权限,并在视图端单独发布它们。

Ninjaframework 的默认视图是 Freemarker,所以你可以这样写:

  1. <a href="#" class="header item">
  2. RBAC Example
  3. </a>
  4. <#if principal??>
  5. <a href="/" class="item">Home</a>
  6. </#if>
  7. <#if principal?? && principal.permissions?seq_contains("readIssue")>
  8. <a href="/issues/" class="item">Issue</a>
  9. </#if>
  10. <#if principal?? && principal.permissions?seq_contains("manageUser")>
  11. <a href="/users/" class="item">Users</a>
  12. </#if>

当您拥有“管理员”权限时

当您没有“管理员”权限时

我能够像这样整理出来。

如何运行示例

  1. % git clone https://github.com/kawasima/rbac-example.git
  2. % cd rbac-example
  3. % mvn compile
  4. % mvn waitt:run

您可以使用 .
哦,这个叫waitt的插件是2015年最火的插件之一,不用Spring Boot或者IDE的付费功能,不用打包web应用就可以启动。请尝试一下。

春季安全

Spring security 允许您实现 RBAC。但是,由于篇幅限制,我将省略它。

概括

尽管权限控制是业务系统中最常见的需求,但很难找到任何可以清楚地解释如何设计它的东西,而现实是有很多系统被设计成难以测试和操作的想法。我们希望本文对您的设计有所帮助。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/352210
推荐阅读
相关标签