若依框架之数据权限的设计和编写

​ 在实际开发中,需要设置用户只能查看哪些部门的数据,这种情况一般称为数据权限。 ​ 例如对于销售,财务的数据,它们是非常敏感的,因此要求对数据权限进行控制, 对于基于集团性的应用系统而言,就更多需要控制好各自公司的数据了。如设置只能看本公司、或者本部门的数据,对于特殊的领导,可能需要跨部门的数据, 因此程序不能硬编码那个领导该访问哪些数据,需要进行后台的权限和数据权限的控制。

提示

默认系统管理员admin拥有所有数据权限(userId=1),默认角色拥有所有数据权限(如不需要数据权限不用设置数据权限操作)

1. 基础依赖导入

首先,我们需要确保项目中的 pom.xml 只包含实现角色管理和 RBAC 权限控制所需的基础依赖。

pom.xml 基础依赖

 <dependencies>
        <!-- Spring Boot 相关依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!-- MyBatis 相关依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!-- Shiro 依赖 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.7.0</version>
        </dependency>

        <!-- MySQL 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
<!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

详解

  • Spring Boot Starter Web: 提供创建 REST API 和 MVC 应用程序所需的基本功能。
  • Spring Boot Starter Data JPA: 提供与数据库进行交互的基本功能,并使用 JPA(Java Persistence API)进行 ORM(对象关系映射)。
  • MyBatis Starter: 这是 MyBatis 的 Spring Boot Starter,它简化了与数据库的交互,并允许使用传统的 SQL 查询。
  • Apache Shiro: 用于实现安全框架,提供了身份验证、授权、加密等安全功能。
  • MySQL Connector: MySQL 的 JDBC 驱动程序,用于连接和操作 MySQL 数据库。

2. 配置文件

application.ymlapplication.properties 中配置数据库连接和 Shiro 相关的设置。

application.yml

yaml复制代码spring:
  datasource:
    url: jdbc:mysql://localhost:3306/rbac_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: yourpassword
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  type-aliases-package: com.example.rbac.domain

shiro:
  user:
    login-url: /login
    success-url: /index
    unauthorized-url: /403

详解

  • spring.datasource: 定义数据库连接参数,包括连接 URL、用户名、密码和驱动程序类名。
  • mybatis.mapper-locations: 指定 MyBatis Mapper XML 文件的位置。
  • mybatis.type-aliases-package: 指定 MyBatis 使用的实体类所在的包。
  • shiro.user.login-url: Shiro 登录 URL。
  • shiro.user.success-url: 登录成功后重定向的 URL。
  • shiro.user.unauthorized-url: 未授权时重定向的 URL。

3. 数据库表结构设计

我们将设计三个核心表,用于实现 RBAC 权限控制:

  1. 用户表 (sys_user): 存储用户的基本信息。
  2. 角色表 (sys_role): 存储角色的基本信息。
  3. 用户-角色关联表 (sys_user_role): 存储用户与角色的关联关系。

此外,我们还会创建权限表和角色-权限关联表来支持更细粒度的权限控制。

sys_user

sql复制代码CREATE TABLE sys_user (
    user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL,
    salt VARCHAR(50) NOT NULL,
    status TINYINT(1) DEFAULT 1,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

详解

  • user_id: 用户的唯一标识。
  • username: 用户的登录名,唯一。
  • password: 加密存储的密码。
  • salt: 密码加密时使用的盐值,用于增强安全性。
  • status: 用户状态(例如,1 表示启用,0 表示禁用)。
  • create_timeupdate_time: 自动记录创建和更新的时间。

sys_role

sql复制代码CREATE TABLE sys_role (
    role_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    role_name VARCHAR(50) NOT NULL UNIQUE,
    role_key VARCHAR(50) NOT NULL UNIQUE,
    status TINYINT(1) DEFAULT 1,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

详解

  • role_id: 角色的唯一标识。
  • role_name: 角色名称(例如,管理员、普通用户)。
  • role_key: 角色标识,用于程序内部引用。
  • status: 角色状态(例如,1 表示启用,0 表示禁用)。
  • create_timeupdate_time: 记录创建和更新的时间。

sys_user_role

sql复制代码CREATE TABLE sys_user_role (
    user_id BIGINT NOT NULL,
    role_id BIGINT NOT NULL,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES sys_user(user_id) ON DELETE CASCADE,
    FOREIGN KEY (role_id) REFERENCES sys_role(role_id) ON DELETE CASCADE
);

详解

  • user_id: 关联到 sys_user 表的用户标识。
  • role_id: 关联到 sys_role 表的角色标识。
  • PRIMARY KEY: 复合主键,确保每个用户-角色组合唯一。
  • FOREIGN KEY: 外键约束,确保用户和角色存在,并在删除用户或角色时自动删除关联记录。

4. 数据库表结构扩展(权限相关表)

为了支持更细粒度的权限控制,我们还需要创建权限表和角色-权限关联表:

sys_permission

sql复制代码CREATE TABLE sys_permission (
    permission_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    permission_name VARCHAR(50) NOT NULL UNIQUE,
    permission_key VARCHAR(50) NOT NULL UNIQUE,
    status TINYINT(1) DEFAULT 1,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

详解

  • permission_id: 权限的唯一标识。
  • permission_name: 权限名称(例如,查看用户、编辑用户)。
  • permission_key: 权限标识,用于程序内部引用。
  • status: 权限状态(例如,1 表示启用,0 表示禁用)。
  • create_timeupdate_time: 记录创建和更新的时间。

sys_role_permission

sql复制代码CREATE TABLE sys_role_permission (
    role_id BIGINT NOT NULL,
    permission_id BIGINT NOT NULL,
    PRIMARY KEY (role_id, permission_id),
    FOREIGN KEY (role_id) REFERENCES sys_role(role_id) ON DELETE CASCADE,
    FOREIGN KEY (permission_id) REFERENCES sys_permission(permission_id) ON DELETE CASCADE
);

详解

  • role_id: 关联到 sys_role 表的角色标识。
  • permission_id: 关联到 sys_permission 表的权限标识。
  • PRIMARY KEY: 复合主键,确保每个角色-权限组合唯一。
  • FOREIGN KEY: 外键约束,确保角色和权限存在,并在删除角色或权限时自动删除关联记录。

总体思路:

1.总共有五个表:用户表,角色表,用户角色关联表,角色权限关联表,权限表
2.用户如何拿到一个权限呢?
3.通过用户Id去查询用户角色关联表,可以得到该用户所拥有的角色Id,这个可以是一对多的关系,即一个用户可以拥有多个角色。
4.根据查询出来的角色id去查询角色权限关联表,可以得到拥有的权限id,当然这也是一对多的关系,所以一个角色就可以拥有多个权限。
5.最后根据权限id去查询权限表,这样就可以得到所拥有的权限了

这样设计的好处:

符合了RBAC原则

1. 灵活性和可扩展性

  • 多对多关系:通过中间关联表(用户角色关联表和角色权限关联表),实现了用户与角色、角色与权限之间的多对多关系。这种设计允许一个用户拥有多个角色,一个角色也可以拥有多个权限,反之亦然,从而提供了更大的灵活性。
  • 易于扩展:当需要添加新角色或权限时,只需在相关的角色表或权限表中添加记录,并在关联表中建立关联即可,无需修改已有结构。

2. 权限管理简化

  • 角色分配:将权限与角色进行绑定后,可以通过为用户分配角色来间接赋予权限,这简化了权限的管理,尤其是当多个用户需要相同的权限时,只需赋予他们相同的角色即可。
  • 权限继承:在需要修改某一类用户的权限时,只需调整角色与权限的关联即可,所有拥有该角色的用户都会立即生效。

3. 数据库查询效率

  • 分级查询:通过多次查询获取权限信息,这种设计有助于减少单次查询的复杂度,且每次查询都基于索引优化,可以提高查询效率。

用户如何获取权限

  1. 通过用户ID查询用户角色关联表:首先,根据用户的ID从用户角色关联表中查询出该用户所拥有的所有角色ID。
  2. 根据角色ID查询角色权限关联表:接下来,通过这些角色ID在角色权限关联表中查询对应的权限ID。
  3. 根据权限ID查询权限表:最后,根据权限ID从权限表中查询出具体的权限信息。

总结

这种设计将用户、角色和权限分离开来,通过中间表实现多对多关联,既提高了系统的灵活性和可扩展性,又简化了权限的管理和维护工作,是一种常见且有效的RBAC实现方式。

RBAC原则:

RBAC(基于角色的访问控制,Role-Based Access Control)的基本原则主要围绕角色、权限、用户之间的关系进行管理和控制,具体原则如下:

1. 最小权限原则(Principle of Least Privilege)

  • 描述:每个用户应该只被授予完成其工作所需的最低权限。
  • 目的:防止用户获取超过其职责范围的权限,从而减少潜在的安全风险。

2. 职责分离原则(Separation of Duties)

  • 描述:通过将特定权限分配给不同的角色,确保一个用户不能同时完成所有关键任务,从而防止欺诈或错误行为。
  • 目的:确保重要操作需要多个角色协作完成,避免单点故障或恶意行为。

3. 权限与角色分离原则(Role-Permission Separation)

  • 描述:权限应与角色分离,而不直接与用户绑定,用户通过其角色获得权限。
  • 目的:简化权限管理,通过角色的权限配置来间接控制用户的权限,方便统一管理和维护。

4. 角色继承原则(Role Hierarchy Principle)

  • 描述:角色可以有继承关系,较高层级的角色可以继承低层级角色的权限。
  • 目的:简化角色的管理,使得一些通用权限可以通过角色继承而共享。

5. 角色互斥原则(Mutually Exclusive Roles)

  • 描述:某些角色之间是互斥的,某个用户不能同时拥有这类互斥的角色,以防止冲突或滥用。
  • 目的:避免因为角色之间的冲突导致权限管理混乱或出现安全漏洞。

6. 最小角色原则(Principle of Least Role)

  • 描述:每个用户应当只被赋予其当前任务所需的最少数量的角色。
  • 目的:进一步细化权限控制,确保用户不因拥有过多角色而获得不必要的权限。

7. 基于角色的权限聚合(Permission Aggregation Based on Roles)

  • 描述:权限不应直接分配给用户,而应聚合到角色中,用户通过角色获得权限。
  • 目的:使权限分配和管理更有条理,便于系统的权限架构设计和优化。

8. 临时角色原则(Temporary Roles Principle)

  • 描述:有些角色可以是临时的,用户在特定时间段或任务期间拥有这些角色,任务完成后角色会被撤销。
  • 目的:减少长期暴露在系统中的权限,降低风险。

总结

RBAC原则确保了权限管理的有效性和安全性,通过合理配置角色和权限,RBAC可以帮助组织实现更安全、更精细化的访问控制,符合安全管理和合规性要求。

具体代码实现:

通过shiro框架的认证和授权协议

拥有什么权限访问什么数据!

注意:巨多坑!!!
1.shiro框架要实现授权必须是在登录的情况下
2.授权的配置规则必须在认证规则上面,不然会重定向
3.一定要写登陆页面,拦截会跳转到登录页面!
4.登录的请求要给通过,不需要拦截,即给游客身份
5.授权必须是perms[a:b]这样的格式,不然会出错!

必须必须是 元素一:元素二这样的格式!!! 必须有冒号!

注意点配置大概如下:

//
        filterChainDefinitionMap.put("/login","anon");//放过登录的请求

//授权拦截规则必须在认证拦截上面定义,不然授权会被重定向到登录页面 大坑!!!
        filterChainDefinitionMap.put("/test/find","perms[View:Users]"); //认证了的用户才可以授权!!! 切记
        filterChainDefinitionMap.put("/**", "authc"); //拦截其他的请求

具体实现:

1.导入依赖

版本不行就往下降!!!

       <!-- Shiro 依赖 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.7.0</version>
        </dependency>

2.编写配置类

一个Realm类

package com.example.ruoyirbac.config;

import com.example.ruoyirbac.mapper.SysPermissionMapper;
import com.example.ruoyirbac.pojo.*;
import com.example.ruoyirbac.service.SysRolePermissionService;
import com.example.ruoyirbac.service.SysRoleService;
import com.example.ruoyirbac.service.SysUserRoleService;
import com.example.ruoyirbac.service.SysUserService;
import org.apache.catalina.User;
import org.apache.catalina.realm.AuthenticatedUserRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {
    @Autowired
    SysUserService service;

    @Autowired
    SysRoleService roleService;

    @Autowired
    SysUserRoleService userRoleService;

    @Autowired
    SysRolePermissionService rolePermissionService;

    @Autowired
    SysPermissionMapper mapper;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        System.out.println("进入授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

//        // 获取当前登录的用户对象
//        Subject subject = SecurityUtils.getSubject();// 获取对象
//
//        // 从用户对象中获取认证时传递过来的用户信息(User对象)
//        SysUser principal = (SysUser) subject.getPrincipal();//接收认证传过来的对象
//
//        System.out.println("获取到的用户是:"+principal);
//
//        //根据用户查询用户角色表的到角色ID
//
//        SysUserRole sysUserRole = userRoleService.selectById(principal.getUser_id());
//
//        System.out.println("角色ID为:"+sysUserRole.getRoleId());
//
//        //根据角色ID和角色权限表获取权限ID
//
//        SysRolePermission sysRolePermission = rolePermissionService.selectById(sysUserRole.getRoleId());
//
//        System.out.println("权限ID为:"+sysRolePermission.getPermissionId());
//
//        //根据权限ID获取权限
//
//        SysPermission sysPermission = mapper.selectById(sysRolePermission.getPermissionId());
//
//        System.out.println("权限为:"+sysPermission);
//
//        info.addStringPermission(sysPermission.getPermissionName());



        info.addStringPermission("View:Users"); //授权协议
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("进入了认证");
        //验证token

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;//强转为用户的认证

        SysUser user = service.findByUsername(token.getUsername());

        System.out.println(user);

        //账号认证自己做
        if(user == null)
        {
            return null;//会自带抛出异常
        }
        //密码认证,shiro来做
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");  //把user传递给授权
    }

}

config配置类:

package com.example.ruoyirbac.config;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    // 1. 创建一个ShiroFilterFactoryBean的Bean
    @Bean
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 设置SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 定义拦截规则
        Map<String, String> filterChainDefinitionMap = new HashMap<>();

//
        filterChainDefinitionMap.put("/login","anon");//放过登录的请求

//授权拦截规则必须在认证拦截上面定义,不然授权会被重定向到登录页面 大坑!!!
        filterChainDefinitionMap.put("/test/find","perms[View:Users]"); //认证了的用户才可以授权!!! 切记
        filterChainDefinitionMap.put("/**", "authc"); //拦截其他的请求



        /*
         anon(匿名):不需要身份验证即可访问。

        authc(身份验证):需要身份验证才能访问。

        authcBasic(基本身份验证):需要HTTP基本身份验证才能访问。

        user(用户):只有已记住的用户才能访问,或者已经身份验证的用户也可以。

        perms(权限):需要指定的权限才能访问。例如:"perms[user:create]" 表示需要user:create权限。

        roles(角色):需要指定的角色才能访问。例如:"roles[admin]" 表示需要admin角色。

        ssl(SSL):需要SSL连接才能访问。

        rest(REST):基于REST风格的URL权限控制,自动根据请求的方法(如GET、POST、DELETE等)进行权限控制。

        port(端口):访问的URL的端口必须匹配才允许访问。*/

        // 如果需要更复杂的权限控制,可以添加更多规则,例如:
        // filterChainDefinitionMap.put("/admin/**", "roles[admin]"); // admin目录下的所有资源需要admin角色

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        // 设置登录页面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");



        return shiroFilterFactoryBean;
    }

    // 2. 创建一个DefaultWebSecurityManager的Bean
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("userRealm") Realm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    // 3. 定义一个Realm对象
    @Bean(name = "userRealm")
    public UserRealm userRealm() {
        return new UserRealm();
    }

    // 注意:UserRealm需要您自己实现,并且需要继承org.apache.shiro.realm.AuthorizingRealm
    // 在UserRealm中,您需要重写doGetAuthorizationInfo和doGetAuthenticationInfo方法
    // 来实现权限和身份验证逻辑
}

登陆控制层编写

    @RequestMapping("/login")
    public String login(@RequestParam("username")String username,
                         @RequestParam("password")String password){

        Subject currentUser = SecurityUtils.getSubject();

        if (!currentUser.isAuthenticated()) {
            //如果还未认证,配置token
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);

            try {
                currentUser.login(token);
                System.out.println("1");
                return "index.html";
            }
            catch (UnknownAccountException uae)
            {
                System.out.println("2");

                return "login.html"; //错误就返回登录页面
            }
            catch (IncorrectCredentialsException ice)
            {
                System.out.println("3");

                return "login.html";
            }
        }

        return "index.html"; //正确就进入主页面


    }

shiro整合thymeleaf

实现html元素特定角色特定看

在 Thymeleaf 中集成 Shiro,可以通过使用 thymeleaf-extras-shiro 提供的自定义标签来控制页面元素的显示与隐藏。下面是一个示例 HTML 页面,展示了如何使用这些标签。

1. 引入 Thymeleaf Shiro Namespace

首先,在 HTML 页面顶部引入 Shiro 的命名空间:

<html xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <title>Shiro & Thymeleaf Example</title>
</head>
<body>

2. 使用 Shiro 标签控制页面显示

<!-- 仅认证用户可见 -->
<shiro:authenticated>
    <p>Welcome, authenticated user!</p>
</shiro:authenticated>

<!-- 仅未认证用户可见 -->
<shiro:notauthenticated>
    <p>Please <a th:href="@{/login}">log in</a>.</p>
</shiro:notauthenticated>

<!-- 用户具有特定角色时可见 -->
<shiro:hasRole name="admin">
    <p>You are an administrator.</p>
</shiro:hasRole>

<shiro:lacksRole name="admin">
    <p>You are not an administrator.</p>
</shiro:lacksRole>

<!-- 用户具有特定权限时可见 -->
<shiro:hasPermission name="document:read">
    <p>You have permission to read documents.</p>
</shiro:hasPermission>

<shiro:lacksPermission name="document:read">
    <p>You do not have permission to read documents.</p>
</shiro:lacksPermission>

3. 示例页面内容

综合以上标签,一个完整的示例页面可能如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <title>Shiro & Thymeleaf Example</title>
</head>
<body>

<shiro:authenticated>
    <h2>Welcome, authenticated user!</h2>
    <p>Thank you for logging in.</p>
    
    <shiro:hasRole name="admin">
        <p>You are an administrator.</p>
    </shiro:hasRole>

    <shiro:hasPermission name="document:read">
        <p>You have permission to read documents.</p>
    </shiro:hasPermission>
</shiro:authenticated>

<shiro:notauthenticated>
    <h2>Welcome, Guest!</h2>
    <p>Please <a th:href="@{/login}">log in</a> to access more features.</p>
</shiro:notauthenticated>

</body>
</html>

4. 运行效果

  • 如果用户已认证,他们会看到 "Welcome, authenticated user!" 的消息。
  • 如果用户具有 admin 角色,他们会看到 "You are an administrator." 的消息。
  • 如果用户具有 document:read 权限,他们会看到相应的权限提示。
  • 如果用户未认证,则会提示登录信息。

通过这种方式,Shiro 可以与 Thymeleaf 模板很好地结合,提供基于角色和权限的内容控制。

通过Shiro来实现的大致思路

通过Apache Shiro实现RBAC的基本思路如下:

1. 用户认证(Authentication)

  • 用户登录:首先通过用户名和密码进行用户身份验证。Shiro会根据用户输入的凭证(通常是用户名和密码)与系统中的信息进行匹配。
  • Realm配置:自定义Realm,用于从数据库或其他存储中获取用户信息,并实现认证逻辑。

2. 角色授权(Authorization)

  • 定义角色和权限:在数据库中设计并存储角色与权限的关系。
  • 基于角色的授权:Shiro支持通过角色进行授权,即检查用户是否拥有某个角色,决定其是否可以执行某些操作。
  • 基于权限的授权:Shiro也支持更细粒度的授权,即检查用户是否拥有某个具体的权限。

3. 权限控制配置

  • 配置文件:在Shiro的配置文件(如shiro.ini或Spring配置文件)中,配置URL与角色或权限的映射关系。例如:

    [urls]
    /admin/** = roles[admin]
    /edit/** = perms[edit]
    
  • 注解方式:在代码中使用Shiro的注解(如@RequiresRoles@RequiresPermissions)来控制方法的访问权限。例如:

    @RequiresRoles("admin")
    public void adminMethod() {
        // 管理员操作
    }
    

4. 权限校验流程

  • 用户访问某个资源时,Shiro会根据预先配置好的角色或权限映射,检查当前用户是否具备访问该资源的权限。
  • 如果用户没有相应的权限,Shiro将拒绝该请求并返回相应的错误提示(如403 Forbidden)。

5. 会话管理和单点登录

  • 会话管理:Shiro提供了会话管理功能,可以管理用户的登录状态、超时处理等。
  • 单点登录(SSO):通过Shiro的扩展,支持单点登录功能,使用户在多个系统间无缝切换。

6. 自定义扩展

  • 自定义Realm:通过继承Shiro的AuthorizingRealm类,可以自定义用户的认证和授权逻辑,支持从数据库或其他存储中动态加载角色和权限。
  • 灵活配置:通过Shiro的配置文件或Spring集成,可以灵活配置各种权限控制策略。

总结

通过Shiro实现RBAC,可以充分利用其灵活的认证和授权机制,结合自定义的Realm,实现基于角色的细粒度权限控制,适应不同应用场景的需求。