django 身份验证与鉴权

目录

  • 用户模型建立
  • 账号密码登录
  • 手机号登录验证码
  • 双后端鉴权
  • JWT 介绍

用户模型建立

在django中自带auth_user表,字段名有id, password,username,is_superuer,is_activate , email ,is_staff(用于标识某个用户是否可以登录到 Django 的管理界面。如果 is_staff 设置为 True,该用户就可以访问管理后台;)等
可以用

python manage.py createsuperuser

来创建超级用户,然后输入 /admin 可进入后台添加用户,这里是使用账号和密码登录的权限。

但是由于django自带的auth_user表字段过少,所以这里使用用户拓展表的概念(方便日后对用户的字段进行扩展和数据迁移),如果继承auth_user 则很难做到扩展功能了。
所以一般建立user_expand模型进行,如图所示建立模型:

class UserExpand(models.Model):class UserMakerSource(models.IntegerChoices):ADMIN = 1, 'ADMIN'Operator = 2, 'T2'Merchant = 3, 'T3'class StatusSource(models.IntegerChoices):Inactive = 1, '未激活'Active = 2, '已激活'Deactivated = 3, '已停用'objects = models.Manager()user = models.OneToOneField(User, on_delete=models.DO_NOTHING, primary_key=True, verbose_name="用户")telephone = models.CharField(max_length=100, null=True, blank=True, verbose_name="用户手机号")company_alias = models.CharField(max_length=100, null=True, blank=True, verbose_name="企业简称")user_maker = models.IntegerField(choices=UserMakerSource.choices, default=UserMakerSource.Merchant,verbose_name="用户端标记符")  company_id = models.IntegerField(null=True, blank=True, verbose_name="企业id")create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")update_time = models.DateTimeField(auto_now=True, verbose_name="修改时间")status = models.IntegerField(choices=StatusSource.choices, default=StatusSource.Inactive, verbose_name="用户状态")yn = models.BooleanField(default=True)class Meta:verbose_name = '2.用户扩展信息管理'verbose_name_plural = verbose_name

class Meta 是一个内嵌的类,用于定义一些与模型相关的配置(非字段属性),不会作为数据库表的一部分存储。这些配置包括模型的一些行为特征和属性,例如:
verbose_name 和 verbose_name_plural:用来定义在 Django 管理后台显示的名字。verbose_name 是单数形式,表示一个模型实例的名称;verbose_name_plural 是复数形式,用来表示模型的名称集合。
在这里使用:

    user = models.OneToOneField(User, on_delete=models.DO_NOTHING, primary_key=True, verbose_name="用户")#OneToOneField(User): 这表示 UserExpand 模型与 User 模型之间有一个一对一的关系。一对一字段的工作方式类似于外键,区别在于它保证与之关联的另一个模型(这里是 User)是唯一的。也就是说,每个 User 实例在 UserExpand 中只能有一个对应的实例,反之亦然。# on_delete=models.DO_NOTHING: 这是一个用于指定当关联的 User 对象被删除时应如何处理 UserExpand 实例的参数。models.DO_NOTHING 表示如果删除了 User 对象,不会对 UserExpand 实例做任何操作。这种设置需要小心使用,因为它可能导致数据库中的外键引用不一致。(所以一般用假删除)

在 UserExpand 模型中定义了一个名为 user 的字段,这个字段是一个一对一关系字段,连接到 Django 的内置用户模型 User

由于前后端,一般以JSON传输,所以这里使用DRF 序列化进行操作。(用于将 UserExpand 模型的实例转换为 JSON 格式,并在 API 中进行交互。
注意,要保证time是 只读,不可以进行修改!,同时进行了格式化处理,方便前端传入yy-mm-dd的形式。

class UserExpandModelSerializer(serializers.ModelSerializer):
#这种序列化器直接与一个 Django 模型关联,自动处理模型字段到 JSON 数据的转换。user_id = serializers.IntegerField(source='user.id', read_only=True)  # 获取关联用户的 IDcreate_time = serializers.DateTimeField(format="%Y-%m-%d", read_only=True)update_time = serializers.DateTimeField(format="%Y-%m-%d", read_only=True)class Meta:model = UserExpandfields = ['user_id', 'telephone', 'company_alias', 'user_maker', 'company_id', 'create_time', 'update_time','status', 'yn']read_only_fields = ('create_time', 'update_time')

建立了user_expand与auth_user联系了,但是如果直接从auth_user插入数据不会写进user_expand,怎么办,在user_expand.models 写,层级独立。

@receiver(post_save, sender=User)
#@receiver(post_save, sender=User): 这是一个装饰器,用于将下面定义的函数注册为一个信号接收器。post_save 是 Django 发送的一个信号,每当任何数据库中的模型实例保存后都会发送这个信号。sender=User 参数指定这个接收器只响应 User 模型实例的保存操作。
def create_user_expand(sender, instance, created, **kwargs):if created:UserExpand.objects.create(user=instance, user_maker=UserExpand.UserMakerSource.Operator)

在这里也需要对auth_user进行DRF 序列化操作:

class UserSerializer(serializers.ModelSerializer):class Meta:model = Userfields = ['id', 'username', 'password']extra_kwargs = {'password': {'write_only': True}}def create(self, validated_data):user = User.objects.create_user(**validated_data)return userclass UserListSerializer(serializers.ModelSerializer):class Meta:model = Userfields = ["id", "username"]

账号密码登录

只要是登录,就不设置鉴权
鉴权是前端请求头:
Authorization :‘Bearer eyJhXXXXXXXXX’

from rest_framework.permissions import AllowAnyclass SpecialistPasswordLoginView(views.APIView):permission_classes = [AllowAny]  # 允许所有用户访问这个登录界面(后台管理,其余一律用token验证)def post(self, request):username = request.data.get("username")password = request.data.get("password")if not username or not password:raise BusinessException.build_by_dict(status_data.PARAM_ERROR_40001)user = authenticate(request, username=username, password=password)if user is not None:# 登录成功,处理 UserExpanduser_expand, created = UserExpand.objects.get_or_create(user=user,defaults={'user_maker': UserExpand.UserMakerSource.Operator,  # admin'status': UserExpand.StatusSource.Active,  # 已激活})# 如果用户已存在,检查并更新相关字段if not created:user_expand.status = UserExpand.StatusSource.Active  # 确保状态是已激活user_expand.save()# 生成 JWT tokenrefresh = RefreshToken.for_user(user)  # 创建 refresh 和 access tokensreturn Response({"refresh": str(refresh),"access": str(refresh.access_token),}, status=status.HTTP_200_OK)else:raise BusinessException(status_code.LOGIN_CODE_ERROR_40111, "Invalid username or password.")

在使用 Django REST framework(DRF)及其简单 JWT(Simple JWT)扩展来生成和返回 JSON Web Tokens (JWTs),其中包括一个刷新令牌(refresh token)和访问令牌(access token)

refresh = RefreshToken.for_user(user): 这行代码通过调用 RefreshToken.for_user() 方法为指定的用户(user)创建一个新的刷新令牌。这个方法是 django-rest-framework-simplejwt 包提供的,用于为给定的用户生成一个新的 JWT 刷新令牌。刷新令牌主要用于在访问令牌过期后获取新的访问令牌,而无需用户重新登录。

return Response({…}, status=status.HTTP_200_OK): 这行代码创建并返回一个 HTTP 响应,状态码为 200 OK。响应的内容是一个包含两个键值对的字典:

“refresh”: str(refresh): 将刷新令牌转换为字符串格式并作为 refresh 键的值。
“access”: str(refresh.access_token): 通过访问刷新令牌的 access_token 属性获取对应的访问令牌,并将其转换为字符串格式作为 access 键的值。

authenticate() 函数的主要功能是检查用户提供的用户名和密码是否与数据库中存储的凭证匹配。如果匹配,它会返回对应的用户对象;如果不匹配,它会返回 None。
注意需要在setting.py设置:

SIMPLE_JWT = {"ACCESS_TOKEN_LIFETIME": timedelta(days=30),  # 访问令牌30天有效"REFRESH_TOKEN_LIFETIME": timedelta(days=30),  # 刷新令牌30天有效# 可以添加更多的配置项,如签名算法、响应负载等
}
在apps:"rest_framework",  # RESTful API"rest_framework_simplejwt",  # JWT 认证

手机号登录验证码

手机登录验证码较为复杂,我总结了一下步骤:
用户输入手机号 , 后端拿到手机号并根据时间戳+手机号生成对应的随机6位验证码,然后将手机号和验证码(加密)存到数据库中(并设置对应的令牌时间),后端服务器将验证码和手机号发送到短信服务商中,短信服务商获取到对应的信息后,将验证码发送到对应的手机号中,用户收到验证码后,在页面输入验证码,后端在数据库查询手机号+验证码是否正确,如果正确和上述账号密码登录一样返回对应的token。
所以这里设计到三个model:
1.auth_user 2.user_expand 3.LoginCode
这是logincode 模型:这里的code是加密保存,使用了django自带的make_password

class LoginCode(models.Model):"""登录验证码"""objects = models.Manager()id = models.AutoField(primary_key=True)telephone = models.CharField(max_length=20, verbose_name="手机号")code = models.CharField(max_length=100, verbose_name="验证码")create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")update_time = models.DateTimeField(auto_now=True, verbose_name="修改时间")yn = models.BooleanField(default=True)def __str__(self):return f"{self.telephone} - {self.code}"def set_codes(self, raw_code):self.code = make_password(raw_code)def check_code(self, raw_code):return self.is_expired() and check_password(raw_code, self.code)def is_expired(self):ten_minutes_ago = timezone.now() - timedelta(minutes=10)return self.update_time >= ten_minutes_agoclass LoginCodeSerializer(serializers.ModelSerializer):class Meta:model = LoginCodefields = ['id', 'telephone', 'code', 'create_time', 'update_time', 'yn']  # 包括所有字段

下面提供了一种代码思路,

logger = logging.getLogger(__name__)# 发送验证码线程池
SEND_CODE_THREADPOOL = ThreadPoolExecutor(max_workers=1, thread_name_prefix="send_code_")
class GetLoginCodeView(views.APIView):"""获取登录验证码"""User = get_user_model()permission_classes = [AllowAny]def post(self, request):# 手机号destination = request.data.get("telephone")self.check_user(destination)code = self.generate_code(destination)if code is None:raise BusinessException.build_by_dict(status_data.LOGIN_CODE_ERROR_40112)logger.info(f"GetLoginCodeView destination:{destination},code:{code}")try:self.send_sms_code(destination, code)except Exception as e:raise BusinessException.build_by_dict(status_data.PARAM_ERROR_40001)return Response(status_data.OK_200, status=status.HTTP_200_OK)# 检查用户是否存在,如果不存在则创建def check_user(self, username):"""检查用户是否存在,如果不存在则创建,并检查状态"""user, created = self.User.objects.get_or_create(username=username)if created:user.set_unusable_password()user.save()user_expand, created = UserExpand.objects.get_or_create(user=user)if created:user_expand.telephone = usernameif user_expand.status == UserExpand.StatusSource.Deactivated:raise BusinessException(status_code.LOGIN_CODE_ERROR_40111, "账户已被停用")if user_expand.status == UserExpand.StatusSource.Inactive:user_expand.status = UserExpand.StatusSource.Activeuser_expand.save()return user_expanddef generate_code(self, destination):"""校验验证码生成时间 如果小于一分钟,不生成未生成过验证码或之前生成时间大于一分钟重新生成ArgsReturns: 生成的验证码"""# 生成6位随机数字验证码code = ''.join(random.sample(string.digits, 6))# 获取或创建验证码login_code, created = LoginCode.objects.get_or_create(telephone=destination)# 如果验证码已经存在,且创建时间在1分钟之内,则不重新生成,否则重新生成并更新验证码if not created:send_time = login_code.update_timeone_minutes_ago = timezone.now() - timedelta(minutes=1)if send_time > one_minutes_ago:code = Noneelse:login_code.set_codes(code)login_code.save()# 如果验证码不存在,则生成验证码else:login_code.set_codes(code)login_code.save()return codedef send_sms_code(self, phone, code):"""发送短信验证码Args:phone: 手机号code: 验证码Returns:"""SEND_CODE_THREADPOOL.submit(self.tencent_cloud_send_sms_code(phone, code, "10"))def tencent_cloud_send_sms_code(self, phone, code, expiration_date):"""腾讯云发送短信验证码Args:phone: 手机号code: 验证码expiration_date: 验证码有效期Returns:"""try:cred = credential.Credential(settings.TENCENT_SMS_SECRET_ID, settings.TENCENT_SMS_SECRET_KEY)client = sms_client.SmsClient(cred, settings.TENCENT_SMS_REGION)req = models.SendSmsRequest()req.SmsSdkAppId = settings.TENCENT_SMS_APP_IDreq.SignName = settings.TENCENT_SMS_SIGN_NAMEreq.TemplateId = settings.TENCENT_SMS_TEMPLATE_IDreq.TemplateParamSet = [code, expiration_date]req.PhoneNumberSet = [phone]resp = client.SendSms(req)logger.info(resp.to_json_string(indent=2))except TencentCloudSDKException as err:logger.error("tencent send sms code error:", err)class CheckLoginCodeView(views.APIView):"""检查登录验证码"""permission_classes = [AllowAny]def post(self, request):destination = request.data.get("telephone")code = request.data.get("code")login_code = LoginCode.objects.filter(telephone=destination).first()if not login_code:return Response(status_data.LOGIN_CODE_ERROR_40111, status=status.HTTP_406_NOT_ACCEPTABLE)if not login_code.check_code(code):return Response(status_data.LOGIN_CODE_ERROR_40111, status=status.HTTP_406_NOT_ACCEPTABLE)User = get_user_model()user = User.objects.get(username=destination)try:userExpand = UserExpand.objects.get(user=user)userExpand.save()except UserExpand.DoesNotExist:passrefresh = RefreshToken.for_user(user)r_data = {'refresh': str(refresh),'access': str(refresh.access_token),}return Response(r_data, status=status.HTTP_200_OK)

在处理短信验证码的发送过程中使用线程池,如 ThreadPoolExecutor,主要是出于性能和响应时间的考虑。以下是几个关键的原因和好处:

  1. 异步操作:
    发送短信通常涉及网络请求,这是一个可能耗时的操作,尤其是当短信服务商的API响应时间不稳定或较慢时。如果在主执行线程(通常是处理Web请求的线程)中直接进行这类操作,会导致整个Web请求的处理时间显著延长,从而影响用户体验。
  2. 不阻塞主线程:
    通过使用线程池来异步发送短信,可以确保主执行线程快速响应用户请求,例如立即返回一个“验证码已发送,请查收”的消息。与此同时,实际的短信发送操作在后台的另一个线程中处理,从而不会阻塞主线程。
  3. 资源管理和效率:
    线程池还有助于更有效地管理系统资源。它允许控制同时运行的线程数量,避免了创建和销毁线程的高开销,以及可能由于过多并发线程而导致的资源耗尽问题。max_workers=1 意味着线程池中只有一个工作线程处理发送任务,这可能是为了防止对短信API的过度请求或控制成本。
  4. 可扩展性和控制:
    使用线程池还提供了更好的扩展性和控制。如果未来业务增长,需要更高的并发处理能力,可以简单地调整 max_workers 参数来增加处理能力。同时,线程池提供的是一种更结构化的方式来处理并发任务,使得代码更易于管理和维护。
  5. 异常处理和稳定性:
    在独立的线程中处理短信发送还允许更好地隔离和管理可能出现的异常,不会影响主要的业务流程。此外,可以在这些后台线程中实现更复杂的错误处理逻辑,如重试机制等。
  6. 日志记录:
    你的代码中也显示了日志记录器的设置 logger = logging.getLogger(name),这是为了在线程池中执行的任务出现问题时能够记录详细的日志信息,从而便于调试和追踪问题。

总之,使用 ThreadPoolExecutor 是一种提高Web应用性能和用户响应速度的常见实践,尤其是在涉及外部网络请求的操作中。这种方法可以有效地改善系统的可用性和响应性,同时保持代码的清晰和易于维护。

注意使用腾讯短信服务商服务,需要在settings.py设置:

TENCENT_SMS_SECRET_ID = ''
TENCENT_SMS_SECRET_KEY = ''
TENCENT_SMS_REGION = ''
TENCENT_SMS_ENDPOINT = 'sms.tencentcloudapi.com'
TENCENT_SMS_APP_ID = ''
TENCENT_SMS_SIGN_NAME = ''
TENCENT_SMS_TEMPLATE_ID = ''

双后端鉴权

一般来说很多项目不止一个后端来运行,比如针对于对话项目,一个后端使用django,一个后端使用websockets协议,django对用户设置了权限了,但是websockets如何鉴权呢?
这里提供一个思路。
首先在django服务中写入一个得到用户信息的接口:POST /user/info/,请求头参数Authorization
django返回响应:

{"status": 200,"message": "OK","data":{"id":1,"telephone": "1234456","company_id": 8,"status":1,"user_maker":1,}
}

所以ws服务只要请求这个接口,然后拿到这个telephone就表示这个用户是对的了。一般ws请求参数写出token = ,所以访问ws服务的时候,ws首先会提取到这个用户token,扔给django获取用户信息是否正确。

import  asynciofrom utils.logger import logger
from utils.http_utils import aio_get, aio_postclass EchoiceCoreSDK:def __init__(self, base_url="http://localhost:8000"):self.base_url = base_urlasync def check_auth(self, token: str) -> dict:"""检查用户token是否有效:param token: 用户token:return:{"status": 200,"message": "OK","data":{"id":1,"telephone": "1234456","company_id": 8,"status":1,"user_maker":1,}}"""try:response = await self._post("/user/info/", headers={"Authorization": f"Bearer {token}"})user_info = response.get("data", {})return user_infoexcept Exception as e:logger.warning(f"check_auth error: {e}")return {}async def _post(self, path, **kwargs):url = self.base_url + pathkwargs.setdefault("headers", {})self.update_headers(kwargs["headers"])status, resp_headers, resp_body = await aio_post(url, **kwargs)if status != 200:raise Exception(f"Failed to get {url}, {resp_body=}")return resp_body@staticmethoddef update_headers(headers: dict):headers["Content-Type"] = "application/json"headers["Accept"] = "application/json"if headers.get("Authorization") == "Bearer ":headers.pop("Authorization")ce_core_sdk = ceCoreSDK()async def main():sdk = ceCoreSDK()token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."user_info = await sdk.check_auth(token)print("User Info:", user_info)if __name__ == "__main__":asyncio.run(main())

JWT 介绍

JWT(JSON Web Tokens)是一种开放标准(RFC 7519),用于在网络应用环境间安全地传递声明(claims)。它被广泛用于身份验证和信息交换。JWT设计轻巧,可以通过URL、POST参数或在HTTP头内传输。它主要由三个部分组成,用点(.)连接成一个长字符串。
可以不用在服务端存储认证信息(比如 token),完全由客户端提供,服务端只要根据 JWT 自身提供的解密算法就可以验证用户合法性,而且这个过程是安全的。
组成:头部、载荷与签名
头部:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

{"alg": "HS256","typ": "JWT"
}
# 对应base64编码

载荷
用来存储服务器需要的数据,比如用户信息,例如姓名、性别、年龄等,要注意的是重要的机密信息最好不要放到这里,比如密码等。

{"name": "古时的风筝","introduce": "英俊潇洒"
}

签名有一个计算公式。

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),Secret
)

使用HMACSHA256算法计算得出,这个方法有两个参数,前一个参数是 (base64 编码的头部 + base64 编码的载荷)用点号相连,后一个参数是自定义的字符串密钥,密钥不要暴露在客户端,近应该服务器知道。

JWT最常用于:
身份验证:用户登录后,服务器生成一个JWT并返回给客户端,客户端后续请求将JWT包含在Header中。服务器通过验证JWT来认证用户身份。
信息交换:JWT安全地在各方之间传递信息,确保信息可以验证和可信。
JWT因其简单和扩展性,已经成为现代网络应用中身份验证和授权的流行方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/857563.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Sa-Token|2】Sa-Token在微服务中的使用

在微服务架构中,使用 Sa-Token 进行分布式认证和权限管理需要考虑到各个服务之间的通信和 Token 的共享。以下是一个详细的教程,介绍如何在微服务中使用 Sa-Token。 1. 微服务架构介绍 假设有以下几个微服务: AuthService: 负责用户认证和 …

Python-日志模块

目录 一、日志级别 二、日志配置 1、日志基本配置 2、日志配置字典(知道咋么改就可以) 3、日志的使用 一、日志级别 import logginglogging.debug(调试日志) logging.info(消息日志) logging.warning(警告日志) logging.error(错误日志) logging.cr…

Java自增自减运算符

Java中的自增自减运算符分为两种: 自增运算符“ ”,作用是执行加法运算的变量值1 自减运算符“ -- ”,作用是执行加法运算的变量值-1 注意事项: “ ”和“ -- ”既可以放在变量的前面,也可以放在变量的后面。 自增自…

Linux驱动开发笔记(十三)Sysfs文件系统

文章目录 前言一、Sysfs1.1 Sysfs的引入1.2 Sysfs的目录结构1.2 Sysfs的目录详解1.2.1 devices1.2.2 bus1.2.3 class1.2.4 devices、bus、class目录之间的关系1.2.5 其他子目录 二、Sysfs使用2.1 核心数据结构2.2 相关函数2.2.1 kobject_create_and_add2.2.2 kobject_put()2.2.…

大模型自然语言生成自动驾驶可编辑仿真场景(其一 共十篇)

第一篇:LLM greater scene summarize 第二篇:LLM simulation Test effect 第三篇:LLM simulation driving scenario flow work 第四篇:LLM Algorithm flow description 第五篇:Configure the environment and back…

每天一个项目管理概念之PDCA

在项目管理中,PDCA(计划-执行-检查-行动)循环是一种广泛采用的质量管理工具,它帮助组织确保项目能够高效、有序地进行,并持续优化流程。本文将深入探讨PDCA循环的四个阶段,以及如何将其应用于项目管理中&am…

一键转换PDL至HTML,轻松驾驭文档格式,高效办公新纪元从此开启!

在信息爆炸的时代,文档格式繁多,如何高效处理这些文档成为了每个职场人士关注的焦点。现在,我们为您带来一款革命性的工具——一键转换PDL至HTML,让您轻松驾驭文档格式,开启高效办公新纪元! 首先&#xff0…

八-工具包3-pandas数据清洗及相关性

数据清洗概述 数据清洗是对一些没有用的、不合理的数据进行处理的过程。 很多数据集存在数据缺失、数据格式错误、错误数据或重复数据的情况,如果要使数据分析更加准确,就需要对这些没有用的数据进行处理。 样本数据: from io import StringIO import pandas as pdcsv_d…

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-14模型构造

14模型构造 import torch from torch import nn from torch.nn import functional as F#通过实例化nn.Sequential来构建我们的模型, 层的执行顺序是作为参数传递的 net1 nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256,10)) """ nn.…

不同@Transactional方法调用同一个synchronized方法引发的生成重复序号问题

背景: Transactional( propagation Propagation.REQUIRED ) 方法 A内调用synchronized方法 这种默认传播方式使得子函数也加入当前事务, synchronized方法结束之后, Transactional 方法A 执行结束之前, synchronized方法对数据库表所作变…

Electron快速入门(三):在(二)的基础上修改了一个文件夹做了个备忘录

Lingering Memories 诗绪萦怀 修改index.html <!--index.html--> <!DOCTYPE html> <html lang="zh-CN"><head><meta charset="UTF-8"><!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --><meta http…

opencv-python(十一)

图像加法 import numpy as np import cv2 img cv2.imread(jk.jpg) res cv2.add(img, img) cv2.imshow(Mypicture1,img) cv2.imshow(MyPicture2,res)cv2.waitKey(0) cv2.destroyAllWindows() res cv2.add(src1, src2, dstNone, maskNone, dtypeNone) src1&#xff1a;第一幅…

Android RecyclerView卡顿

Android RecyclerView卡顿的原因可能包括以下几点&#xff1a; 数据加载和渲染耗时&#xff1a; 当列表项较多或者数据量庞大时&#xff0c;如果在主线程中进行数据加载和布局渲染&#xff0c;会导致主线程阻塞&#xff0c;从而造成滑动卡顿。例如&#xff0c;在滚动过程中需要…

作为一名程序员,怎么才能写出简洁实用还漂亮的代码楼呢?这25个超惊艳的Python代码写法,你一定要学会!

前言 Python可以用于复杂的数据分析和Web开发项目&#xff0c;还能以极少的代码行数完成令人惊叹的任务。本文将分享25个简短的Python代码示例&#xff0c;用来展示Python编程语言的魅力和效率。 1.列表推导式 Python的列表推导式提供了一种优雅的方法来创建列表。 # 将一个…

以太坊==MetaMask获取测试币最新网址

估算分数https://community.infura.io/t/unable-to-receive-sepolia-eth-from-faucet/7715 Gitcoin Passport 水龙头地址&#xff0c;填入自己的测试地址 水龙头项目地址 GitHub - pk910/PoWFaucet: Modularized faucet for EVM chains with different protection methods (…

从0开发一个Chrome插件:用户反馈与更新 Chrome 插件

前言 这是《从0开发一个Chrome插件》系列的第二十二篇文章,也是最终篇,本系列教你如何从0去开发一个Chrome插件,每篇文章都会好好打磨,写清楚我在开发过程遇到的问题,还有开发经验和技巧。 专栏: 从0开发一个Chrome插件:什么是Chrome插件?从0开发一个Chrome插件:开发…

2023年JCR影响因子正式发布,点击查看能源与燃料领域期刊变化【持续更新02】

2024年6月20日&#xff0c;科睿唯安发布了2024年度《期刊引证报告》(Journal Citation Reports&#xff0c;JCR)&#xff0c;报告覆盖全面的高质量期刊资源&#xff0c;提供了丰富的数据、指标和分析。今年JCR的最大变化为&#xff1a;把属于不同数据库&#xff0c;但属于同一学…

学习笔记:(3)荔枝派Nano在lvgl下使用矢量字体显示中文(全志F1C200S))

荔枝派Nano在lvgl下使用矢量字体显示中文 1.软件版本要求2.在buildroot下编译freetype三级目录 1.软件版本要求 buildroot 2023.05.xFreeType 2.13.1lvgl v8.3.9lv_drivers v9.0.0-dev 2.在buildroot下编译freetype 三级目录

Linux ComfyUI安装使用;Stable Diffusion 3使用

1、Linux ComfyUI安装使用 参考: https://zhuanlan.zhihu.com/p/689021495 安装步骤: ## 1、下载ComfyUI git clone https://github.com/comfyanonymous/ComfyUI ## 2、进入ComfyUI,安装依赖包 cd ComfyUI pip install -r requirements.txt ## 3\安装插件 cd custom_nodes…

FEC 向前纠错编码

随写&#xff0c;看的有点杂&#xff0c;简单记一下。 应该叫ReedSolomon FEC RS算法简单来讲就是&#xff0c;根据已有数据&#xff0c;构造模型&#xff0c;然后根据模型判纠错&#xff1f; 简单来讲&#xff0c;两点确定一条直线&#xff0c;直线直线上的点都会满足 y kx…