eagleye

企业级角色管理方案(基于最小权限原则)存档文档

企业级角色管理方案(基于最小权限原则)存档文档

一、方案概述

方案名称:企业级RBAC角色权限管理系统

核心原则

  • 最小权限原则:默认分配最低权限角色(隐患排查员)
  • 职责分离原则:用户注册与权限分配流程分离
  • 完整审计追踪:所有角色变更操作全程记录

技术栈Django(后端)+ Vue3 + TypeScript + Quasar(前端)

应用场景:企业级安全隐患排查系统的用户权限生命周期管理

二、核心技术实现

1. 后端角色模型设计(Django)

1.1 用户角色定义

# models.py

from django.db import models

from django.contrib.auth.models import AbstractUser

from django.core.validators import MinValueValidator, MaxValueValidator

from django.utils.translation import gettext_lazy as _

class UserRoles(models.IntegerChoices):

HAZARD_INSPECTOR = 10, _('隐患排查员') # 最低权限(默认角色)

HAZARD_MANAGER = 20, _('隐患治理员')

SAFETY_ADMIN = 30, _('安全管理员')

SAFETY_AUDITOR = 40, _('安全审计员')

SYSTEM_ADMIN = 99, _('系统管理员') # 最高权限

class User(AbstractUser):

mobile = models.CharField(_('手机号'), max_length=15, unique=True)

nickname = models.CharField(_('昵称'), max_length=50)

role = models.PositiveSmallIntegerField(

_('用户角色'),

choices=UserRoles.choices,

default=UserRoles.HAZARD_INSPECTOR, # 新用户默认最低权限

validators=[MinValueValidator(10), MaxValueValidator(99)],

help_text=_('基于RBAC模型的权限角色')

)

# 权限检查方法

def is_system_admin(self):

return self.role == UserRoles.SYSTEM_ADMIN

def has_perm(self, perm, obj=None):

"""覆盖权限检查方法,实现基于角色的权限控制"""

# 系统管理员拥有全部权限

if self.is_system_admin():

return True

# 其他角色权限检查逻辑

return super().has_perm(perm, obj)

1.2 注册序列化器(强制默认角色)

# serializers.py

from rest_framework import serializers

from .models import User, UserRoles

class UserRegistrationSerializer(serializers.ModelSerializer):

password_confirm = serializers.CharField(write_only=True)

class Meta:

model = User

fields = ['mobile', 'email', 'nickname', 'password', 'password_confirm']

extra_kwargs = {'password': {'write_only': True}}

def validate(self, data):

if data['password'] != data['password_confirm']:

raise serializers.ValidationError({"password_confirm": "两次输入的密码不一致"})

return data

def create(self, validated_data):

validated_data.pop('password_confirm')

# 强制设置默认角色为隐患排查员(最小权限)

validated_data['role'] = UserRoles.HAZARD_INSPECTOR

# 使用create_user方法确保密码加密存储

return User.objects.create_user(**validated_data)

1.3 角色管理API视图

# views.py

from rest_framework import generics, permissions

from .models import User

from .serializers import UserRegistrationSerializer, UserSerializer

from .audit import log_security_event

class UserRegistrationAPIView(generics.CreateAPIView):

"""用户注册API,默认分配隐患排查员角色"""

serializer_class = UserRegistrationSerializer

permission_classes = [permissions.AllowAny]

def perform_create(self, serializer):

instance = serializer.save()

# 记录用户注册审计日志

log_security_event(

event_type='USER_REGISTER',

user_id=instance.id,

details={

'ip': self.request.META.get('REMOTE_ADDR'),

'role': instance.get_role_display(),

'timestamp': self.request.META.get('HTTP_X_REQUEST_ID')

}

)

class UserRoleUpdateAPIView(generics.UpdateAPIView):

"""角色更新API,仅系统管理员可访问"""

queryset = User.objects.all()

serializer_class = UserSerializer

permission_classes = [permissions.IsAdminUser] # 权限控制

def update(self, request, *args, **kwargs):

# 记录角色变更前状态

user = self.get_object()

old_role = user.get_role_display()

# 执行更新

response = super().update(request, *args, **kwargs)

# 记录角色变更审计日志

new_role = user.get_role_display()

log_security_event(

event_type='ROLE_CHANGE',

actor_id=request.user.id, # 操作人ID

user_id=user.id, # 被操作人ID

details={

'old_role': old_role,

'new_role': new_role,

'reason': request.data.get('reason', '未提供理由'),

'ip_address': request.META.get('REMOTE_ADDR')

}

)

return response

2. 前端实现(Vue3 + Quasar)

2.1 注册成功角色提示

<template>

<!-- 成功注册提示对话框 -->

<q-dialog v-model="showSuccessDialog" persistent>

<q-card>

<q-card-section class="row items-center">

<q-avatar icon="check_circle" color="positive" text-color="white" />

<span class="q-ml-sm">用户注册成功!</span>

</q-card-section>

<q-card-section>

<p>

您的用户ID: <strong>{{ registeredUserId }}</strong>

</p>

<p class="q-mt-md">

<q-icon name="info" color="info" size="sm" />

您的角色为: <strong>隐患排查员</strong>

</p>

<p class="text-caption">

这是您的初始角色,如需更高权限,请联系管理员调整角色。

</p>

</q-card-section>

<q-card-actions align="right">

<q-btn flat label="确定" color="primary" v-close-popup @click="redirectToLogin" />

</q-card-actions>

</q-card>

</q-dialog>

</template>

2.2 管理员角色管理界面

<template>

<q-page padding>

<q-table

title="用户管理"

:rows="users"

:columns="columns"

row-key="id"

:loading="loading"

>

<!-- 角色编辑单元格 -->

<template v-slot:body-cell-role="props">

<q-td :props="props">

<div v-if="editingUserId === props.row.id">

<q-select

v-model="roleEditValue"

:options="roleOptions"

label="用户角色"

emit-value

map-options

dense

/>

</div>

<div v-else>

{{ props.row.roleLabel }}

</div>

</q-td>

</template>

<!-- 操作按钮单元格 -->

<template v-slot:body-cell-actions="props">

<q-td :props="props">

<div v-if="editingUserId === props.row.id">

<q-btn

icon="check"

color="positive"

dense

flat

@click="saveRoleChange(props.row)"

/>

<q-btn

icon="close"

color="negative"

dense

flat

@click="cancelEdit"

/>

</div>

<div v-else>

<q-btn

icon="edit"

color="primary"

dense

flat

@click="startEditing(props.row)"

v-if="canEditRole(props.row)"

/>

</div>

</q-td>

</template>

</q-table>

<!-- 角色变更理由对话框 -->

<q-dialog v-model="showReasonDialog" persistent>

<q-card style="min-width: 350px">

<q-card-section>

<div class="text-h6">角色变更说明</div>

</q-card-section>

<q-card-section>

<q-input

v-model="roleChangeReason"

label="请输入角色变更理由"

autogrow

:rules="[(val) => !!val || '必须填写变更理由']"

/>

</q-card-section>

<q-card-actions align="right">

<q-btn flat label="取消" color="primary" v-close-popup />

<q-btn flat label="确认变更" color="primary" @click="confirmRoleChange" />

</q-card-actions>

</q-card>

</q-dialog>

</q-page>

</template>

<script setup lang="ts">

import { ref, onMounted } from 'vue'

import { apiClient } from 'src/services/axios'

import { useQuasar } from 'quasar'

import { UserRole } from 'src/types/auth/user'

const $q = useQuasar()

const loading = ref(false)

const users = ref([])

const editingUserId = ref(null)

const roleEditValue = ref(null)

const showReasonDialog = ref(false)

const roleChangeReason = ref('')

const pendingRoleChangeUser = ref(null)

// 角色选项配置

const roleOptions = [

{ label: '隐患排查员', value: UserRole.HAZARD_INSPECTOR },

{ label: '隐患治理员', value: UserRole.HAZARD_MANAGER },

{ label: '安全管理员', value: UserRole.SAFETY_ADMIN },

{ label: '安全审计员', value: UserRole.SAFETY_AUDITOR },

{ label: '系统管理员', value: UserRole.SYSTEM_ADMIN },

]

// 表格列配置

const columns = [

{ name: 'id', label: 'ID', field: 'id', align: 'left' },

{ name: 'nickname', label: '昵称', field: 'nickname', align: 'left' },

{ name: 'mobile', label: '手机号', field: 'mobile', align: 'left' },

{ name: 'role', label: '角色', field: 'roleLabel', align: 'left' },

{ name: 'actions', label: '操作', align: 'center' },

]

// 权限检查:管理员不能编辑比自己权限高的用户

const canEditRole = (targetUser) => {

const currentUserRole = authStore.user.role

// 系统管理员可以编辑所有角色

if (currentUserRole === UserRole.SYSTEM_ADMIN) return true

// 安全管理员不能编辑系统管理员

if (currentUserRole === UserRole.SAFETY_ADMIN &&

targetUser.role === UserRole.SYSTEM_ADMIN) return false

// 其他角色权限检查逻辑...

return true

}

// 角色变更流程

const startEditing = (user) => {

editingUserId.value = user.id

roleEditValue.value = user.role

}

const saveRoleChange = (user) => {

if (roleEditValue.value === user.role) {

cancelEdit()

return

}

pendingRoleChangeUser.value = user

showReasonDialog.value = true // 要求输入变更理由

}

const confirmRoleChange = async () => {

try {

await apiClient.patch(`/users/${pendingRoleChangeUser.value.id}/role/`, {

new_role: roleEditValue.value,

reason: roleChangeReason.value, // 记录变更理由

})

$q.notify({ type: 'positive', message: '角色更新成功' })

fetchUsers() // 刷新用户列表

} catch (error) {

$q.notify({

type: 'negative',

message: '角色更新失败',

caption: error.response?.data?.message || '服务器错误'

})

} finally {

showReasonDialog.value = false

roleChangeReason.value = ''

cancelEdit()

}

}

// 加载用户列表

const fetchUsers = async () => {

loading.value = true

try {

const response = await apiClient.get('/users/')

users.value = response.data.map(user => ({

...user,

roleLabel: roleOptions.find(opt => opt.value === user.role)?.label || '未知角色'

}))

} catch (error) {

$q.notify({ type: 'negative', message: '获取用户列表失败' })

} finally {

loading.value = false

}

}

onMounted(() => fetchUsers())

</script>

三、企业级安全实践

1. 最小权限与职责分离

1.1 权限分配流程

用户注册 → 默认分配隐患排查员角色 → 管理员手动提升权限

1.2 角色权限矩阵

角色

权限描述

典型操作

隐患排查员

最低权限,只读+上报

查看隐患、提交隐患报告

隐患治理员

排查+治理权限

处理隐患、验证整改结果

安全管理员

全流程管理权限

分配角色、审批治理方案、生成安全报表

安全审计员

审计与监督权限

查看操作日志、审计权限变更、生成审计报告

系统管理员

系统配置权限

用户管理、角色配置、系统参数设置

2. 完整审计追踪系统

2.1 审计日志记录

# 审计日志实现示例

def log_security_event(event_type, user_id, actor_id=None, details=None):

"""

记录安全事件日志

:param event_type: 事件类型(USER_REGISTER/ROLE_CHANGE等)

:param user_id: 事件关联用户ID

:param actor_id: 操作人ID(如管理员ID)

:param details: 事件详情字典

"""

AuditLog.objects.create(

event_type=event_type,

user_id=user_id,

actor_id=actor_id,

ip_address=get_client_ip(),

user_agent=request.META.get('HTTP_USER_AGENT', ''),

details=details or {},

timestamp=timezone.now()

)

2.2 角色变更审计记录示例

{

"event_type": "ROLE_CHANGE",

"user_id": 10086,

"actor_id": 9527,

"timestamp": "2025-08-07T14:30:22Z",

"details": {

"old_role": "隐患排查员",

"new_role": "隐患治理员",

"reason": "因工作调整需要处理隐患整改任务",

"ip_address": "192.168.1.100"

}

}

3. 企业级安全增强策略

3.1 角色变更控制

  • 多级审批:敏感角色变更(如升级为系统管理员)需多级审批申请 → 安全管理员审批 → 系统管理员审批 → 执行变更
  • 变更通知:角色变更时自动通知用户def notify_role_change(user, new_role_name):

"""发送角色变更通知邮件/SMS"""

send_email(

subject="账户角色变更通知",

recipient_list=[user.email],

template_name="role_change_notification.html",

context={"user": user, "new_role": new_role_name}

)

3.2 系统管理员安全策略

  • 角色数量限制(建议不超过3人)
  • 强制双因素认证
  • 操作需二次确认
  • 定期轮岗(如每季度)

四、方案优势总结

1. 安全性:最小权限原则减少权限滥用风险,完整审计满足合规要求

2. 灵活性:支持企业组织架构调整,角色权限可动态配置

3. 可追溯性:所有权限变更都有详细记录,满足等保2.0审计要求

4. 用户体验:注册流程简化,权限提升流程清晰

5. 扩展性:支持未来增加角色类型和权限粒度

该方案已在企业级安全隐患排查系统中验证,有效降低了权限管理风险,同时保持了业务灵活性,符合ISO 27001信息安全管理体系要求。

 

posted on 2025-08-07 22:23  GoGrid  阅读(14)  评论(0)    收藏  举报

导航