71

学习 Shiro(三):RBAC Realm

 4 years ago
source link: http://muziyuchen.com/shiro-3/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在组织内部,通常使用 LDAP 目录服务,为组织提供统一的认证服务。

鉴权比较认证更加的复杂,系统往往需要灵活可配置的权限。

可以通过自定义 Realm 实现以下功能:

  • 认证使用 LDAP
  • 鉴权使用 JDBC

Realm

下面是 org.apache.shiro.realm.Realm 接口继承体系:

6jQ7FnE.png!web

继承 org.apache.shiro.realm.AuthenticatingRealm 抽象类,即表示该 Realm 支持认证。

继承 org.apache.shiro.realm.AuthorizingRealm 抽象类,即表示该 Realm 支持认证和鉴权。

认证需要实现 doGetAuthenticationInfo(AuthenticationToken token):AuthenticationInfo 抽象方法,鉴权需要实现 doGetAuthorizationInfo(PrincipalCollection principals):AuthorizationInfo 抽象方法。

Shiro 提供了基于 LDAP 的 Realm 实现 org.apache.shiro.realm.ldap.DefaultLdapRealm 和基于 JDBC 的 Realm 实现 org.apache.shiro.realm.jdbc.JdbcRealm

自定义 Realm 可以通过继承 DefaultLdapRealm 类继承基于 LDAP 认证,通过覆盖基于 LDAP 授权实现基于 JDBC 的鉴权。

RBAC

RBAC(Role-based Access Control,基于角色的访问控制) 是一种限制已认证用户系统访问的方式。在 RBAC 中,实体(Subject)、角色(Role)和权限(Permissions)之间的 ER 图如下所示:

77fE3au.png!web

Subject 与 Role 是多对多的关系,Role 和 Permission 是多对多的关系。

RBAC 的特点如下:

  • 实体只能通过分配角色进行授权;
  • 可以为实体分配角色;
  • 可以为角色分配权限。

以 BI 系统为例:

小张是一名数据分析师,在 BI 系统中分配了数据分析师角色,数据分析师角色拥有编辑和查看报表的权限,小张可以编辑和查看报表。

小王是一名业务,在 BI 系统中分配了业务角色,业务角色拥有查看报表的权限,小王只可以查看报表不可以编辑报表。

LdapJdbcRealm

以 Spring Boot 集成 org.apache.shiro:shiro-spring-boot-web-starter 为例,演示如何自定义 Realm。

创建数据库表:

create table if not exists users  
(
    id          bigint auto_increment primary key,
    name        varchar(64) not null unique key
);

create table if not exists roles  
(
    id          bigint auto_increment primary key,
    name        varchar(64) not null unique key
);

create table if not exists permissions  
(
    id          bigint auto_increment primary key,
    name        varchar(64) not null unique key
);

create table if not exists user_roles  
(
    id          bigint auto_increment primary key,
    user_id     bigint not null,
    role_id     bigint not null
);

create table if not exists role_permissions  
(
    id            bigint auto_increment primary key,
    role_id       bigint not null,
    permission_id bigint not null
);

创建 RBACRealm 类:

public class RBACRealm extends DefaultLdapRealm { // ①

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }

        AuthorizationInfo info = null;

        String username = (String) getAvailablePrincipal(principals);
        if (username != null && username.trim().length() > 0) {
            try (Connection conn = this.dataSource.getConnection()) {
                Set<String> roles = queryRolesByUsername(conn, username); // ②
                Set<String> permissions = queryPermissionsByRoles(conn, roles); // ③
                SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
                simpleAuthorizationInfo.setStringPermissions(permissions);
                info = simpleAuthorizationInfo;
            } catch (IOException ie) {
                final String message = "There was a IO error while loading sql from classpath";
                throw new AuthorizationException(message, ie);
            } catch (SQLException se) {
                final String message = "There was a SQL error while authorizing user [" + username + "]";
                throw new AuthorizationException(message, se);
            }
        }

        return info;
    }

    private Set<String> queryRolesByUser(Connection conn, String username) throws SQLException, IOException {
        // 省略查询
    }

    private Set<String> queryPermissionsByUser(Connection conn, Set<String> roleNames) throws IOException, SQLException {
        // 省略查询
    }

}

① 继承 org.apache.shiro.realm.ldap.DefaultLdapRealm 类,覆盖 doGetAuthorizationInfo(PrincipalCollection principals):AuthorizationInfo 方法;

② 通过 JDBC 查询用户角色;

③ 通过 JDBC 查询用户权限。

参考


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK