权限源码分析

写一个权限类 局部使用配置在视图类的 就会执行权限类的permission_classes方法完成权限校验

'''
drf的APIView源码 执行流程:
	1.先匹配路由 APIView中as_view -- 取消csrf校验	
    2.去csrf(view)本质还是view中找as_view -- return self.dispatch(request, *args, **kwargs)
    3.找dispatch 注意这里的self是视图类的对象 所以先从视图类的本身找 -- 在APIView中赵到dispatch 
    4.第497行 self.initial(request, *args, **kwargs)执行三大认证
    5.去到initial中找到:				
    	self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request) 
'''

APIView类的第399行:

    def initial(self, request, *args, **kwargs):
        
        self.format_kwarg = self.get_format_suffix(**kwargs)
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
        #能够解析的编码 版本控制
        self.perform_authentication(request)
        #认证组件的执行位置
        self.check_permissions(request)
        #权限组件的执行位置【*】
        self.check_throttles(request)
        #频率组件的执行位置
        

APIView第236行:

 def check_permissions(self, request):
        for permission in self.get_permissions():
            #self.get_permissions(): -- 详细见 APIView第274行:get_permissions()-->[CommonPermission(),...()]
            #permission就是CommonPermission()
            if not permission.has_permission(request, self):
                #权限类的对象执行has_permission方法
                #self就是自己写的权限类的has_permission方法的view参数
                #如果配了多个权限 第一个没过 就不会再执行下一个权限类了
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )
                #如果return的是False就会走这里 没有权限 

APIView第274行:

    def get_permissions(self):
        return [permission() for permission in self.permission_classes]
    #self.permission_classes
    #每次从[CommonPermission,...]列表中拿一个加括号执行
    #最终return的是[CommonPermission(),...()]本质是返回了权限类的对象放在了列表中
    '''
    self:视图类的对象
    permission_classes: api_settings.DEFAULT_PERMISSION_CLASSES == 视图类中配的 这一句:permission_classes = [CommonPermission]
    '''

总结:

drf的APIView源码 执行流程:
	1.先匹配路由 APIView中as_view -- 取消csrf校验	
    2.去csrf(view)本质还是view中找as_view -- return self.dispatch(request, *args, **kwargs)
    3.找dispatch 注意这里的self是视图类的对象 所以先从视图类的本身找 -- 在APIView中赵到dispatch 
    4.第497行 self.initial(request, *args, **kwargs)执行三大认证
    5.去到initial中找到:				
    	self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request) 
    6.取出配置再视图类上的权限类 实例化的到对象 一个个执行对象的has_permission方法 如果返回False 就直接结束 不再继续执行 权限就认证通过
    7.如果视图类上不配做权限类:permission_classes = [CommonPermission] 
    会使用配置文件中的 api_settings.DEFAULT_PERMISSION_CLASSES
    优先使用项目配置文件 其次使用drf内置配置文件

认证源码分析

'''
drf的APIView源码 执行流程:
	1.先匹配路由 APIView中as_view -- 取消csrf校验	
    2.去csrf(view)本质还是view中找as_view -- return self.dispatch(request, *args, **kwargs)
    3.找dispatch 注意这里的self是视图类的对象 所以先从视图类的本身找 -- 在APIView中赵到dispatch 
    4.第497行 self.initial(request, *args, **kwargs)执行三大认证
    5.去到initial中找到:				
    	self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request) 
'''

APIView类的第399行:

    def initial(self, request, *args, **kwargs):
        
        self.format_kwarg = self.get_format_suffix(**kwargs)
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
        #能够解析的编码 版本控制
        self.perform_authentication(request)
        #认证组件的执行位置【*】
        self.check_permissions(request)
        #权限组件的执行位置
        self.check_throttles(request)
        #频率组件的执行位置
        

APIView的第316行

    def perform_authentication(self, request):
        request.user 
        #这是一个方法 包装成了数据属性 request类的user方法

Request类的第219行

@property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            #看里面有没有
            with wrap_attributeerrors():
                self._authenticate()
                #没有执行这个 下一次就不走了
        return self._user
    #有执行这个

Request类的第373行

    def _authenticate(self):
        for authenticator in self.authenticators:
            #self.authenticators我们在视图类中配的 authentication_classes = [LoginAuth]
            #request 初始化的时候传入的 self.authenticators详情看Request类的第152行
            try:
                 #返回两个值 第一个是当前用户 第二个是token 也可以返回NOne 会继续执行下一个认证类
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:#AuthenticationFailed继承了APIException
                self._not_authenticated() #认证没通过 后面不走
                raise
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                #self.user=当前登录用户
                #self.auth=token
                #self是新request的对象
                return

        self._not_authenticated()

Request类的第152行

 def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        self.authenticators = authenticators or ()
 #什么时候调用__init__ APIView的dispatch的492行

APIView的dispatch的492行

request = self.initialize_request(request, *args, **kwargs)

initialize_request:
    385行:
     return Request(
            authenticators=self.get_authenticators(),
        )
    
get_authenticators:
	return [auth() for auth in self.authentication_classes]
#循环authentication_classes = [LoginAuth]

总结:

1.配置在视图类上的认证类 会在执行视图类方法之前执行 再权限认证之前执行 
2.自己写的认证类可以返回两个值或none
3.后续可以从request.user去除当前登录用户(前提是你要在认证类中返回)

频率源码分析

'''
drf的APIView源码 执行流程:
	1.先匹配路由 APIView中as_view -- 取消csrf校验	
    2.去csrf(view)本质还是view中找as_view -- return self.dispatch(request, *args, **kwargs)
    3.找dispatch 注意这里的self是视图类的对象 所以先从视图类的本身找 -- 在APIView中赵到dispatch 
    4.第497行 self.initial(request, *args, **kwargs)执行三大认证
    5.去到initial中找到:				
    	self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request) 
'''
    def initial(self, request, *args, **kwargs):
        
        self.format_kwarg = self.get_format_suffix(**kwargs)
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
        #能够解析的编码 版本控制
        self.perform_authentication(request)
        #认证组件的执行位置【*】
        self.check_permissions(request)
        #权限组件的执行位置
        self.check_throttles(request)
        #频率组件的执行位置
        

APIView的第352行:

    def check_throttles(self, request):
        throttle_durations = []
        for throttle in self.get_throttles():
            #是我们配置在视图类中的throttle_classes = [CommonThrottle]
            #每取出一个频率类对象执行allow_request方法 如果是False 频率超了 不能走了
            if not throttle.allow_request(request, self):
                throttle_durations.append(throttle.wait())
        if throttle_durations:
            # Filter out `None` values which may happen in case of config / rate
            # changes, see #1438
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]
            duration = max(durations, default=None)
            self.throttled(request, duration)

总结:

1.写频率类 继承BaseThrottle 
2.重写allow_request方法
3.在内部判断 如果超频就返回False 不超就返回True

异常处理

APIView--->dispatch--->三大认证,视图类的方法,如果出了一场,会被一场捕获,捕获后统一处理

drf 内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理的drf的异常

# 写一个函数,内部处理异常,在配置文件中配置一下即可

接口文档

# 前后端分离
	-我们做后端,写接口
    -前端做前端,根据接口写app,pc,小程序
    
    -作为后端来讲,我们很清楚,比如登录接口  /api/v1/login/---->post---->username,password 编码方式json----》返回的格式  {code:100,msg:登录成功}
    
    
    -后端人员,接口写完,一定要写接口文档
    
    
# 接口文档如何编写
	-1 使用word,md 编写接口文档
    -2 使用第三方平台,编写我们的接口文档(非常多)---》收费
    	-https://www.showdoc.com.cn/item/index
    -3 公司自己使用第三方开源的搭建的---》Yapi ---》你如果想自己搭建
    	-https://zhuanlan.zhihu.com/p/366025001 
            
    -4 使用drf编写的接口,可以自动生成接口文档
    	-swagger---》drf-yasg---》官方推荐使用
        -coreapi----》咱们讲
    
    
# 使用coreapi自动生成接口文档步骤
	- 1 安装 
    - 2 配置路由
        from rest_framework.documentation import include_docs_urls
        path('docs/', include_docs_urls(title='xx项目接口文档')),
    -3 在视图类,方法上,写注释即可
    	-在类上加注释
        -在类的方法上加注释
        -在序列化类或表模型的字段上加  help_text,required。。。。
    
    
    -4 配置文件配置
    	REST_FRAMEWORK = {
     		'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',

    	}
        
        
   -5 访问地址:http://127.0.0.1:8000/docs
    
    
    
# 接口文档,需要有的东西
	-描述
    -地址
    -请求方式
    -请求编码格式
    -请求数据详解(必填,类型)
    -返回格式案例
    -返回数据字段解释
    -错误码

jwt介绍和原理

# cookie session token 发展史

#Json web token (JWT) 就是web方向token的使用

#JWT的构成 三部分(头 荷载 签名) 每部分用. 分割

头:header
    声明类型 这里是jwt
    声明加密的算法 通常直接使用  HMAC SHA256
    公司信息 

荷载:payload
    存放有效信息的地方
    过期时间
    签发时间
    用户id
    用户名字

签名:signature
	第一部分和第二部分通过密钥+签名的方式得到

jwt开发重点

登录接口 --- 签发token

认证类 --- jwt认证

base64编码和解码

#编码
import base64
import json
dic = {'user_id':1,'username':'lzy'}
dic_str = json.dumps(dic)
#把这个字符串时用base64编码
res = base64.b64encode(dic_str.encode('utf-8'))
print(res)

注意:base64编码后 字符长度一定要是4的倍数 如果不是 使用=补齐(不表示数据)

#解码
res = base64.b64decode('TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ=')

#base64 应用场景
'''
1.jwt 使用了base64
2.网络中传输数据 也会经常使用 base64编码
3.网络传输中 有的图片使用base64
'''

s=''
res = base64.b64encode(s)
with open('a.png','wb')as f:
    f.with(res)

image-20230209171907300

drf-jwt快速使用

django+drf 平台开发jwt这套 有两个模块

	-djangorestframework-jwt  ---》一直可以用
    -djangorestframework-simplejwt---》公司用的多---》希望你们试一下
    -自己封装jwt签发和认证
    
#使用步骤
1.安装
2.登录接口 快速签发token path('login/',obtain_jwt_token)
from rest_framework_jwt.views import obtain_jwt_token
3.postman 向http://127.0.0.1:8000/login/

定制返回格式

基于auth的User表签发token 可以不自己写了 但是登录接口返回的格式只有token 不符合公司规范 可以自己自定义添加

#使用步骤
	1.建一个py文件 写一个函数def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'code':100,
        'msg':'登陆成功',
        'token':token,
        'username':user.username,

    }
from rest_framework_jwt.views import jwt_response_payload_handler 用这个函数

	2.配置一下settings
    JWT_AUTH = {
    	'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',  
	}
    3.测试

补充

1.django 的 auth user表密码是加密的 同样的密码密文也都不一样
	- 每次加密 都随机生成一个盐 把盐拼在加密后的串中
    eg:
    	pbkdf2_sha256$150000$CptHucCygPZv$Z4HP93MR9qj6mNDKeN4ew9ZrKWceoi5KJ
    明文:
    	123
    加盐:
    	CptHucCygPZv
        
2.自定义用户表  生成密码用密文


3.用户密码忘了怎么办
	-新增一个用户 把他的密文复制过去
    
4.双token认证
	-

drf-jwt源码执行流程(了解)

1.签发(登录)

#登录接口路由匹配成功 执行obtain_jwt_token()--post请求--ObtainJSONWebToken的post方法 
	path('login/',obtain_jwt_token)
    
#ObtainJSONWebToken的post方法继承APIView 
	    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        #序列化得到序列化类
        if serializer.is_valid():
        #做校验 校验自己 局部钩子 全局钩子
            user = serializer.object.get('user') or request.user
        #user:当前登录用户 
            token = serializer.object.get('token')
        #token:签发的token
            response_data = jwt_response_payload_handler(token, user, request)
        #构造返回格式 可以自己定制 
            response = Response(response_data)
            if api_settings.JWT_AUTH_COOKIE:
                expiration = (datetime.utcnow() +
                              api_settings.JWT_EXPIRATION_DELTA)
                response.set_cookie(api_settings.JWT_AUTH_COOKIE,
                                    token,
                                    expires=expiration,
                                    httponly=True)
            return response
        	#返回自己定制的返回格式

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    

如何得到user 如何签发token

class RefreshJSONWebTokenSerializer(VerificationBaseSerializer):
     def validate(self, attrs):
            #前端传入校验过后的数据  {"username":"lzy","password":"123"}
        credentials = {
            #'username':attrs.get('username') 'password':attrs.get('password')
            #获取用户名和密码
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password')
        }
		
        if all(credentials.values()):
            user = authenticate(**credentials)
            #auth模块 authenticate可以传用户名和密码 如果用户名存在 就返回用户对象 如果不存在就是None

            if user:
                if not user.is_active:
                    #校验用户是否是活跃用户 如果禁用了不能登录
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)
				#荷载 通过user生成荷载
                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                    #jwt_encode_handler通过荷载得到token串
                }
            else:
                msg = _('Unable to log in with provided credentials.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "{username_field}" and "password".')
            msg = msg.format(username_field=self.username_field)
            raise serializers.ValidationError(msg)

重点:

1.通过user得到荷载: payload = jwt_payload_handler(user)

2.通过荷载签发token:jwt_encode_handler(payload)

了解:

翻译函数_ 只要做了国际化 放英文 会翻译成该国语言(在配置文件中配置)

from django.utils.translation import ugettext as _
	msg = _('Unable to log in with provided credentials.')

2.认证

#JSONWebTokenAuthentication  去他的父类找 --BaseJSONWebTokenAuthentication -- authenticate方法 

    def authenticate(self, request):
        jwt_value = self.get_jwt_value(request)
        #前端带在请求头中的token值
        if jwt_value is None:
            return None
        #如果没有token值 返回 none 就不进行认证 所以 必须结合权限类一起使用
		
        try:
            payload = jwt_decode_handler(jwt_value)
            #通过token得到荷载  中途会出错
            #出错的原因:篡改token 过期 未知错误
        except jwt.ExpiredSignature:
            msg = _('Signaturexpired.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = _('Error decoding signature.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()
		#如果一点错误都没有 就说明token是可以被信任的 payload就可以使用 通过payload得到当前登录用户 
        user = self.authenticate_credentials(payload)
		#返回当前登录的用户 和token值
        return (user, jwt_value)
#token怎么取出来的?
#jwt_value = self.get_jwt_value(request)
    
    def get_jwt_value(self, request):
        auth = get_authorization_header(request).split()
        #拿到了前端请求头中传入的数据 jwt 68767864670
        #auth = [jwt,7467]
        
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()
		#'jwt'
        if not auth:
            #如果请求头里没带 他就去cookie里取
            if api_settings.JWT_AUTH_COOKIE:
                return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
            return None

        if smart_text(auth[0].lower()) != auth_header_prefix:
            #判断auth = [jwt,7467]第一个数 =不=auth_header_prefix
            return None

        if len(auth) == 1:
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
            #判断auth的长度等于1就不合法了
        elif len(auth) > 2:
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)
			
        return auth[1]
    	# = 2 就可以 取出auth里面索引为1的数值 就是token
        
        
        
        
#认证类配置了 如果不传jwt 不会校验 一定要配合权限使用

自定义用户表签发

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import UserInfo
from .authentication import JsonWebTokenAuthentication


class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        user = UserInfo.objects.filter(username=username, password=password).first()
        if user:
            # 登录成功,签发token
            # 通过user得到payload
            payload = jwt_payload_handler(user)
            # 通过payload得到token
            token = jwt_encode_handler(payload)
            return Response({'code': 1000, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 1001, 'msg': '用户名或密码错误'})

自定义用户表认证

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from .models import UserInfo
from rest_framework_jwt.settings import api_settings

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


class JsonWebTokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 取出token----》请求头中,就叫token
        token = request.META.get('HTTP_TOKEN')
        if token:
            try:
                payload = jwt_decode_handler(token)
                # 得到当前登录用户----》
                user = UserInfo.objects.get(pk=payload.get('user_id'))
                # 只要访问一次需要登录的接口,就会去UserInfo表中查一次用户---》优化
                # user=UserInfo(id=payload.get('user_id'),username=payload.get('username'))
                # user={'id':payload.get('user_id')}

                return user, token
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('token过期')
            except jwt.DecodeError:
                raise AuthenticationFailed('token认证失败')

            except jwt.InvalidTokenError:
                raise AuthenticationFailed('token无效')
            except Exception as e:
                raise AuthenticationFailed('未知异常')

        raise AuthenticationFailed('token没有传,认证失败')

simpleui的使用

django的ahmin界面不好看 我们可以使用 simpleui

1.使用步骤

1.安装:
	pip install django-simpleui

2.在app中注册(官方文档)

3.调整左侧导航栏
	-menu_display对应menus name
    -如果是项目的app,就menus写app
    -菜单可以多级,一般咱们内部app,都是一级
    -可以增加除咱们app外的其它链接---》如果是外部链接,直接写地址,如果是内部链接,跟之前前后端混合项目一样的写法:咱们的案例---》show 的路由
SIMPLEUI_CONFIG = {
    'system_keep': False,
    'menu_display': ['图书管理', '权限认证', '张红测试'],  # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
    'dynamic': True,  # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
    'menus': [
        {
            'name': '图书管理',
            'app': 'app01',
            'icon': 'fas fa-code',
            'models': [
                {
                    'name': '图书',
                    'icon': 'fa fa-user',
                    'url': 'app01/book/'
                },
                {
                    'name': '出版社',
                    'icon': 'fa fa-user',
                    'url': 'app01/publisssh/'
                },
                {
                    'name': '作者',
                    'icon': 'fa fa-user',
                    'url': 'app01/author/'
                },
                {
                    'name': '作者详情',
                    'icon': 'fa fa-user',
                    'url': 'app01/authordetail/'
                },
            ]
        },
        {
            'app': 'auth',
            'name': '权限认证',
            'icon': 'fas fa-user-shield',
            'models': [
                {
                    'name': '用户',
                    'icon': 'fa fa-user',
                    'url': 'auth/user/'
                },
                {
                    'name': '组',
                    'icon': 'fa fa-user',
                    'url': 'auth/group/'
                },
            ]
        },
        {

            'name': '张红测试',
            'icon': 'fa fa-file',
            'models': [
                {
                    'name': 'Baidu',
                    'icon': 'far fa-surprise',
                    # 第三级菜单 ,
                    'models': [
                        {
                            'name': '爱奇艺',
                            'url': 'https://www.iqiyi.com/dianshiju/'
                            # 第四级就不支持了,element只支持了3级
                        }, {
                            'name': '百度问答',
                            'icon': 'far fa-surprise',
                            'url': 'https://zhidao.baidu.com/'
                        }
                    ]
                },
                {
                    'name': '大屏展示',
                    'url': '/show/',
                    'icon': 'fab fa-github'
                }]
        }
    ]
}
4.内部app,图书管理系统某个链接要展示的字段---》在admin.py 中----》自定义按钮
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    list_display = ('nid', 'name', 'price', 'publish_date', 'publish')

    # 增加自定义按钮
    actions = ['custom_button']

    def custom_button(self, request, queryset):
        print(queryset)

    custom_button.confirm = '你是否执意要点击这个按钮?'
    # 显示的文本,与django admin一致
    custom_button.short_description = '测试按钮'
    # icon,参考element-ui icon与https://fontawesome.com
    # custom_button.icon = 'fas fa-audio-description'
    # # 指定element-ui的按钮类型,参考https://element.eleme.cn/#/zh-CN/component/button
    custom_button.type = 'danger'
    # # 给按钮追加自定义的颜色
    # custom_button.style = 'color:black;'
    
    
5. app名字显示中文,字段名字显示中文
	-新增,查看修改展示中文,在表模型的字段上加:verbose_name='图书名字',help_text='这里填图书名'
	-app名字中文:apps.py---》verbose_name = '图书管理系统'

    
6. 其它配置项
	SIMPLEUI_LOGIN_PARTICLES = False  #登录页面动态效果
    SIMPLEUI_LOGO = 'https://avatars2.githubusercontent.com/u/13655483?s=60&v=4'#图标替换
    SIMPLEUI_HOME_INFO = False  #首页右侧github提示
    SIMPLEUI_HOME_QUICK = False #快捷操作
    SIMPLEUI_HOME_ACTION = False # 动作

大屏展示

#gitee
https://search.gitee.com/?skin=rec&type=repository&q=%E5%B1%95%E7%A4%BA%E5%A4%A7%E5%B1%8F

权限控制


```python
# 公司内部项目
	-rbac:是基于角色的访问控制(Role-Based Access Control )在 RBAC  中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便
    
    -用户表:用户和角色多对多关系,
    -角色表
    	-一个角色可能有多个权限----》
        	-开发角色:拉取代码,上传代码
        	-财务角色:开工资,招人,开除人
            -
    -权限表:角色和权限是多多多
    	-拉取代码
        -上传代码
        -部署项目
        -开工资
        -招人
        -开除人
        -
    -通过5张表完成rbac控制:用户表,角色表,权限表, 用户角色中间表, 角色权限中间表
    -如果某个人,属于财务角色,单只想要拉取代码权限,不要上传代码权限
    -通过6张表:django的admin----》后台管理就是使用这套权限认证
    	用户表,
        角色表,
        权限表, 
        用户角色中间表, 
        角色权限中间表
        用户和权限中间表
# 互联网项目
	-acl:Access Control List 访问控制列表,权限放在列表中
    -权限:权限表----》 发视频,评论,开直播
    -用户表:用户和权限是一对多
    
    张三:[发视频,]
    李四:[发视频,评论,开直播]
    
    
    
    
    
 
# 演示了 django-admin 的权限控制
	-授予lqz 某个组
    -单独授予权限
    
    
# django -auth--6张表
	auth_user   用户表
	auth_group  角色表,组表
    auth_permission  权限表
    -----------
    auth_user_groups   用户和角色中间表
    auth_group_permissions  角色和权限中间表
	-------------
    auth_user_user_permissions  用户和权限中间表
    
    
    
# java:若依
# go :gin-vue-admin
# python :django-vue-admin