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. 性能优化:使用哈希表保证查询效率,避免超时

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

相关推荐
夏幻灵5 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
程序猿编码5 小时前
榨干 RTX5090 算力!Qwen3 专用单卡推理引擎,手写C++/CUDA 算子实现 MTP 推测解码
开发语言·c++·大模型推理·qwen3·多模态推理
西飘客6 小时前
vs 怎么根据dll 生成lib文件
c++·qt
bkspiderx7 小时前
Qt 插件机制:动态扩展应用功能的核心框架
开发语言·qt·元数据·qt 插件·qpluginloader
程序员老陆7 小时前
深入理解 C++ thread_local:线程私有存储的正确打开方式
开发语言·c++·程序设计
沐晓时光7 小时前
C语言入门,深入理解指针(3)
c语言·开发语言
码云数智-园园7 小时前
unique_ptr 还是 shared_ptr?C++ 智能指针选型与内存泄漏实战分析
java·开发语言
禾小西8 小时前
07丨Redis 哨兵机制:主库故障后,如何恢复服务?
java·开发语言·redis
我不是阵雨8 小时前
JDK 21虚拟线程Pinning陷阱:一文拔钉解困
java·开发语言
徐小黑ACG9 小时前
Golang 基础05 结构体struct
开发语言·算法·golang