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

news2025/1/17 0:12:56

文章目录

    • 重置密码
    • 找回密码
      • 发送验证码
      • 校验验证码
      • 发送重置密码链接
      • 创建接口
    • 密码强制过期策略
      • 改写接口
    • Vue网页端开发
      • 重置密码页面
      • 忘记密码控件
      • 密码过期提示
    • 项目地址

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

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

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

在这里插入图片描述

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

重置密码

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

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

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

相关文章

一篇文章搞定《动手学深度学习》-(李牧)PyTorch版本的所有内容

目录 目录 简介 阅读指南 1. 深度学习简介 2. 预备知识 3. 深度学习基础 4. 深度学习计算 5. 卷积神经网络 6. 循环神经网络 7. 优化算法 8. 计算性能 9. 计算机视觉 10. 自然语言处理 环境 参考&#xff08;大家可以在这里下载代码&#xff09; 原书地址&#…

优思学院|精益生产和精益管理的区别

精益生产和精益管理&#xff0c;这两个概念我们或多或少都听说过。但是&#xff0c;你是否真的明白这两个概念的区别&#xff1f;或者你是否也像我一样&#xff0c;之前把这两个概念混淆在一起呢&#xff1f;今天&#xff0c;我要和大家分享的是&#xff0c;精益生产和精益管理…

用Flutter开发一款音乐App(从0到1开发一款音乐App)

Flutter Music_Listener(flutter音乐播放器) Flutter version 3.9 项目介绍 1、项目整体基于getxretrofitdiojsonserialize开发 2、封装通用控制器BaseController&#xff0c;类似jetpack mvvm框架中的BaseViemodel 3、封装基础无状态基类BaseStatelessWidget&#xff0c;结合…

jmap执行失败了,怎么获取heapdump?

在之前的OOM问题复盘中&#xff0c;我们添加了jmap脚本来自动dump内存现场&#xff0c;方便排查OOM问题。 但当我反复模拟OOM场景测试时&#xff0c;发现jmap有时可以dump成功&#xff0c;有时会报错&#xff0c;如下&#xff1a; 经过网上一顿搜索&#xff0c;发现两种原因可…

来 Azure 学习 OpenAI 三 - 用 Python 调用 Azure OpenAi API

大家好&#xff0c;我是微软学生大使 Jambo。在我们申请好 Azure 和 Azure OpenAI 之后&#xff0c;我们就可以开始使用 OpenAI 模型了。如果你还没有申请 Azure 和 Azure OpenAI&#xff0c;可以参考 注册 Azure 和申请 OpenAI。 本文将会以 Azure 提供的 Openai 端口为例&am…

2023年4月广东省计算机软考中/高级备考班招生简章

软考是全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;项目&#xff0c;是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试&#xff0c;既属于国家职业资格考试&#xff0c;又是职称资格考试。 系统集成…

VS Code 插件开发概览

VS Code 插件开发概览 前言 VS Code作为开发者的代码开发利器&#xff0c;越来越受开发者的喜爱。像我身边的前端&#xff0c;每天80%的开发工作都是在VS Code上完成的。随着人们对它的使用&#xff0c;不再满足简单的优雅&#xff0c;舒服写代码这一基本需求。有些人利用它进…

FA-PEG-MAL,叶酸-聚乙二醇-马来酰亚胺 实验用科研试剂;Folic acid PEG Maleimide

FA-PEG-MAL,叶酸-聚乙二醇-马来酰亚胺 中文名称&#xff1a;叶酸-聚乙二醇-马来酰亚胺 英文名称&#xff1a;Folic acid PEG Maleimide, FA-PEG-MAL 性状&#xff1a;固体或者粘稠液体&#xff0c;取决于分子量大小。 溶剂&#xff1a;溶于水、DMF、DMSO等常规有机溶剂 分…

Redis高级之IO多路复用和epoll(十二)

nginx 的反向代理也是采用了IO多路复用 1.是什么 I/O 网络 I/O 多路 多个客户端连接&#xff08;连接就是套接字描述符&#xff0c;即socket 或者 channel&#xff09;&#xff0c;指的是多条 TCP 连接 复用 用一个进程来处理多条的连接&#xff0c;使用单进程就能实现同时处…

【cmake学习】set_target_properties 常见属性以及获取target 属性

set_target_properties 的作用是设置目标的属性&#xff0c;可以是目标文件输出的名称或者目录、目标文件的版本号。与之对应的&#xff0c;我们可以使用 get_target_properties 来获取目标文件某一属性对应的值。 命令格式如下&#xff1a; set_target_properties(目标文件1…

凌恩生物美文分享|基于宏基因组的氮循环分析内容重磅升级!

元素循环是生物地球化学循环的重要环节&#xff0c;主要涉及碳、氮、磷、硫等元素的循环过程。凌恩生物强势推出基于宏基因组的氮循环研究方案&#xff0c;构建了完整的氮循环循环模式图&#xff0c;对宏基因组数据进行深入挖掘&#xff0c;各部分结果图可直接用于文章发表&…

NDK RTMP直播客户端三

在之前完成的实战项目【FFmpeg音视频播放器】属于拉流范畴&#xff0c;接下来将完成推流工作&#xff0c;通过RTMP实现推流&#xff0c;即直播客户端。简单的说&#xff0c;就是将手机采集的音频数据和视频数据&#xff0c;推到服务器端。 接下来的RTMP直播客户端系列&#xff…

Openssh 版本升级至8.4

目录 安装包下载地址 zlib包 openssl包 openssh 1、为了防止升级失败登陆不了&#xff0c;所以需要安装telnet 2、检查环境 2.1安装所需的相关组件 2.2备份原来的数据 2.3删除现有的安装sshd的相关软件包 3、下载所需的源码包 3.1编译安装sshd 3.2查看ssh命令的执…

XML文件检索技术:Xpath

纠正&#xff1a;上图中是通过根元素、父元素、子元素… Xpath检索方法及路径&#xff1a; 绝对路径代码示例&#xff1a; 47行&#xff1a;Xpath解析技术也是基于Dom4J的技术&#xff1b; 52行&#xff1a;List<Node> 创建Node类型的集合nameNodes&#xff0c;selec…

大数据应用开发--概述

大数据应用开发–概述 1. 大数据应用开发简介 1.1 数据分析的概念 数据分析就是利用数学、统计学理论相结合科学统计分析方法对数据库中的数据、Excel数据、收集的大量数据、网页抓取的数据进行分析&#xff0c;从中提取有价值的信息形成结论并进行展示的过程。 数据分析的目…

没想到大厂Adobe还有这些“猫腻”!

北京时间周四晚间&#xff0c;图像及视频生产力工具大厂Adobe发布公告&#xff0c;宣布旗下的视频创作应用Premiere Pro将喜提一系列新的AI功能。这也是Adobe上个月发布AIGC创作功能“萤火虫”后的最新动作。综合Adobe的官方公告和演示视频&#xff0c;最大亮点就是基于文字的视…

生存函数(Survival function)

文章目录1. 定义2. 生存函数的例子3. 参数生存函数3.1 指数生存函数&#xff08;Exponential survival function&#xff09;3.2 威布尔生存函数&#xff08;Weibull survival function&#xff09;3.3 其他参数生存函数4. 非参数生存函数5. 性质6. Kaplan–Meier estimator6.1…

总结824

学习目标&#xff1a; 4月&#xff08;复习完高数18讲内容&#xff0c;背诵21篇短文&#xff0c;熟词僻义300词基础词&#xff09; 学习内容&#xff1a; 英语&#xff1a;早上 读了《nasty place》&#xff0c;单词150个 高数&#xff1a;看了12讲二重积分的内容&#xff0…

算法设计与智能计算 || 专题六: 不可导凸函数的最优解搜索问题

不可导凸函数的最优解搜索问题 文章目录不可导凸函数的最优解搜索问题1. 次梯度下降方法1.1 基于次梯度的 Lasso 回归求解1.2 次梯度求解 Lasso 算法1.3 编程实现2. 软阈值方法2.1 软阈值求解Lasso回归1. 次梯度下降方法 如目标函数包含不可微分的部分&#xff0c;形如 E(w)1N…

计组2.3——浮点数的表示和运算

计组2.3 浮点数 #mermaid-svg-hwjyO2bt7hFXy1eD {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hwjyO2bt7hFXy1eD .error-icon{fill:#552222;}#mermaid-svg-hwjyO2bt7hFXy1eD .error-text{fill:#552222;stroke:#552…