CSP202206C.角色授权

今天我们来看CSP202206C.角色授权这道题目

题意分析

本题要求实现一个基于角色的访问控制(RBAC)模型。系统中有三类实体:

  1. 角色(Role):包含名称、允许的操作清单、允许的资源种类清单、允许的资源名称清单
  2. 角色关联(Role Association):将角色与用户或用户组关联
  3. 待授权行为(Authorization Request):包含用户信息、操作、资源信息

判断规则:

  • 角色能否执行某操作:操作、资源种类、资源名称都要匹配(支持通配符*,资源名称清单为空表示不限制)
  • 用户能否执行某操作:找到所有与该用户或所属用户组关联的角色,任一角色允许即可

解题思路

1. 数据结构设计

  • 用结构体存储角色信息,使用 unordered_set 存储操作、资源种类、资源名称,便于快速查找
  • unordered_map 建立角色名称到索引的映射
  • 用两个 unordered_map 分别存储:
    • 用户名 → 关联的角色索引列表
    • 用户组名 → 关联的角色索引列表

2. 核心算法

角色匹配判断

  • 检查操作是否在角色的操作清单中,或清单包含 *
  • 检查资源种类是否在角色的资源种类清单中,或清单包含 *
  • 检查资源名称是否在角色的资源名称清单中,或清单为空(不限制)

用户权限判断

  • 收集用户直接关联的所有角色
  • 收集用户所属用户组关联的所有角色(需要去重)
  • 遍历所有关联角色,任一角色允许该操作则返回允许

3. 关键点

  • 独立判断:每个待授权行为独立判断,不存储历史信息
  • 去重:一个用户可能通过多个途径关联到同一角色,需要去重避免重复判断
  • 通配符处理* 表示匹配所有,空资源名称清单表示不限制
  • 性能优化:使用哈希表存储和查找,保证 O(1) 的平均查找时间

4. 时间复杂度

  • 每个角色的存储:O(nv + no + nn)
  • 每次查询:O(关联角色数量),最坏 O(n)
  • 总体:O(q × n),在数据范围内可行

代码实现

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>

using namespace std;

// 角色结构体
struct Role {
    string name;
    unordered_set<string> ops;          // 操作清单
    unordered_set<string> res_types;    // 资源种类清单
    unordered_set<string> res_names;    // 资源名称清单(空表示不限制)
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);  // 加速输入输出

    int n, m, q;
    cin >> n >> m >> q;

    vector<Role> roles(n);
    unordered_map<string, int> roleIdx;  // 角色名到索引的映射

    // 读取角色信息
    for (int i = 0; i < n; ++i) {
        Role &r = roles[i];
        cin >> r.name;
        roleIdx[r.name] = i;

        // 读取操作清单
        int nv;
        cin >> nv;
        for (int j = 0; j < nv; ++j) {
            string op;
            cin >> op;
            r.ops.insert(op);
        }

        // 读取资源种类清单
        int no;
        cin >> no;
        for (int j = 0; j < no; ++j) {
            string rt;
            cin >> rt;
            r.res_types.insert(rt);
        }

        // 读取资源名称清单
        int nn;
        cin >> nn;
        for (int j = 0; j < nn; ++j) {
            string rn;
            cin >> rn;
            r.res_names.insert(rn);
        }
        // 如果 nn = 0,res_names 保持空,表示不限制
    }

    // 构建用户/组到角色的映射
    unordered_map<string, vector<int>> userToRoles;
    unordered_map<string, vector<int>> groupToRoles;

    // 读取角色关联
    for (int i = 0; i < m; ++i) {
        string roleName;
        cin >> roleName;
        int idx = roleIdx[roleName];  // 获取角色索引

        int ns;
        cin >> ns;
        for (int j = 0; j < ns; ++j) {
            string type, name;
            cin >> type >> name;
            if (type == "u") {
                userToRoles[name].push_back(idx);
            } else {  // type == "g"
                groupToRoles[name].push_back(idx);
            }
        }
    }

    // 处理每个待授权的行为
    for (int query = 0; query < q; ++query) {
        string userName;
        int ng;
        cin >> userName >> ng;

        // 读取用户所属用户组
        vector<string> groups(ng);
        for (int i = 0; i < ng; ++i) {
            cin >> groups[i];
        }

        string op, resType, resName;
        cin >> op >> resType >> resName;

        // 标记匹配的角色(去重)
        vector<char> matched(n, 0);
        vector<int> matchedRoles;
        matchedRoles.reserve(n);

        // 处理用户直接关联的角色
        auto itUser = userToRoles.find(userName);
        if (itUser != userToRoles.end()) {
            for (int idx : itUser->second) {
                if (!matched[idx]) {
                    matched[idx] = 1;
                    matchedRoles.push_back(idx);
                }
            }
        }

        // 处理用户组关联的角色
        for (const string &g : groups) {
            auto itGroup = groupToRoles.find(g);
            if (itGroup != groupToRoles.end()) {
                for (int idx : itGroup->second) {
                    if (!matched[idx]) {
                        matched[idx] = 1;
                        matchedRoles.push_back(idx);
                    }
                }
            }
        }

        // 检查是否有任意匹配的角色允许该操作
        bool allowed = false;
        for (int idx : matchedRoles) {
            const Role &r = roles[idx];
            
            // 检查操作是否匹配
            bool opOK = r.ops.count("*") || r.ops.count(op);
            
            // 检查资源种类是否匹配
            bool typeOK = r.res_types.count("*") || r.res_types.count(resType);
            
            // 检查资源名称是否匹配(空清单表示不限制)
            bool nameOK = r.res_names.empty() || r.res_names.count(resName);
            
            // 三个条件都满足则允许
            if (opOK && typeOK && nameOK) {
                allowed = true;
                break;
            }
        }

        cout << (allowed ? 1 : 0) << '\n';
    }

    return 0;
}

总结

本题是典型的 RBAC(基于角色的访问控制)模型实现,主要考察:

  1. 数据结构设计 :合理使用哈希表(unordered_set/unordered_map)存储和查找数据
  2. 逻辑判断:准确理解并实现三层判断逻辑(用户→角色→权限)
  3. 边界处理 :正确处理通配符*、空清单、去重等特殊情况
  4. 性能优化:使用哈希表保证查询效率,避免超时

关键是要理清判断的层次关系:先确定用户关联的所有角色,再逐个检查这些角色的权限是否覆盖当前操作。使用哈希表能够高效地处理大量查询。

相关推荐
艾莉丝努力练剑16 分钟前
【AI大模型接入SDK】WebSocket & SSE 协议
网络·c++·websocket·网络协议·学习·面试
码匠许师傅17 分钟前
【设计模式精讲】6.抽象工厂(Abstract Factory)
c++·设计模式
GKxx19 分钟前
在 HarmonyOS 上从源码构建 GCC 16(gcc/g++ + libstdc++ + libsanitizer):完整记录
c++·华为·harmonyos·鸿蒙·gcc
晴天的雨.99221 分钟前
类和对象下(内部类,匿名对象,对象拷贝时的编译器优化)
开发语言·c++·算法
其实防守也摸鱼25 分钟前
智能体推荐:精选 AI Agent 工具与实战指南
运维·开发语言·人工智能·学习·web安全·自动化
always_TT25 分钟前
【Python 字符串格式化:format() 方法】
android·开发语言·python
小义_29 分钟前
JDK 深度解析
java·linux·开发语言·python·面试
旖旎夜光40 分钟前
LeetCode 238:除自身以外数组的乘积(前缀和) —— 题解
数据结构·c++·算法·leetcode·前缀和
智购科技自动售货机工厂44 分钟前
2026自动售货机异常重启根因分析:从日志挖掘到内存取证的技术实践~YH
开发语言·数据库·单片机·嵌入式硬件·人机交互