用Abp实现找回密码和密码强制过期策略

OpenIddict在ABP vNext中的深度集成:从配置到自定义授权类型 本文详细介绍了如何在ABP vNext框架中深度集成OpenIddict,从基础环境配置、核心授权流程(如授权码、客户端凭证流程)的实战,到实现自定义授权类型(以微信扫码登录为例),为构建灵活、安全且符合现代标准的身份认证与授权体系提供了完整路径。 阅读详情


用户找回密码,确切地说是 重置密码,为了保证用户账号安全,原始密码将不再以明文的方式找回,而是通过短信或者邮件的方式发送一个随机的重置校验码(带校验码的页面连接),用户点击该链接,跳转到重置密码页面,输入新的密码。这个重置校验码是一次性的,用户重置密码后立即失效。

用户找回密码是在用户没有登录时进行的,因此需要先校验身份(除用户名+密码外的第二种身份验证方式)。
第二种身份验证的前提是绑定了手机号或者邮箱,如果没有绑定,那么只能通过管理员进行原始密码重置。

密码强制过期策略,是指用户在一段时间内没有修改密码,在下次登录时系统阻止用户登录,直到用户修改了密码后方可继续登录。此策略提高用户账号的安全性。

在这里插入图片描述

找回密码和密码过期重置密码,两种机制有相近的业务逻辑,即密码重置。今天我们来实现这个功能。

重置密码

Abp框架中,AbpUserBase类中已经定义了重置校验码PasswordResetCode属性,以及SetNewPasswordResetCode方法,用于生成新的重置校验码。

[StringLength(328)]
public virtual string PasswordResetCode { get; set; }
public virtual void SetNewPasswordResetCode()
{
    PasswordResetCode = Guid.NewGuid().ToString("N").Truncate(328);
}

在UserAppService中添加ResetPasswordByCode,用于响应重置密码的请求。
在其参数ResetPasswordByLinkDto中携带了校验信息PasswordResetCode,因此添加了特性[AbpAllowAnonymous],不需要登录认证即可调用此接口

密码更新完成后,立刻将PasswordResetCode重置为null,以防止重复使用。

[AbpAllowAnonymous]
public async Task<bool> ResetPasswordByCode(ResetPasswordByLinkDto input)
{
    await _userManager.InitializeOptionsAsync(AbpSession.TenantId);

    var currentUser = await _userManager.GetUserByIdAsync(input.UserId);
    if (currentUser == null || currentUser.PasswordResetCode.IsNullOrEmpty() || currentUser.PasswordResetCode != input.ResetCode)
    {
        throw new UserFriendlyException("PasswordResetCode不正确");
    }

    var loginAsync = await _logInManager.LoginAsync(currentUser.UserName, input.NewPassword, shouldLockout: false);
    if (loginAsync.Result == AbpLoginResultType.Success)
    {
        throw new UserFriendlyException("重置的密码不应与之前密码相同");
    }

    if (currentUser.IsDeleted || !currentUser.IsActive)
    {
        return false;
    }

    CheckErrors(await _userManager.ChangePasswordAsync(currentUser, input.NewPassword));
    currentUser.PasswordResetCode = null;
    currentUser.LastPasswordModificationTime = DateTime.Now;
    await this._userManager.UpdateAsync(currentUser);

    return true;
}

找回密码

发送验证码

使用AbpBoilerplate.Sms作为短信服务库。

之前的项目中,我们定义好了ICaptchaManager接口,已经实现了验证码的发送、验证码校验、解绑手机号、绑定手机号

这4个功能,通过定义用途(purpose)字段以校验区分短信模板

public interface ICaptchaManager
{
    Task BindAsync(string token);
    Task UnbindAsync(string token);
    Task SendCaptchaAsync(long userId, string phoneNumber, string purpose);
    Task<bool> VerifyCaptchaAsync(string token, string purpose = "IDENTITY_VERIFICATION");
}

添加一个用于重置密码的purpose,在CaptchaPurpose枚举类型中添加RESET_PASSWORD

public class CaptchaPurpose
{
    ...

    public const string RESET_PASSWORD = "RESET_PASSWORD";

}

在SMS服务商管理端后台申请一个短信模板,用于重置密码。

在这里插入图片描述

打开短信验证码的领域服务类SmsCaptchaManager, 添加RESET_PASSWORD对应短信模板的编号

public async Task SendCaptchaAsync(long userId, string phoneNumber, string purpose)
{
    var captcha = CommonHelper.GetRandomCaptchaNumber();
    var model = new SendSmsRequest();
    model.PhoneNumbers = new string[] { phoneNumber };
    model.SignName = "MatoApp";
    model.TemplateCode = purpose switch
    {
        ...
        CaptchaPurpose.RESET_PASSWORD => "SMS_1587660"    //添加重置密码对应短信模板的编号
    };

    ...
}

接下来我们创建ResetPasswordManager类,用于处理找回密码和密码过期重置密码的业务逻辑。
注入UserManager,ISmsService,SmsCaptchaManager,EmailCaptchaManager。

public class ResetPasswordManager : ITransientDependency
{
    private readonly UserManager userManager;
    private readonly ISmsService smsService;
    private readonly SmsCaptchaManager smsCaptchaManager;
    private readonly EmailCaptchaManager emailCaptchaManager;

    public ResetPasswordManager(
        UserManager userManager,
        ISmsService smsService,
        SmsCaptchaManager smsCaptchaManager,
        EmailCaptchaManager emailCaptchaManager
        )
    {
        this.userManager = userManager;
        this.smsService = smsService;
        this.smsCaptchaManager = smsCaptchaManager;
        this.emailCaptchaManager = emailCaptchaManager;
    }

在ResetPasswordManager中添加SendForgotPasswordCaptchaAsync方法,用于短信或邮箱方式的身份验证。

public async Task SendForgotPasswordCaptchaAsync(string provider, string phoneNumberOrEmail)
{

    User user;
    if (provider == "Email")
    {
        user = await userManager.FindByEmailAsync(phoneNumberOrEmail);
        if (user == null)
        {
            throw new UserFriendlyException("未找到绑定邮箱的用户");
        }
        await emailCaptchaManager.SendCaptchaAsync(user.Id, user.EmailAddress, CaptchaPurpose.RESET_PASSWORD);


    }
    else if (provider == "Phone")
    {
        user = await userManager.FindByNameOrPhoneNumberAsync(phoneNumberOrEmail);
        if (user == null)
        {
            throw new UserFriendlyException("未找到绑定手机号的用户");
        }
        await smsCaptchaManager.SendCaptchaAsync(user.Id, user.PhoneNumber, CaptchaPurpose.RESET_PASSWORD);
    }


}

校验验证码

添加VerifyAndSendResetPasswordLinkAsync方法,用于校验验证码,并发送重置密码的链接。

public async Task VerifyAndSendResetPasswordLinkAsync(string token, string provider)
{
    if (provider == "Email")
    {
        EmailCaptchaTokenCacheItem currentItem = await emailCaptchaManager.GetToken(token);
        if (currentItem == null || currentItem.Purpose != CaptchaPurpose.RESET_PASSWORD)
        {
            throw new UserFriendlyException("验证码不正确或已过期");
        }

        var user = await userManager.GetUserByIdAsync(currentItem.UserId);
        var emailAddress = currentItem.EmailAddress;
        await SendEmailResetPasswordLink(user, emailAddress);

        await emailCaptchaManager.RemoveToken(token);
    }

    else if (provider == "Phone")
    {

        SmsCaptchaTokenCacheItem currentItem = await smsCaptchaManager.GetToken(token);
        if (currentItem == null || currentItem.Purpose != CaptchaPurpose.RESET_PASSWORD)
        {
            throw new UserFriendlyException("验证码不正确或已过期");
        }

        var user = await userManager.GetUserByIdAsync(currentItem.UserId);
        var phoneNumber = currentItem.PhoneNumber;
        await SendSmsResetPasswordLink(user, phoneNumber);

        await smsCaptchaManager.RemoveToken(token);
    }
    else
    {
        throw new UserFriendlyException("验证码提供者错误");
    }

}

发送重置密码链接

创建SendSmsResetPasswordLink,用于对当前用户产生一个NewPasswordResetCode,并发送重置密码的短信链接。

private async Task SendSmsResetPasswordLink(User user, string phoneNumber)
{
    var model = new SendSmsRequest();
    user.SetNewPasswordResetCode();
    var passwordResetCode = user.PasswordResetCode;
    model.PhoneNumbers = new string[] { phoneNumber };
    model.SignName = "MatoApp";
    model.TemplateCode = "SMS_255330989";
    //for aliyun
    model.TemplateParam = JsonConvert.SerializeObject(new { username = user.UserName, code = passwordResetCode });

    //for tencent-cloud
    //model.TemplateParam = JsonConvert.SerializeObject(new string[] { user.UserName, passwordResetCode });


    var result = await smsService.SendSmsAsync(model);

    if (string.IsNullOrEmpty(result.BizId) && result.Code != "OK")
    {
        throw new UserFriendlyException("验证码发送失败,错误信息:" + result.Message);
    }
}

创建接口

在UserAppService暴露出SendForgotPasswordCaptcha和VerifyAndSendResetPasswordLink两个接口,

注意这两个接口都需要添加[AbpAllowAnonymous]特性,因为在用户未登录的情况下,也需要使用这两个接口。

[AbpAllowAnonymous]
public async Task SendForgotPasswordCaptcha(ForgotPasswordProviderDto input)
{
    var provider = input.Provider;
    var phoneNumberOrEmail = input.ProviderNumber;

    await forgotPasswordManager.SendForgotPasswordCaptchaAsync(provider, phoneNumberOrEmail);

}

[AbpAllowAnonymous]
public async Task VerifyAndSendResetPasswordLink(SendResetPasswordLinkDto input)
{
    var provider = input.Provider;
    var token = input.Token;
    await forgotPasswordManager.VerifyAndSendResetPasswordLinkAsync(token, provider);

}

这两个接口分别在用户忘记密码的两个阶段调用,

  1. 第一阶段是发送验证码,
  2. 第二阶段是校验验证码并发送重置密码的链接。

在这里插入图片描述

密码强制过期策略

在User实体中添加一个属性,用于记录密码最后修改时间,在登录时验证这个时间至此时的时间跨度,如果超过一定时间(例如90天),强制用户重置密码。

[Required]
public DateTime LastPasswordModificationTime { get; set; }

改写接口

将重置校验码PasswordResetCode添加到AuthenticateResultModel中

public string PasswordResetCode { get; set; }

打开TokenAuthController,注入ResetPasswordManager服务对象

登录验证终节点方法Authenticate中,添加对密码强制过期的逻辑代码

[HttpPost]
public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)
{

    var loginResult = await GetLoginResultAsync(
                model.UserNameOrEmailAddress,
                model.Password,
                GetTenancyNameOrNull()
            );


    ...

    //Password Expiration Check
    if (DateTime.Now - loginResult.User.LastPasswordModificationTime > TimeSpan.FromDays(90))
    {
        loginResult.User.SetNewPasswordResetCode();

        return new AuthenticateResultModel
        {
            PasswordResetCode = loginResult.User.PasswordResetCode,
            UserId = loginResult.User.Id,
        };
    }

}

当登录账号的LastPasswordModificationTime距此时大于90天时,将阻止登录,并提示账户密码已过期,需要修改密码
在这里插入图片描述
在这里插入图片描述

Vue网页端开发

重置密码页面

创建Web端的重置密码页面,用于用户重置密码。
在这里插入图片描述

当用户通过短信或邮箱接收到重置密码的链接后,点击链接,会跳转到重置密码的页面,用户输入新密码后,点击提交,就可以完成密码重置。

连接格式如下

http://localhost:8080/reset-password-sample/reset.html?code=f16b5fbb057d4a04bce5b9e7f24e1d56&userId=1

项目参与实际生产中请加密参数,在此为了简单起见采用明文传递。

<template>
  <div id="app">
    <div class="title-container center">
      <h3 class="title">修改密码</h3>
    </div>
    <el-row>
      <el-form
        ref="loginForm"
        :model="input"
        class="login-form"
        autocomplete="on"
        label-position="left"
      >
        <el-form-item label="验证码">
          <el-input v-model="input.code" placeholder="请输入验证码" clearable />
        </el-form-item>
        <el-form-item label="新密码" prop="newPassword">
          <el-input
            v-model="input.newPassword"
            placeholder="请输入新密码"
            clearable
            show-password
          />
        </el-form-item>
        <el-form-item label="新密码(确认)" prop="newPassword2">
          <el-input
            v-model="input.newPassword2"
            placeholder="请再次输入新密码"
            clearable
            show-password
          />
        </el-form-item>

        <el-row type="flex" class="row-bg">
          <el-col :offset="6" :span="10">
            <el-button
              type="primary"
              style="width: 100%"
              @click.native.prevent="submit"
              >修改
            </el-button>
          </el-col>
        </el-row>
      </el-form>
    </el-row>
  </div>
</template>

创建页面时会根据url中的参数,获取code和userId。

created: async function () {
    var url = window.location.href;
    var reg = /[?&]([^?&#]+)=([^?&#]+)/g;
    var param = {};
    var ret = reg.exec(url);
    while (ret) {
      param[ret[1]] = ret[2];
      ret = reg.exec(url);
    }
    if ("code" in param) {
      this.input.code = param["code"];
    }
    if ("userId" in param) {
      this.input.userId = param["userId"];
    }
  },

点击修改时会触发submit方法,这个方法会调用ResetPasswordByCode接口,将UserId,newPassword以及resetCode回传。

 async submit() {
      if ((this.input.newPassword != this.input.newPassword2) == null) {
        this.$message.warning("两次输入的密码不一致!");
        return;
      }

      await request(
        `${this.host}${this.prefix}/User/ResetPasswordByCode`,
        "post",
        {
          userId: this.input.userId,
          newPassword: this.input.newPassword,
          resetCode: this.input.code,
        }
      )
        .catch((re) => {
          var res = re.response.data;
          this.errorMessage(res.error.message);
        })
        .then(async (res) => {
          var data = res.data.result;
          this.successMessage("密码修改成功!");
          
          window.location.href = "/reset-password-sample.html";
        })
        .finally(() => {
          setTimeout(() => {
            this.loading = false;
          }, 1.5 * 1000);
        });
    },

忘记密码控件

在登录页面中,添加忘记密码的控件。

在这里插入图片描述

resetPasswordStage 是判定当前是哪个阶段的变量,
0表示正常用户名密码登录(初始状态),1表示输入手机号或邮箱验证身份,2表示通过验证即将发送重置密码的链接。

默认两种方式,一种是短信验证码,一种是邮箱验证码,这里我们采用了elementUI的tab组件,来实现两种方式的切换。

<template v-else-if="resetPasswordStage == 1">
    <p>
    请输入与要找回的账户关联的手机号或邮箱。我们将为你发送密码重置连接
    </p>
    <el-tabs tab-position="top" v-model="forgotPasswordProvider.provider">
    <el-tab-pane :lazy="true" label="通过手机号找回" name="Phone">
        <el-row>
        <el-col :span="24">
            <el-input
            v-model="forgotPasswordProvider.providerNumber"
            :placeholder="'请输入手机号'"
            tabindex="2"
            >
            <el-button
                slot="append"
                @click="sendResetPasswordLink"
                :disabled="forgotPasswordProvider.providerNumber == ''"
                >下一步</el-button
            >
            </el-input>
        </el-col>
        </el-row>
    </el-tab-pane>

    <el-tab-pane :lazy="true" label="通过邮箱找回" name="Email">
        <el-row>
        <el-col :span="24">
            <el-alert
            v-if="showResetRequireSuccess"
            title="密码重置连接已发送至登录用户对应的邮箱,请查收"
            type="info"
            >
            </el-alert>
        </el-col>
        <el-col :span="24">
            <p>建设中..</p>
        </el-col>
        </el-row>
    </el-tab-pane>
    </el-tabs>
</template>

不通的阶段,将分别调用不同的接口,sendResetPasswordLink以及verifyAndSendResetPasswordLink。

调用verifyAndSendResetPasswordLink接口完毕时,resetPasswordStage将设置位初始状态,即0。

async sendResetPasswordLink() {
    await request(
    `${this.host}${this.prefix}/User/SendForgotPasswordCaptcha`,
    "post",
    this.forgotPasswordProvider
    )
    .catch((re) => {
        var res = re.response.data;
        this.errorMessage(res.error.message);
    })
    .then(async (re) => {
        if (re) {
        this.successMessage("发送验证码成功");
        this.resetPasswordStage++;
        }
    });
},
async verifyAndSendResetPasswordLink() {
    await request(
    `${this.host}${this.prefix}/User/VerifyAndSendResetPasswordLink`,
    "post",
    {
        provider: this.forgotPasswordProvider.provider,
        token: this.captchaToken,
    }
    )
    .catch((re) => {
        var res = re.response.data;
        this.errorMessage(res.error.message);
    })
    .then(async (re) => {
        if (re) {
        this.successMessage("发送连接成功");
        this.resetPasswordStage = 0;
        }
    });
},

密码过期提示

主页面中添加对passwordResetCode的响应,当passwordResetCode不为空时,显示一个提示框,提示用户密码已超过90天未修改,请修改密码。

<el-alert
    v-if="passwordResetCode != null"
    close-text="点此修改密码"
    title="密码已超过90天未修改,为了安全,请修改密码"
    type="info"
    @close="
        gotoUrl(
        '/reset-password-sample/reset.html?code=' +
            passwordResetCode +
            '&userId=' +
            userId
        )
    "
    >
</el-alert>

在这里插入图片描述
用户点击点此修改密码按钮时将跳转至重置密码页面。

项目地址

Github:matoapp-samples

MAPPO实战:从理论到代码的协作智能体训练指南 本文详细介绍了MAPPO算法在多智能体协作训练中的实战应用,从理论基础到代码实现全面解析。通过对比PPO与MAPPO的差异,深入讲解CTDE框架、网络架构设计、参数调优等关键环节,并提供实际项目中的调参经验和常见问题解决方案,帮助开发者快速掌握这一强化学习前沿技术。 阅读详情

相关推荐

51单片机-点亮LED和蜂鸣器

ULN2003是高耐压、大电流达林顿陈列,由七个硅NPN达林顿管组成。ULN2003是大电流驱动陈列,多用于单片机、只能仪表、PLC、数字量输出卡等控制电路中。可直接驱动蜂鸣器、继电器等负载。void delay(u16 i) // i等于1大约10uswhile(i--);//取反,高低电平不断变化,产生脉冲delay(10);// 周期200us。

三日沐水 1126

ABP动态配置数据库连接字符串 Oracle11g数据库

ABP动态配置数据库连接 Oracle11g数据库 ABP的仓储关于数据库连接字符串的配置是从web.config(app.config)的connectionStrings读取的。 我想实现的功能是使用Consul的KV加密存储Oracle数据库的连接字符串,在ABP的应用层动态获取。 采用的ABP版本4.7,.Net Famework 4.61,Oracle 数据库11.2,vs2019...

dacong的专栏 2520

Android13 CarPowerManagementService启动流程分析

在CarPowerManagementService中init调用PowerHalService的setListener设置事件侦听器以接收车辆的电源事件。

liuning1985622的博客 956

abp vnext token失效时间设置

abp vnext设置token失效时间

你要明白,任何问题都不是孤立存在的,一定有人曾经遇到过,并且已经有更好的解决办法了,只是我还不知道。我不应该在黑暗中独自前行,去重新发明轮子,也许我的顿悟,只是别人的基本功!我应该要站在巨人的肩膀上,学习更成熟的经验和方法,然后再来解决这个问题 1945

PTPX功耗分析实战——从脚本配置到报告解读

本文详细介绍了PTPX功耗分析的全流程,从脚本配置到报告解读,帮助工程师快速掌握芯片设计中的功耗估算技术。文章涵盖了环境搭建、波形文件处理、功耗报告解析等关键环节,并提供了实战经验和优化建议,特别适合需要提升PTPX功耗分析效率的开发者。

weixin_29248313的博客 279

基于YOLOv8-obb的自动车牌检测与CRNN车牌识别

基于YOLOv8-obb模型实现了针对带角度车牌的自动检测,并结合CRNN(Convolutional Recurrent Neural Network)技术进行车牌字符识别,旨在提高复杂场景下车牌识别系统的准确性和鲁棒性

坚强的强酸柠檬的博客 1145

网页忘记密码怎么办?一招帮你找回密码!

有没有遇到过这种情况,网页上还保存着密码,但是就是忘记了,想着换电脑或者退出登录后就怎么办?

qq_41822490的博客 1万+

FTP新手必看:vsftpd 530登录错误的8种实战解决方案(附详细配置步骤)

本文针对FTP服务配置中常见的vsftpd 530登录错误,提供了8种实战解决方案。从理解错误本质入手,系统性地排查用户列表策略、PAM认证、系统黑名单、Shell限制、目录权限及DNS解析等问题,并附有详细的配置步骤与命令,帮助新手快速定位并解决登录失败难题。

ik678901的博客 696

NET使用ABP框架搭建博客项目(二) 用户模块

首先我们先来设计下用户模块,简单用户模块设计涵盖两个类,用户表以及角色表。如下图:我们在Core项目中增加一个文件夹Domain,在Domain增加Customers文件夹,添加一个枚举PasswordFormat并且继承Int。/// <summary> /// 密码加密的格式枚举 /// </summary> public enum PasswordFormat:int

Mr.Color 7205

模糊控制——基本原理(附MATLAB程序)

模糊化:使用隶属函数将实际输入转换为模糊集合的隶属度值。规则库:利用“如果-那么”规则将模糊输入映射到模糊输出。推理机制:通过规则的模糊化和模糊集合的合成,生成模糊输出。去模糊化:将模糊输出转换为具体的控制信号,常用的方法包括质心法和最大隶属度法。这些步骤综合起来,实现了模糊控制系统对复杂和不确定系统的有效控制。模糊控制系统通过结合模糊逻辑和规则库来处理复杂系统中的不确定性和模糊性。它的核心在于模糊集合和隶属函数,通过推理机处理模糊规则,并最终通过去模糊化生成具体的控制信号。

qq_35623594的博客 5559

NuPlayer 之 Renderer

触发postDrainAudioQueue_l()或postDrainVideoQueue()开始渲染。解码器输出数据 → NuPlayer::Decoder::onMessageReceived。音频数据存入mAudioQueue,视频数据存入mVideoQueue。• 每次写入后通过mNotifyConsumed通知解码器。• 通过AudioSink::write()写入音频设备。• 根据VSync信号和音频时钟调整渲染时机。• 通过Surface直接提交给显示系统。

aningxiaoxixi的博客 310

ABP入门系列(16)——通过webapi与系统进行交互

ABP入门系列目录——学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1. 引言 上一节我们讲解了如何创建微信公众号模块,这一节我们就继续跟进,来讲一讲公众号模块如何与系统进行交互。 微信公众号模块作为一个独立的web模块部署,要想与现有的【任务清单】进行交互,我们要想明白以下几个问题: 如何进行交互? ABP模板项目中默认创建了webapi项目,其动态weba...

weixin_33985679的博客 755

第三篇:NVIDIA cuOpt Python API 详解:LP、QP、MILP 建模与求解,附核心术语名词解释

NVIDIA cuOpt是一款GPU加速的优化求解库,支持LP、QP、MILP和路径优化问题。本文详细介绍了其Python API使用方法,包括核心类Problem的变量定义、约束添加、目标设置和求解配置,并提供了术语解释。cuOpt通过GPU并行计算大幅提升大规模优化问题的求解效率,支持多种求解算法和参数配置,适用于需要高性能计算的优化场景。安装方式包含pip和Docker两种,验证安装后即可快速开始建模求解。

算法工程师 236

ABP vNext双Token策略全解析:用Redis实现无感刷新与强制下线

本文深入解析ABP vNext框架下的双Token策略,结合Redis实现高效认证方案。通过无感刷新与强制下线机制,解决传统Token机制在多端登录、并发请求中的瓶颈问题,提升系统安全性与性能。详细介绍了Redis存储结构设计、关键中间件实现及前端协作流程,为.NET开发者提供了一套完整的分布式认证解决方案。

c7d8e9的博客 338

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Abp框架Web站点的安全性提升

本文将从GB/T 28448-2019《信息安全技术 网络安全等级保护测评要求》规定的安全计算环境中解读、摘要若干安全要求,结合Abp框架,对站点进行安全升级。

jevonsflash的专栏 680

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

ABP vNext + OpenIddict实战:5种授权模式如何选?附完整配置代码

本文深入探讨了ABP vNext与OpenIddict整合中的5种授权模式选择策略,包括授权码模式、密码模式、客户端凭据模式等,提供了详细的配置代码和工程化决策指南。通过安全评估矩阵和典型场景适配建议,帮助开发者在不同业务需求下做出最优选择,确保系统安全性与扩展性。

julia4scientist的博客 914

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

终极隐私保护浏览器:Min如何重新定义快速与极简的上网体验

Min是一款以速度、极简设计和隐私保护为核心的创新浏览器。它通过精简界面设计减少干扰,同时集成强大的隐私保护技术,为用户提供安全高效的网页浏览体验。作为开源项目,Min的源代码结构清晰,主要功能模块分布在[js/](https://link.gitcode.com/i/060240ec48e6dd06a7800f9837c4b2bc)目录下,包括隐私保护相关的[filtering.js](http

gitblog_00004的博客 924

Min浏览器终极隐私保护指南:5大加密技术全面解析

Min浏览器是一款以"快速、极简、隐私保护"为核心设计理念的开源浏览器,通过多重加密技术构建全方位的隐私防护体系。本文将深入解析Min浏览器的五大核心隐私保护技术,帮助用户充分利用这款浏览器打造安全的网络环境。 ## 1. 智能广告过滤系统:从源头阻断隐私泄露 Min浏览器内置了强大的广告和跟踪器过滤功能,通过定期更新的过滤规则库有效阻止第三方跟踪脚本。其核心实现位于[ext/filterLi

gitblog_00711的博客 635

随谈年的技术生涯和技术成长

主从备份,主数据库,从数据库,我们都知道,是往主数据库写入,从数据库自动从主数据库复制数据,但是,这里是有延迟的,而且延迟还不低,不说多吧,100ms是很常见的!上面说到了读写分离,在这个框架中,我主要用dbContext的方式实现数据库的相关操作,别问为啥不用仓储,我感觉仓储的存在很奇怪,或者说不够直接,不够灵活!读写分离最大的问题,不是读写分离如何写,而是如何处理实际情况,比如写入了,更新了,你读取的时候还没有读取到,读取到的是旧的!其实在实际开发中,几乎的项目很多是走主库的,很少走从的,为啥呢?

ascasgascasasd的博客 519

《C#妹妹和Objective-C阿姨对话录》()自动释放池--拆迁队的外援

主从备份,主数据库,从数据库,我们都知道,是往主数据库写入,从数据库自动从主数据库复制数据,但是,这里是有延迟的,而且延迟还不低,不说多吧,100ms是很常见的!上面说到了读写分离,在这个框架中,我主要用dbContext的方式实现数据库的相关操作,别问为啥不用仓储,我感觉仓储的存在很奇怪,或者说不够直接,不够灵活!读写分离最大的问题,不是读写分离如何写,而是如何处理实际情况,比如写入了,更新了,你读取的时候还没有读取到,读取到的是旧的!其实在实际开发中,几乎的项目很多是走主库的,很少走从的,为啥呢?

sdvsheshs的博客 913

我来说说读写分离,就是数据库读写分离在ORM中是如何实现的,附上源码

主从备份,主数据库,从数据库,我们都知道,是往主数据库写入,从数据库自动从主数据库复制数据,但是,这里是有延迟的,而且延迟还不低,不说多吧,100ms是很常见的!上面说到了读写分离,在这个框架中,我主要用dbContext的方式实现数据库的相关操作,别问为啥不用仓储,我感觉仓储的存在很奇怪,或者说不够直接,不够灵活!读写分离最大的问题,不是读写分离如何写,而是如何处理实际情况,比如写入了,更新了,你读取的时候还没有读取到,读取到的是旧的!其实在实际开发中,几乎的项目很多是走主库的,很少走从的,为啥呢?

2501_94594970的博客 588

将openGLProgrammingGuide/版和openGLSuperBible的源代码转换为C#

主从备份,主数据库,从数据库,我们都知道,是往主数据库写入,从数据库自动从主数据库复制数据,但是,这里是有延迟的,而且延迟还不低,不说多吧,100ms是很常见的!上面说到了读写分离,在这个框架中,我主要用dbContext的方式实现数据库的相关操作,别问为啥不用仓储,我感觉仓储的存在很奇怪,或者说不够直接,不够灵活!读写分离最大的问题,不是读写分离如何写,而是如何处理实际情况,比如写入了,更新了,你读取的时候还没有读取到,读取到的是旧的!其实在实际开发中,几乎的项目很多是走主库的,很少走从的,为啥呢?

sdvsheshs的博客 283

我和AI不得不说的故事:现在是最好的时代也是最坏的时代

主从备份,主数据库,从数据库,我们都知道,是往主数据库写入,从数据库自动从主数据库复制数据,但是,这里是有延迟的,而且延迟还不低,不说多吧,100ms是很常见的!上面说到了读写分离,在这个框架中,我主要用dbContext的方式实现数据库的相关操作,别问为啥不用仓储,我感觉仓储的存在很奇怪,或者说不够直接,不够灵活!读写分离最大的问题,不是读写分离如何写,而是如何处理实际情况,比如写入了,更新了,你读取的时候还没有读取到,读取到的是旧的!其实在实际开发中,几乎的项目很多是走主库的,很少走从的,为啥呢?

gongyang1的博客 380

ASP.NET Boilerplate安全防护实战:XSS、CSRF与SQL注入纵深防御指南

在Web应用开发中,安全防护是保障系统稳定和数据安全的核心环节。其基本原理在于通过多层次、多维度的技术手段,构建纵深防御体系,以应对各类网络攻击。从技术价值看,有效的安全防护不仅能防止数据泄露和服务中断,更是企业级应用可信赖的基石。常见的应用场景包括用户输入处理、会话管理、数据库交互等关键环节。本文聚焦于ASP.NET Boilerplate框架,深入探讨如何针对XSS(跨站脚本攻击)、CSRF(跨站请求伪造)和SQL注入这三大经典威胁,结合防伪令牌、内容安全策略和参数化查询等热词技术,进行专项加固与实战配

weixin_30915951的博客 929

是它们的详细介绍。以下内容仅适用于 Redis .。 一、单机模式(Single) . 简介 ()最简单的部署方式,仅运行单个 Re ...

主从备份,主数据库,从数据库,我们都知道,是往主数据库写入,从数据库自动从主数据库复制数据,但是,这里是有延迟的,而且延迟还不低,不说多吧,100ms是很常见的!上面说到了读写分离,在这个框架中,我主要用dbContext的方式实现数据库的相关操作,别问为啥不用仓储,我感觉仓储的存在很奇怪,或者说不够直接,不够灵活!读写分离最大的问题,不是读写分离如何写,而是如何处理实际情况,比如写入了,更新了,你读取的时候还没有读取到,读取到的是旧的!其实在实际开发中,几乎的项目很多是走主库的,很少走从的,为啥呢?

gongyang1的博客 246

货拉拉开源两款三方库,为鸿蒙应用高效开发贡献力量

主从备份,主数据库,从数据库,我们都知道,是往主数据库写入,从数据库自动从主数据库复制数据,但是,这里是有延迟的,而且延迟还不低,不说多吧,100ms是很常见的!上面说到了读写分离,在这个框架中,我主要用dbContext的方式实现数据库的相关操作,别问为啥不用仓储,我感觉仓储的存在很奇怪,或者说不够直接,不够灵活!读写分离最大的问题,不是读写分离如何写,而是如何处理实际情况,比如写入了,更新了,你读取的时候还没有读取到,读取到的是旧的!其实在实际开发中,几乎的项目很多是走主库的,很少走从的,为啥呢?

asfasgawa的博客 865
上一篇: 用Abp实现两步验证(Two-Factor Authentication,2FA)登录(三):免登录验证
下一篇: [Asp.Net Core] 网站中的XSS跨站脚本攻击和防范
林晓lx
林晓lx 2017年优快云移动跨平台大赛优秀奖获得者 2017年优快云移动跨平台大赛优秀奖获得者
博客等级 码龄15年 3761粉丝 123原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林晓lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值