记一次用户认证代码重构中设计模式及设计原则的使用

news2024/9/20 22:43:31

目录

    • 一、需求
    • 二、初始代码 - 上来就怼
    • 三、重构1 - 单一职责(方法级)
    • 四、重构2 - 单一职责(类级、策略模式)、简单工厂
    • 五、重构3 - 依赖注入(避免重复创建对象)
    • 六、重构4 - 使用Map替代if...else、享元模式
    • 七、重构5 - 满足开闭原则、迪米特原则
      • 7.1 扩展 - 责任链变种
    • 八、重构6 - DRY、模板方法

一、需求

接入AuthServer框架,预留UniLoginUserDetailsService接口,负责用户登录认证逻辑,
RBAC支持多种用户登录认证,如账号密码、LDAP用户,且后续支持扩展其他的登录认证方式如手机验证码等。
在这里插入图片描述

二、初始代码 - 上来就怼

public class RbacUserDetailsDispatchServiceImpl implements UniLoginUserDetailsService {
    /**
     * 身份类型参数名称identityType
     */
    private final String IDENTIY_TYPE_PARAMETER = ColumnUtils.getFieldName(UserAuth::getIdentityType);

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> loginParams) throws UsernameNotFoundException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //处理系统用户账密码登录
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
            //具体处理逻辑......
        }
        //处理LDAP登录
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            //具体处理逻辑......
        }
        //身份类型不存在
        else {
            throw I18nOAuth2AuthenticationException.ofRespCodeWithI18nMsg(SystemMessage.COMMON_LOGIN_FAILED);
        }
    }

    @Override
    public void authenticateUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //认证系统用户
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
            //具体处理逻辑......
        }
        //认证LDAP
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            //具体处理逻辑......
        }
    }
}

问题:

  • 违背单一职责原则
  • if…else分支过多
  • 后续扩展新的认证方式需修改该类,违背开闭原则

三、重构1 - 单一职责(方法级)

public class RbacUserDetailsDispatchServiceImplTemp implements UniLoginUserDetailsService {
    /**
     * 身份类型参数名称identityType
     */
    private final String IDENTITY_TYPE_PARAMETER = ColumnUtils.getFieldName(UserAuth::getIdentityType);

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> loginParams) throws UsernameNotFoundException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //处理系统用户账密码登录
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
            return this.loadSystemUserByAuthParams(loginParams);
        }
        //处理LDAP登录
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            return this.loadLdapUserByAuthParams(loginParams);
        }
        //身份类型不存在
        else {
            throw I18nOAuth2AuthenticationException.ofRespCodeWithI18nMsg(SystemMessage.COMMON_LOGIN_FAILED);
        }
    }

    @Override
    public void authenticateUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //认证系统用户
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
            this.authenticateSystemUser(loginParams, uniLoginUserDetails);
        }
        //认证LDAP
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            this.authenticateLdapUser(loginParams, uniLoginUserDetails);
        }
    }

    private UniLoginUserDetails loadSystemUserByAuthParams(Map<String, String> loginParams) {
        //具体处理逻辑......
    }

    private UniLoginUserDetails loadLdapUserByAuthParams(Map<String, String> loginParams) {
        //具体处理逻辑......
    }

    private void authenticateSystemUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) {
        //具体处理逻辑......
    }

    private void authenticateLdapUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) {
        //具体处理逻辑......
    }
}

改进:

  • 通过方法完成职责拆分

问题:

  • 单类中代码量过多
  • 单类中职责过多,负责多种认证方式,后续维护起来比较困难

四、重构2 - 单一职责(类级、策略模式)、简单工厂

在这里插入图片描述

public class RbacUserDetailsDispatchServiceImplTemp implements UniLoginUserDetailsService {
    /**
     * 身份类型参数名称identityType
     */
    private final String IDENTITY_TYPE_PARAMETER = ColumnUtils.getFieldName(UserAuth::getIdentityType);

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> loginParams) throws UsernameNotFoundException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //具体用户认证服务实现
        UniLoginUserDetailsService uniLoginUserDetailsService = null;
        //处理系统用户账密码登录
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
            uniLoginUserDetailsService = new SystemUserDetailsServiceImpl();
        }
        //处理LDAP登录
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            uniLoginUserDetailsService = new LdapUserDetailsServiceImpl();
        }
        //身份类型不存在
        else {
            throw I18nOAuth2AuthenticationException.ofRespCodeWithI18nMsg(SystemMessage.COMMON_LOGIN_FAILED);
        }
        //调用具体用户认证服务实现
        return uniLoginUserDetailsService.loadUserByAuthParams(loginParams);
    }

    @Override
    public void authenticateUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //具体用户认证服务实现
        UniLoginUserDetailsService uniLoginUserDetailsService = null;
        //处理系统用户账密码登录
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
            uniLoginUserDetailsService = new SystemUserDetailsServiceImpl();
        }
        //处理LDAP登录
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            uniLoginUserDetailsService = new LdapUserDetailsServiceImpl();
        }
        //调用具体用户认证服务实现
        uniLoginUserDetailsService.authenticateUser(loginParams, uniLoginUserDetails);
    }
}

改进:

  • 通过类拆分职责(策略模式),提高可维护性

问题:

  • 重复创建认证实现类,增加内存消耗

五、重构3 - 依赖注入(避免重复创建对象)

在这里插入图片描述

public class RbacUserDetailsDispatchServiceImplTemp implements UniLoginUserDetailsService {
    /**
     * 身份类型参数名称identityType
     */
    private final String IDENTITY_TYPE_PARAMETER = ColumnUtils.getFieldName(UserAuth::getIdentityType);
    
    @Resource
    private SystemUserDetailsServiceImpl systemUserDetailsServiceImpl;
    
    @Resource
    private LdapUserDetailsServiceImpl ldapUserDetailsServiceImpl;

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> loginParams) throws UsernameNotFoundException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //处理系统用户账密码登录
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
            return this.systemUserDetailsServiceImpl.loadUserByAuthParams(loginParams);
        }
        //处理LDAP登录
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            return this.ldapUserDetailsServiceImpl.loadUserByAuthParams(loginParams);
        }
        //身份类型不存在
        else {
            throw I18nOAuth2AuthenticationException.ofRespCodeWithI18nMsg(SystemMessage.COMMON_LOGIN_FAILED);
        }
    }

    @Override
    public void authenticateUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //处理系统用户账密码登录
        if (IdentityTypeEnum.SYSTEM.equalCode(identityType)) {
           this.systemUserDetailsServiceImpl.authenticateUser(loginParams, uniLoginUserDetails);
        }
        //处理LDAP登录
        else if (IdentityTypeEnum.LDAP.equalCode(identityType)) {
            this.ldapUserDetailsServiceImpl.authenticateUserInner(loginParams, uniLoginUserDetails);
        }
    }
}

改进:

  • 通过依赖注入避免重复创建对象,节省内存消耗

问题:

  • 大量的if…else逻辑

六、重构4 - 使用Map替代if…else、享元模式

public class RbacUserDetailsDispatchServiceImplTemp implements UniLoginUserDetailsService {
    /**
     * 身份类型参数名称identityType
     */
    private final String IDENTITY_TYPE_PARAMETER = ColumnUtils.getFieldName(UserAuth::getIdentityType);

    @Resource
    private SystemUserDetailsServiceImpl systemUserDetailsServiceImpl;

    @Resource
    private LdapUserDetailsServiceImpl ldapUserDetailsServiceImpl;


    /**
     * Map(身份类型 -> 对应类型的登录认证实现)
     */
    private Map<String, UniLoginUserDetailsService> authType2ServiceImplMap = new HashMap<>(4);

    //核心流程不用修改,若扩展新认证方式仅需修改此处init方法
    @PostConstruct
    private void init() {
        //初始Map(身份类型 -> 对应类型的登录认证实现)
        this.authType2ServiceImplMap.put(IdentityTypeEnum.SYSTEM.getCode(), this.systemUserDetailsServiceImpl);
        this.authType2ServiceImplMap.put(IdentityTypeEnum.LDAP.getCode(), this.ldapUserDetailsServiceImpl);
    }

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> loginParams) throws UsernameNotFoundException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //验证身份类型是否存在
        if (!StringUtils.hasText(identityType) || !this.authType2ServiceImplMap.containsKey(identityType)) {
            throw I18nOAuth2AuthenticationException.ofRespCodeWithI18nMsg(SystemMessage.COMMON_LOGIN_FAILED);
        }
        //调用对应身份类型的用户认证实现 - 根据参数加载用户信息
        return this.authType2ServiceImplMap.get(identityType).loadUserByAuthParams(loginParams);
    }

    @Override
    public void authenticateUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //调用对应身份类型的用户认证实现 - 认证用户信息
        this.authType2ServiceImplMap.get(identityType).authenticateUser(loginParams, uniLoginUserDetails);
    }
}

改进:

  • 使用Map替代if…else分支判断逻辑,代码更整洁(享元模式)
  • 扩展新的认证方式仅需修改init方法即可,核心流程处理代码无需调整

问题:

  • 扩展新的认证方仍需修改init方法
  • 扩展新的认证方仍需添加新实现类的依赖注入
  • 违背开闭原则

七、重构5 - 满足开闭原则、迪米特原则

在这里插入图片描述

public class RbacUserDetailsDispatchServiceImpl implements UniLoginUserDetailsService {
    /**
     * 身份类型参数名称identityType
     */
    private final String IDENTITY_TYPE_PARAMETER = ColumnUtils.getFieldName(UserAuth::getIdentityType);

    /**
     * 不同身份类型的登录认证实现列表(即会自动注入BaseUserDetailService的多个实现,根据BaseUserDetailService.getIdentityType()区分身份类型)
     */
    @Resource
    private List<BaseUserDetailService> rbacUserDetailsServices;

    /**
     * Map(身份类型 -> 对应类型的登录认证实现)
     */
    private Map<String, UniLoginUserDetailsService> authType2ServiceImplMap = new HashMap<>(4);

    @PostConstruct
    private void init() {
        //初始Map(身份类型 -> 对应类型的登录认证实现)
        this.rbacUserDetailsServices.stream().forEach(rbacUserDetailsService -> {
            this.authType2ServiceImplMap.put(rbacUserDetailsService.getIdentityType(), rbacUserDetailsService);
        });
    }

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> loginParams) throws UsernameNotFoundException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //验证身份类型是否存在
        if (!StringUtils.hasText(identityType) || !this.authType2ServiceImplMap.containsKey(identityType)) {
            throw I18nOAuth2AuthenticationException.ofRespCodeWithI18nMsg(SystemMessage.COMMON_LOGIN_FAILED);
        }
        //调用对应身份类型的用户认证实现 - 根据参数加载用户信息
        return this.authType2ServiceImplMap.get(identityType).loadUserByAuthParams(loginParams);
    }

    @Override
    public void authenticateUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //调用对应身份类型的用户认证实现 - 认证用户信息
        this.authType2ServiceImplMap.get(identityType).authenticateUser(loginParams, uniLoginUserDetails);
    }
}

改进:

  • 使用Spring支持集合注入的特性,避免单独依赖每个实现类
  • 将认证类型移到具体认证实现类中确定,无需由上层预先知道(认证类型为String,避免使用枚举导致后续未知认证类型无法扩展)、迪米特原则(最小知道原则)
  • 扩展新的认证方式,仅需添加一个新的基于BaseUserDetailService的实现类,无需修改原RbacUserDetailsDispatchServiceImpl中的任何代码,满足开闭原则

7.1 扩展 - 责任链变种

public class RbacUserDetailsDispatchServiceImplTemp implements UniLoginUserDetailsService {
    /**
     * 身份类型参数名称identityType
     */
    private final String IDENTITY_TYPE_PARAMETER = ColumnUtils.getFieldName(UserAuth::getIdentityType);

    @Resource
    private List<BaseUserDetailService> rbacUserDetailsServices;

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> loginParams) throws UsernameNotFoundException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //调用对应身份类型的用户认证实现 - 根据参数加载用户信息
        return this.rbacUserDetailsServices.stream()
                .filter(rbacUserDetailsService -> rbacUserDetailsService.getIdentityType().equals(identityType))
                .map(rbacUserDetailsService -> rbacUserDetailsService.loadUserByAuthParams(loginParams))
                .findAny()
                .orElseThrow(() -> {
                    //身份类型不存在
                    throw I18nOAuth2AuthenticationException.ofRespCodeWithI18nMsg(SystemMessage.COMMON_LOGIN_FAILED);
                });
    }

    @Override
    public void authenticateUser(Map<String, String> loginParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //获取参数中的身份类型
        String identityType = loginParams.get(IDENTITY_TYPE_PARAMETER);
        //调用对应身份类型的用户认证实现 - 认证用户信息
        this.rbacUserDetailsServices.stream()
                .filter(rbacUserDetailsService -> rbacUserDetailsService.getIdentityType().equals(identityType))
                .findAny()
                .ifPresent(rbacUserDetailsService -> rbacUserDetailsService.authenticateUser(loginParams, uniLoginUserDetails));
    }
}

八、重构6 - DRY、模板方法

随着业务的发展,用户认证需支持:

  • 用户连续登录失败则锁定用户
    • 锁定用户不允许继续登录
    • 用户登录成功后清空登录失败计数
  • 记录认证成功、失败审计事件

且这些业务规则适用于所有的认证类型,为了避免在每个认证实现类中都添加上述逻辑,则使用了模板方法模式。

在这里插入图片描述

public abstract class BaseUserDetailService implements UniLoginUserDetailsService {
    //......

    @Override
    public UniLoginUserDetails loadUserByAuthParams(Map<String, String> authParams) throws UsernameNotFoundException {
        //登录认证参数非空验证
        this.validateAuthFormsNotEmpty(authParams);

        //用户ID = userName:idType
        String userWithTypeId = this.buildUserLockId(authParams);
        //验证用户锁定状态,若用户已锁定则拒绝登录
        if (this.loginLockService.isUserLocked(userWithTypeId)) {
            //拒绝登录并给出提示信息
            throw this.buildUserLockedException(userWithTypeId, false);
        }

        //调用内部实现加载用户逻辑
        return this.loadUserByAuthParamsInner(authParams);
    }

    @Override
    public void authenticateUser(Map<String, String> authParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException {
        //用户ID = userName:idType
        String userWithTypeId = this.buildUserLockId(authParams);
        try {
            //验证验证码是否匹配
            this.validateCaptchaParams(authParams);
            //调用内部实现认证用户
            this.authenticateUserInner(authParams, uniLoginUserDetails);
            //登录成功则清除连续登录失败计数
            this.loginLockService.clearUserLoginErrorCount(userWithTypeId);
            //审计: 登录成功
            this.recordAuthenticateSuccessAudit(uniLoginUserDetails);
        } catch (AuthenticationException authEx) {
            //审计: 登录失败
            this.recordAuthenticateExceptionAudit(authParams, uniLoginUserDetails, authEx);
            //记录连续登录失败次数,若触发用户锁定则锁定用户、拒绝登录
            if (this.loginLockService.incrUserLoginErrorCountAndReturnLockable(userWithTypeId)) {
                //锁定用户
                this.loginLockService.lockUserAndClearUserLoginErrorCount(userWithTypeId);
                //拒绝登录并给出提示信息
                throw this.buildUserLockedException(userWithTypeId, true);
            }
            //抛出原异常(兼容原错误提示)
            throw authEx;
        }
    }
    
    //其他方法实现,省略......

    /**
     * 获取身份类型(具体用户服务支持的身份类型)
     *
     * @return 身份类型
     * @see IdentityTypeEnum
     */
    public abstract String getIdentityType();

    /**
     * 根据认证form参数查询用户信息 - 内部实现
     *
     * @param authParams form参数
     * @return 用户信息
     * @throws UsernameNotFoundException
     */
    public abstract UniLoginUserDetails loadUserByAuthParamsInner(Map<String, String> authParams) throws UsernameNotFoundException;

    /**
     * 认证用户(即form参数和用户信息中的凭证是否匹配等) - 内部实现
     *
     * @param authParams          form参数
     * @param uniLoginUserDetails 用户信息
     * @throws AuthenticationException
     */
    public abstract void authenticateUserInner(Map<String, String> authParams, UniLoginUserDetails uniLoginUserDetails) throws AuthenticationException;
}

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

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

相关文章

AI浪潮下的程序员命运:消失还是进化?

随着人工智能技术的迅猛发展&#xff0c;程序员职业正面临着前所未有的挑战与机遇。在这个变革的时代&#xff0c;程序员们究竟该如何应对&#xff0c;才能在AI浪潮中乘风破浪&#xff0c;开创属于自己的朝阳之路呢&#xff1f; 程序员这一职业正迎来前所未有的变革&#xff0…

DC-9靶机渗透测试

DC-9靶机 文章目录 DC-9靶机信息收集web渗透后台渗透用户横向移动权限提升靶机总结 信息收集 扫描目标主机存在的端口信息 存在80和22端口 看来思路应该不会有太大变化了&#xff0c;但是ssh端口是处于过滤的状态 进行漏洞扫描也没扫描出有可以利用的 但知道了一些界面 web渗透…

沪深300股指期货如何操作套期保值?

沪深300股指期货的套期保值是一种重要的风险管理工具&#xff0c;用于对冲投资组合面临的市场风险。 以下是套期保值流程的详细步骤说明&#xff1a; 第一&#xff0c;评估套保需求&#xff1a;投资者首先需要基于对市场走势的深入分析和对投资组合的细致评估&#xff0c;确定…

python-flask-上传多个文件并存储

本地环境&#xff1a;win10 / centos6 &#xff0c; python3 flask入门看这里&#xff1a; ↓ python-flask结合bootstrap实现网页小工具实例-半小时速通版_bootstrap flask-CSDN博客 https://blog.csdn.net/pxy7896/article/details/137854455 动态添加和删除表格中的行&…

论文概览 |《Urban Analytics and City Science》2024.07 Vol.51 Issue.6

本次给大家整理的是《Environment and Planning B: Urban Analytics and City Science》杂志2024年7月第51卷第6期的论文的题目和摘要&#xff0c;一共包括16篇SCI论文&#xff01; 论文1 Digital twins on trial: Can they actually solve wicked societal problems and chan…

[开端]JAVA抽象类使用到redis观察着

一、绪论 当redis内容发生变化时需要通知一些观察者做一些动作怎么做&#xff1f; 二、JAVA抽象类 public abstract class AbstractRedisChangeListener {public abstract void change(String key, String value, String crudType); }使用abstract进行修饰一个类 其中抽象类…

数字信号处理2: 离散信号与系统的频谱分析

文章目录 前言一、实验目的二、实验设备三、实验内容四、实验原理五、实验步骤1.序列的离散傅里叶变换及分析2.利用共轭对称性&#xff0c;设计高效算法计算2个N点实序列的DFT。3.线性卷积及循环卷积的实现及二者关系分析4.比较DFT和FFT的运算时间5.利用FFT求信号频谱及分析采样…

【机器学习】(基础篇三) —— 损失函数和梯度下降

损失函数 损失函数&#xff08;Loss Function&#xff09;&#xff0c;也称为代价函数&#xff08;Cost Function&#xff09;或误差函数&#xff08;Error Function&#xff09;&#xff0c;是机器学习和深度学习中一个核心的概念。它用于量化模型预测与实际目标之间的差异&a…

有序转化数组(LeetCode)

题目 给你一个已经 排好序 的整数数组 和整数 、 、 。对于数组中的每一个元素 &#xff0c;计算函数值 &#xff0c;请 按升序返回数组 。 解题 在时间复杂度为解决问题 def sortTransformedArray(nums, a, b, c):def f(x):return a * x * x b * x cn len(nums)result…

配置Mysql的慢查询日志

一、什么是Mysql慢查询日志 MySQL慢查询日志是MySQL数据库自带的一个功能&#xff0c;用于记录执行时间超过指定阈值的SQL语句&#xff0c;以便于后续的性能优化工作 帮助开发和DBA发现哪些SQL语句需要优化&#xff0c;在哪些地方需要修改&#xff0c;以提高数据库的性能 默认…

【Hot100】LeetCode—51. N 皇后

原题链接&#xff1a;51. N 皇后 1- 思路 使用回溯的方式实现&#xff0c;回溯三部曲 数据结构&#xff1a;定义三维数组&#xff0c;收集结果1- 回溯终止条件&#xff1a;如果遍历到 row n-1 此时就是结果收集的时刻2- 执行 for 循环遍历&#xff0c;执行递归和回溯 2- 实现…

【Linux】系列入门摘抄笔记-3-系统管理、显示相关命令

系统管理、显示相关命令 shutdown/关机和重启 sync 数据同步:把内存中的数据强制向硬盘中保存。不放心的话,应该在关机或重启之前手工执行几次,避免数据丢失。 shutdown [选项] 时间 [警告信息] shutdown -h now shutdown -h 05:30 shutdown -h 5 (5分钟后关机)reb…

计算机毕业设计选题推荐-视频点播系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

ubuntu安装微信

ubuntu安装微信 微信特性 可以截图无聊天乱码 安装指南 输入下面命令 wget -c -O atzlinux-v12-archive-keyring_lastest_all.deb https://www.atzlinux.com/atzlinux/pool/main/a/atzlinux-archive-keyring/atzlinux-v12-archive-keyring_lastest_all.debsudo apt -y insta…

全国不动产登记技能竞赛有哪些奖项

&#xff08;一&#xff09;个人综合奖 个人综合奖在参加代表展示的选手中产生。 1&#xff0e;对获得全国决赛第1名的选手&#xff0c;符合条件的&#xff0c;按程序向中华全国总工会推荐申报“全国五一劳动奖章”。 2&#xff0e;对获得全国决赛前15名的选手&#xff0c;由竞…

【Vue】vue3中通过自定义指令实现数字的动态增加动画

在Vue 3中通过自定义指令实现数字的动态增加动画&#xff0c;可以利用Vue的自定义指令功能&#xff0c;这允许你扩展Vue的内置指令&#xff0c;使得DOM操作更加灵活和强大。以下是如何创建一个自定义指令来实现数字动态增加动画的步骤&#xff1a; 效果演示 代码实现 1、定义指…

四种推荐算法——Embedding+MLP、WideDeep、DeepFM、NeuralCF

一、EmbeddingMLP模型 EmbeddingMLP 主要是由 Embedding 部分和 MLP 部分这两部分组成&#xff0c;使用 Embedding 层是为了将类别型特征转换成 Embedding 向量&#xff0c;MLP 部分是通过多层神经网络拟合优化目标。——用于广告推荐。 Feature层即输入特征层&#xff0c;是模…

【C++】string类——模拟实现

&#x1f680;个人主页&#xff1a;奋斗的小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言&#x1f4a5;1、string类主要函数接口&#x1f4a5;2、string类的模拟实现&#x1f4a5;2.1 构造和析构…

【Material-UI】FormGroup 组件:多选框分组的最佳实践详解

文章目录 一、FormGroup 组件概述1. 组件介绍2. 组件的基本结构 二、FormGroup 的关键特性1. 逻辑分组2. 状态管理3. 错误处理 三、FormGroup 的实际应用场景1. 多选项管理2. 逻辑验证3. 用户偏好设置4. 表单提交 四、注意事项1. 无障碍支持2. 样式定制 五、总结 在 Material-U…

AWVS下载安装使用教程图文教程(超详细)

《网络安全自学教程》 AWVS&#xff08;Acunetix Web Vulnerability Scanner&#xff09;是一款常用的Web网站漏洞扫描工具。 AWVS扫描时&#xff0c;对数据库有增删改的操作&#xff0c;部分渗透项目禁止使用&#xff0c;在实际环境中需要注意。 这篇文章使用 Windows Serve…