1.drf-jwt源码执行流程

1.1 签发(登录)

1.代码:
urls.py:
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/',obtain_jwt_token),
] 2.我们点进obtain_jwt_token源码:
drf/views.py:
obtain_jwt_token = ObtainJSONWebToken.as_view()
refresh_jwt_token = RefreshJSONWebToken.as_view()
verify_jwt_token = VerifyJSONWebToken.as_view() 3.login需要提交用户名和密码,所以是post请求,我们需要在其父类中找到post方法:
ObtainJSONWebToken>>>JSONWebTokenAPIView,在JSONWebTokenAPIView中找到了post方法:
def post(self, request, *args, **kwargs):
# serializer是序列化类的对象
serializer = self.get_serializer(data=request.data)
# 校验,如果校验通过:
if serializer.is_valid():
# 拿到user和token
user = serializer.object.get('user') or request.user
token = serializer.object.get('token')
# 拿到返回格式,之前我们自定义过token的返回格式。
"""
当我们点击方法:jwt_response_payload_handler(token, user, request),跳转到了rest_framework_jwt:jwt_response_payload_handler = api_settings.JWT_RESPONSE_PAYLOAD_HANDLER。说明JWT_RESPONSE_PAYLOAD_HANDLER需要在配置中指定返回格式。因此我们在设置中指定:JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.jwt_response.jwt_response',
}。所以返回格式才能按照我们指定的格式返回。
"""

            response_data = jwt_response_payload_handler(token, user, request)
response = Response(response_data)
return response
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) """
执行if serializer.is_valid():这句话时就会执行序列化类中的代码,但是如何得到user和token,在序列化类的全局钩子中寻找答案:
"""
4.还是回到drf/views.py中:
obtain_jwt_token = ObtainJSONWebToken.as_view()
refresh_jwt_token = RefreshJSONWebToken.as_view()
verify_jwt_token = VerifyJSONWebToken.as_view()
ObtainJSONWebToken后面跟了as_view()说明这是视图类,点进去:
class ObtainJSONWebToken(JSONWebTokenAPIView):
serializer_class = JSONWebTokenSerializer
说明JSONWebTokenSerializer就是序列化类。 5.JSONWebTokenSerializer代码:
class JSONWebTokenSerializer(Serializer):
# 这是一个全局钩子,因为上面没有单个字段的校验规则,所以此时的addr就是{'username':'max','password':'max123'}
def validate(self, attrs):
credentials = {
# 这一步还是拿到了用户名,只不过是绕了一下
self.username_field: attrs.get(self.username_field),
# 拿到密码
'password': attrs.get('password')
}
# 必须用户名和密码都幼值才成立
if all(credentials.values()):
# auth模块中的,如果用户存在会拿到用户对象
user = authenticate(**credentials)
if user:
# 如果能拿到用户对象,并且用户被锁(is_active默认是1,如果用户被锁则是0)
if not user.is_active:
# 如果被锁则提示disabled
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
# 通过用户对象拿到荷载
payload = jwt_payload_handler(user)
# 通过payload生成token
return {
'token': jwt_encode_handler(payload),
'user': user
}
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.2 认证 (认证类)

1.认证类需要从JSONWebTokenAuthentication中找到authenticate方法。在其父类中找到了authenticate方法。
from rest_framework_jwt.authentication import JSONWebTokenAuthentication def authenticate(self, request):
"""
Returns a two-tuple of `User` and token if a valid signature has been
"""
# jwt_value就是token字符串
jwt_value = self.get_jwt_value(request)
# 如果token值没传,直接返回None
if jwt_value is None:
return None try:
# payload是一个字典:{'user_id': 1, 'username': 'max', 'exp': 1676113688, 'email': ''}
payload = jwt_decode_handler(jwt_value)
# 还有几种可能拿不到,分别是:篡改token、token过期了、未知错误
except jwt.ExpiredSignature:
msg = _('Signature has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# 如果没有错误顺利能拿到用户对象
user = self.authenticate_credentials(payload)
# 返回当前登录用户,token
return (user, jwt_value) 2.接下来我们来看刚才的方法get_jwt_value(request)是如何拿到token的,该方法在类JSONWebTokenAuthentication中:
def get_jwt_value(self, request):
auth = get_authorization_header(request).split() 3.我们需要找到方法get_authorization_header(request),在BaseAuthentication中找到了该方法:
def get_authorization_header(request):
# request.META可以拿到get请求头当中的值,结果是个字典。在数据发送到后端时键都变成了'HTTP_前端传入的键',如果拿不到就拿一个空字符串。此时的auth是jwt dfjkdlsjf...
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(auth, str):
# Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING)
# 转码然后返回
return auth 4.继续回到get_jwt_value(request)方法:
def get_jwt_value(self, request):
# auth是个被分割列表:[jwt,dfjkdlsjf]
auth = get_authorization_header(request).split()
# JWT_AUTH_HEADER_PREFIX就是'JWT',转化成小写'jwt'
auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower() if not auth:
# 如果请求头没带,就去cookie中取
if api_settings.JWT_AUTH_COOKIE:
return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
return None
# 如果列表索引0不为jwt返回None
if smart_text(auth[0].lower()) != auth_header_prefix:
return None if len(auth) == 1:
msg = _('Invalid Authorization header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid Authorization header. Credentials string '
'should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
# 返回列表索引1,也就是token
return auth[1]

2.自定义用户表签发和认证

2.1 签发

views.py:
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from .models import Userinfo
from rest_framework_jwt.settings import api_settings
# 生成荷载的方法,我们直接调用drf的
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
# 生成token的方法,我们也调用drf的
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER 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:
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({'code':100,'msg':'登录成功','token':token})
else:
return Response({'code':101,'msg':'用户名或密码错误'}) urls.py:
router = SimpleRouter()
router.register('user', UserView, 'user') # 此时路由:http://127.0.0.1:8000/api/v1/user/login/ urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls))
]
通过以上步骤,我们可以自定义出功颁布token:

2.2 认证

新建一个认证类authentication.py,在其中写认证类的代码:
authentication.py:
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.settings import api_settings
import jwt
from rest_framework.exceptions import AuthenticationFailed
from .models import Userinfo
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER class JsonWebTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.META.get('HTTP_TOKEN') # 前端的格式可以自定义,取的时候在前面加上HTTP_就好,并且键要大写
if token:
try:
# jwt_decode_handler()方法仅仅是通过token找到payload,内部并没有切割字符串的方法 get_jwt_value(),所以前端在传的时候不需要加jwt和空格。
payload = jwt_decode_handler(
token)
print(payload) # :{'user_id': 1, 'username': 'max', 'exp': 1676113688, 'email': ''}
user = Userinfo.objects.filter(pk=payload.get('user_id')).first()
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没有传 认证失败') views.py:
class BookView(ModelViewSet):
# 手写jwt认证只需要写认证类不用写权限类
authentication_classes = [JsonWebTokenAuthentication] def list(self, request, *args, **kwargs):
return Response('success')

3.auth_user表密码加密

3.1 手动定义类似token的加密方式:

1.token的加密方式:token由三段构成,第一段声明加密算法和类型,第二段存放有效信息的地方:过期时间、签发时间、用户id、用户名等。第三段是加密后的header和base64加密后的payload。

2.我们也可以定义一中类似token的加密方式:改密码分为三段,用两个$连接起来,第一段是密码加密后的密文,第二段是随机生成的盐(不加密),第三段是加密后的原密码和盐连接在一起(中间不加符号),在通过md5加密。

3.代码:
views.py:
import uuid
import hashlib def register_hash(request):
"""
password:原密码
res:原密码加密之后的密文
salt:随机生成的盐(不加密)
pwd1:res$salt
pwd_part3:password+salt
res2:给pwd_part3加密之后的密文
pwd2:最终密码:pwd1+res2
"""
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# print(password) # 123
md51 = hashlib.md5()
md51.update(password.encode('utf8'))
res = md51.hexdigest()
# print('res',res) # 202cb962ac59075b964b07152d234b70
# 随机生成一个盐(不加密)
salt = str(uuid.uuid4())
print('salt',salt)
# 将加密的原密码和不加密的盐组合起来,组成密码的前两部分
pwd1 = res + '$' + salt # 202cb962ac59075b964b07152d234b70$44590a73-2602-4f96-a718-972d83fb7ae6
# 将不加密的密码和盐组合起来,组成明文
pwd_part3 = res + salt
md52 = hashlib.md5()
md52.update(pwd_part3.encode('utf8'))
# 原密码和盐组成的明文加密,组成密码的第三部分
res2 = md52.hexdigest()
# 最终的密码
pwd2 = pwd1 + '$' + res2
print(pwd2)
User_hash.objects.create(username=username,hash_pwd=pwd2)
return HttpResponse('注册成功') return render(request, 'pwd.html', locals()) def login_view(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
md51 = hashlib.md5()
md51.update(password.encode('utf8'))
res = md51.hexdigest()
user_obj = User_hash.objects.filter(username=username).first()
if not user_obj:
return HttpResponse('用户未注册')
if not res == user_obj.hash_pwd.split('$')[0]:
return HttpResponse('密码错误')
salt = user_obj.hash_pwd.split('$')[1]
pwd_part3 = res + salt
md52 = hashlib.md5()
md52.update(pwd_part3.encode('utf8'))
res2 = md52.hexdigest()
if not res2 == user_obj.hash_pwd.split('$')[2]:
return HttpResponse('密码错误')
return HttpResponse('登陆成功')
return render(request,'login.html',locals()) urls.py:
urlpatterns = [
path('register/',views.register_hash),
path('login1/',views.login_view)
]

3.2 利用django自带的方法make_password()和check_password()来编写登录注册

1.make_password()只有一个参数,就是原密码。返回值是加密后的密码,也是django的auth_user表中的用户密码的加密方式:

from django.contrib.auth.hashers import make_password, check_password
from .models import User1
# 用djanngo的make_password方法注册
def register2(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 密码加密:
pwd = make_password(password)
User1.objects.create(username=username,password=pwd)
return HttpResponse('注册成功')
return render(request,'register2.html',locals()) 2.check_password()方法用来校验密码,里面有两个参数,第一个是明文密码,第二个参数是密文密码,如果这两个密码匹配那么结果是True,不匹配返回结果是False。
# 用django的check_password方法登陆
def login2(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
real_pwd = User1.objects.filter(username=username).first().password
is_correct = check_password(password,real_pwd)
if is_correct:
return HttpResponse('登陆成功')
return render(request,'login2.html',locals())
"""
如果超级管理员密码忘记了,可以再创建一个超级管理员,然后将新创建的管理员密码(密文)复制到前一个超级管理员的密码处,这两个管理员就会使用同一个密码。
"""

4.simpleui使用

1.之前公司里,做项目,要使用权限,要快速搭建后台管理,使用djagno的admin直接搭建,django的admin界面不好。所以采用第三方软件。

2.第三方的美化:
xadmin:作者弃坑了,bootstrap+jq
simpleui: vue,界面更好看 3.现在阶段,一般前后端分离比较多:django+vue

4.1 使用步骤

1.安装:pip install simpleui

2.在app中注册
要注册在最上面
INSTALLED_APPS = [
'simpleui'
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
'rest_framework',
]
然后当我们登录到admin后台管理就变成了这样:

3.然后我们在models.py中构造以下几张表,并且在admin.py中注册:
models.py:
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField()
publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
authors=models.ManyToManyField(to='Author')
def __str__(self):
return self.name class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail',to_field='nid',unique=True,on_delete=models.CASCADE) class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
telephone = models.BigIntegerField()
birthday = models.DateField()
addr = models.CharField(max_length=64) class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField() admin.py:
from .models import Book,Publish,AuthorDetail,Author
admin.site.register(Book)
admin.site.register(Publish)
admin.site.register(AuthorDetail)
admin.site.register(Author)
然后我们就可以在admin后台管理页看到这几张表:

4.在apps.py中加入verbose_name = '图书管理系统',就可以将左侧列表中的app名改成自定义的名字:
class App01Config(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'app01'
verbose_name = '图书管理系统'

5.在models.py中每张表下面加入:
class Meta:
verbose_name_plural = '作者表'
在后台管理就可以将表名显示成中文:

然后再数据库加一些数据(可以在admin后台管理加,也可以在pycharm中加),添加好之后可以直接点进去修改,这就完成了对一个图书管理系统增删改查的创建。
"""
DataTimeField字段刚开始默认是英文,如果我们想要把它设置成中文,需要在settings.py中设置:LANGUAGE_CODE = 'zh-hans'。
"""

6.当我们在admin.py中注册好之后,我们在页面上只能看到书名。注册还有一种方式,在admin.py中写一个类,定义哪张表选择显示的字段就继承哪张表,用list_play=('字段名')来定义显示的字段名,但是不能上传多对多的外键字段:

7.还可以在页面上增加按钮:在刚才定义的BookAdmin中继续加内容:
actions = ['custom_button'] # custom_button不能更改 def custom_button(self, request, queryset):
print(queryset) # queryset就是选中对象的queryset,可以额外做一些操作 custom_button.short_description = '额外操作' # 按钮的中文名
custom_button.type = 'success' # 设置按钮颜色 8.侧边栏设置,需要在settings.py中进行如下设置:
SIMPLEUI_CONFIG = {
'system_keep': False,
'menu_display': ['图书管理', '权限认证', '外链'], # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
'dynamic': True, # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
'menus': [
# name要和menu_display中注册的名字保持一致
{
'name': '图书管理',
'app': 'app01',
'icon': 'fas fa-code',
# models继续往下写下面的子目
'models': [
{
'name': '图书',
'icon': 'fa fa-user',
'url': '/admin/app01/book/'
},
#url只能是自己在urls.py中配置的路由或者是自动生成的路由
{
'name': '出版社',
'icon': 'fa fa-user',
'url': 'app01/publish/'
},
{
'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'
}]
}
]
} 9.其他配置项:
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 # 动作

5.权限控制

5.1 互联网项目:

	alc:访问控制列表,权限放在列表中
用户表:存储用户信息,和权限表是一对多的关系
权限表:每个用户拥有的权限 比如:
权限列表:[发视频,发评论,开直播]
max拥有的权限:[发视频,发评论,开直播]
jerry拥有的权限:[发视频]

5.2 公司内部项目(python写公司内部项目居多):

1.rbac:是基于角色的访问控制(Role-Based Access Control )在 RBAC  中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

2表关系:
用户表:用户和角色是多对多关系(一个用户可以对应多个角色)
角色表:类似于公司中的岗位
权限表:用户表不直接和用户表建立联系,而是和角色表建立联系(某个用户成为了某个角色之后才拥有某项权限)。角色表和权限表是多对多关系。 所以描述以上三者关系需要建立5张表:
用户表、角色表、权限表、用户角色表、角色权限表 3.用户和权限不直接建立联系是为了简化流程方便管理,但是也有特殊情况:比如公司人资想要获取拉取代码的权限,但是开发角色拥有的权限不仅仅是拉取代码而且还能操作代码。
如果将开发的角色赋给人资就会导致人资的权限过大。所以角色和权限直接监理联系,产生第6张表:角色权限中间表。 4.以图书管理系统为例,目前设置2个用户,一个是root(超级管理员),一个是max(普通用户)。目前想要设置max的权限为查看书籍列表和作者列表,需要首先创建一个组(角色),该组中规定了查看书籍列表和作者列表的权限。

在进入到用户设置列表中:首先取消该用户超级管理员身份(公司内超级管理员数量很有限)。

再登陆用户max,发现系统中只有作者和图书两个选项,并且只能查看:

5.管理员也可以直接设置用户和权限的对应关系:

6.在表中权限、组以及它们的对应关系都在以下6张表中:
auth_user:用户表
auth_group:角色表,组表
auth_permission:权限表
auth_user_groups:用户和角色中间表
auth_group_permissions:角色和权限中间表
auth_user_user_permissions:用户和权限中间表

7.用管理员给用户max通过用户和权限对应关系给max添加了一个权限(不通过角色),该功能也可以叠加到max的权限中,用户max现在有三个功能:查看图书和作者(通过角色添加权限)、查看出版社(通过用户添加权限):

drf-jwt源码分析以及自定义token签发认证、alc和rbac的更多相关文章

  1. Django(63)drf权限源码分析与自定义权限

    前言 上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf默认是允许所有用户访问 权限源码分析 源码入口:APIView.py文件下的initial方法下的check_per ...

  2. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  3. Django(64)频率认证源码分析与自定义频率认证

    前言 有时候我们发送手机验证码,会发现1分钟只能发送1次,这是做了频率限制,限制的时间次数,都由开发者自己决定 频率认证源码分析 def check_throttles(self, request): ...

  4. JUnit源码分析 - 扩展 - 自定义Rule

    JUnit Rule简述 Rule是JUnit 4.7之后新加入的特性,有点类似于拦截器,可以在测试类或测试方法执行前后添加额外的处理,本质上是对@BeforeClass, @AfterClass, ...

  5. JUnit源码分析 - 扩展 - 自定义RunListener

    RunListener简述 JUnit4中的RunListener类用来监听测试执行的各个阶段,由RunNotifier通知测试去运行.RunListener与RunNotifier之间的协作应用的是 ...

  6. DRF cbv源码分析 restful规范10条 drf:APIView的源码 Request的源码 postman的安装和使用

    CBV 执行流程 路由配置:url(r'^test/',views.Test.as_view()),  --> 根据路由匹配,一旦成功,会执行后面函数(request) --> 本质就是执 ...

  7. Netty源码分析之自定义编解码器

    在日常的网络开发当中,协议解析都是必须的工作内容,Netty中虽然内置了基于长度.分隔符的编解码器,但在大部分场景中我们使用的都是自定义协议,所以Netty提供了  MessageToByteEnco ...

  8. Django(44)drf序列化源码分析(1)

    序列化与反序列化   一般后端数据返回给前端的数据格式都是json格式,简单易懂,但是我们使用的语言本身并不是json格式,像我们使用的Python如果直接返回给前端,前端用的javascript语言 ...

  9. ArrayList 源码分析和自定义ArrayList实现

    概述 ArrayList 是基于数组实现的,是一个能自动扩展的动态数组. ArrayList 是线程不安全的,多线程情况下添加元素会出现数组越界的情况,而且数组赋值操作不是原子操作,会导致多线程情况下 ...

  10. Django(60)Django内置User模型源码分析及自定义User

    前言 Django为我们提供了内置的User模型,不需要我们再额外定义用户模型,建立用户体系了.它的完整的路径是在django.contrib.auth.models.User. User模型源码分析 ...

随机推荐

  1. PHP使用PHPmailer类和smtp发送邮件

    开启邮件smtp服务 设置授权码 引入phpmailer类,smtp类本地下载https://github.com/PHPMailer/PHPMailer //下载PHPMailer并开启php_op ...

  2. 8、将两个字符串s1,s2进行比较,如果s1>s2,则输出一个正数。如果s1 = s2,输出零。如果s1 < s2, 输出一个负数,不用strcmp函数,输出的正数或者负数的绝对值应该是比较两字符串相应字符的ascii码的差值。

    /* 将两个字符串s1,s2进行比较,如果s1>s2,则输出一个正数.如果s1 = s2,输出零.如果s1 < s2, 输出一个负数,不用strcmp函数,输出的正数或者负数的绝对值应该是 ...

  3. 【项目案例】配置小型网络WLAN基本业务示例

    组网需求 如图1-1所示,AC直接与AP连接.现某企业分支机构为了保证工作人员可以随时随地的访问Internet,需要通过部署WLAN基本业务实现移动办公. 具体要求如下: 1.提供名为"t ...

  4. 研究光度立体法阶段性小结和优化(可20ms获取4个2500*2000灰度图的Normal Map)。

    这个东西是我接触的第一个非2D方面的算法,到目前为止其实也没有完全搞定,不过可能短时间内也无法突破.先把能搞定的搞定吧. 这个东西也有一大堆参考资料,不过呢,搜来搜去其实也就那些同样的东西,个人觉得就 ...

  5. py周结04

    py周结04 异常类型,理语法结构及实践案例 1.异常类型 SyntaxError 语法错误 NameError 名字错误 IndexError 指数错误 KeyError 关键字错误 Indenta ...

  6. 总算给女盆友讲明白了,如何使用stream流的filter()操作

    一.引言 在上一篇文章中<这么简单,还不会使用java8 stream流的map()方法吗?>分享了使用stream的map()方法,不知道小伙伴还有印象吗,先来回顾下要点,map()方法 ...

  7. Shell及Linux常见易错题目题库-Shell/Linux-选择、简答、判断、编程

    1.以下不合法的shell头是(不合法指运行会报错)(   ) A. #!/bin/bash B. #-/bin/bash C. !#/bin/bash 答案:C 2.if [ $2 -a $2 = ...

  8. view-design table的renderHeader中hover添加checkboxGroup遇到的问题

    示例demo https://codepen.io/sphjy/pen/mdKaQJZ 问题见图 勾选多个复选框 on-change事件返回的数据只有当前点击的这一项,而且设置在checkboxGro ...

  9. 关于解决pip安装python第三方库超时的问题

    直接换源下载 1. 设置超时时间,安装txt 文件内安装包 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --default-time ...

  10. PPT排版技巧