Spring Boot + Vue3前后端分离实战wiki知识库系统<十三>--单点登录开发二

news2024/11/24 0:16:23

接着Spring Boot + Vue3前后端分离实战wiki知识库系统<十二>--用户管理&单点登录开发一继续往下。

登录功能开发: 

接下来则来开发用户的登录功能,先准备后端的接口。

后端增加登录接口:

1、UserLoginReq:

先来准备用户登录的请求实体:

package com.cexo.wiki.req;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

public class UserLoginReq {

    @NotEmpty(message = "【用户名】不能为空")
    private String loginName;

    @NotEmpty(message = "【密码】不能为空")
    // @Length(min = 6, max = 20, message = "【密码】6~20位")
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,32}$", message = "【密码】规则不正确")
    private String password;

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", loginName=").append(loginName);
        sb.append(", password=").append(password);
        sb.append("]");
        return sb.toString();
    }
}

对于用户登录,只需要用户名和密码既可,其中有一个小细节需要说明一下,就是对于之前后台实现的用户管理保存的实体其密码是做了非常详细的规则提示的:

而这里对于用户登录的密码规则校验,则不能提示这么详情了,因为提示这么详情容易被人根据规则来进行密码破解,所以这里就提示了一个比较模糊的校验提示:

2、UserLoginResp:

再来准备登录的response实体:

package com.cexo.wiki.resp;

public class UserLoginResp {
    private Long id;

    private String loginName;

    private String name;

    private String token;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    @Override
    public String toString() {
        return "UserLoginResp{" +
                "id=" + id +
                ", loginName='" + loginName + '\'' +
                ", name='" + name + '\'' +
                ", token='" + token + '\'' +
                '}';
    }
}

3、Controller增加登录接口:

4、Service实现登录逻辑:

那接下来咱们是直接来校验用户名和密码的正确性么?其实应该先校验用户名是否存在,如果不存在,则密码都没密要校验了,所以先来查一下:

而处理异常可以采用在之前用户管理保存时的方式,抛出异常:

这里咱们先定义一个用户名不存在的异常码:

这里提示语貌似写错了吧。。其实这里是故意写一个模糊的提示的,因为给用户提示得越详细,越容易被攻击,所以这种风险意识在做项目时是很重要的。好,接下来就可以抛出这个异常了:

接下来再来判断密码的正确性:

最后,还有一个细节,就是目前这两块给用户提示了一个一模一样的提示:

很明显这对于后端排错不太好,所以有必要针对这俩加点日志:

前端增加登录模态框:

效果:

先来看一下登录的效果:

实现:

1、增加登录入口:

接下来则先在前端页面中增加一个用户登录的入口,这里很明显是应该在头部区域加,也就是:

此时的效果:

另外这里既然改了头部区域,左上角这块的logo也顺带改一下,目前这里是一个色块,没有什么实际意义,这里改一下:

控制它的css在这:

这里将这个logo的css先去掉,在头部组件里来定义就成:

然后修改为:

此时效果:

2、 点击弹出模态登录窗口:

先来给登录这个a标签增加点击事件:

而模态框的处理在之前的页面中已经多次用到了,所以这里就不过多解释了,直接准备一个登录的模态框:

其中提交的时候得请求接口,需要耗时,交互上肯定需要有loading等待效果,所以先提前把响应式的变量定义好:

此时运行看一下:

3、登录点击事件添加:

接下来给模拟框增加一个提交事件:

这里为了使用方便,给表单输一个默认值,如下:

此时运行:

4、发起登录请求:

最后发起登录请求,这块的网络请求代码也没啥好说的,直接copy原来的逻辑改下接口就可以了,如下:

其中需要导两个组件:

好,此时运行看一下效果:

成功登录。

登录成功处理并集成vuex:

 目前我们已经登录成功,但是登录成功之后,其实就给了一个提示,没有做登录成功之后的逻辑处理,接下来咱们则来完善它。

后端保存用户信息:

1、集成Redis:

由于需要将用户登录成功的信息以token形式存到Redis当中,所以先在工程中集成它:

而关于Redis的环境的配置这里就不说明了,当时在前后端接口设计与配置中心系统<二十六>-------后端-DAU超千万的移动端接口设计实现2【整合与优化Swagger2文档Api、登录功能实现与Response设计、基于Redis实现高速的用户信息缓存、基于注解拦截器实现接口访问控制与登录态处理】这篇已经详细学习过了,这块网上资料也比较多,我本机装好之后,先使用此命令启动一下它:

redis-server

然后再用可视化的图形工具RDM连接看一下:

目前里面没有存任何信息,所以数量都是0,然后再配置一下redis的数据源,类似于mysql一样:

2、登录时生成token并存入Redis中:

此时我们就需要修改一下登录的逻辑时,在登录成功之后,需要生成一个token,这里就用之前使用的雪花算法来生成,如下:

此时则需要将token存入到Redis当中,此时需要这么来做:

其中有个细节需要注意,我们目前存放到Redis中是将实体转换成了一个json串了:

如果想直接保存实体,注意需要让实体实现序列化的接口,不然保存的时候会报如下异常:

另外此token信息也需要返回给前端,因为之后前端也需要将此token信息在请求时携带上进行身份的识别,所以:

其中为了调试方便,将这块加个日志输出:

3、测试:

接下来咱们来登录测试一下是否写入成功了:

显示是成功了,到RDM可视化工具中刷新查看一下:

貌似是有了,不过看了是一串乱码,为了验证这个token写入到Redis是没有问题的,下面在TestController中增加测试方法,如下:

然后咱们就可以这样来测试了:

咱们运行一下:

那对于这里的登录时,生成这么一个token:

此时借助这个测试能查出存在redis中的用户信息么?

木问题。

前端显示登录用户:

接下来在用户登录成功之后,则需要显示出用户的昵称,修改页面:

其中user是一个响应式的变量,定义一下:

另外由于数据库中用户test的密码是test123,所以这里改一下:

然后在登录成功之后则给它进行赋值,如下:

接下来在登录入口处增加显示与隐藏的控制:

接下来运行看一下:

集成vuex:

说明:

接下来说明一个问题,就是目前我们登录后信息是在头部组件中来保存的:

那如果我想在底部组件里也能拿到用户的登录信息该怎么办呢?

很明显是拿不到的,因为定义的头部的响应式变量不能在其它页面使用,此时就得声明一个全局的变量来进行存储,这里就需要使用到vuex了,度娘先了了解一下它:

哦,就是来解决多组件数据通信的问题的,刚好符合我们目前的使用场景,而使用它的位置就在这块:

接下来咱们则来使用一下它。

实现:

1、定义一个全局的变量:

2、定义操作变量的函数:

光定义变量还不行,得定义操作它的行为,这里则会涉及到如下两处:

其中mutations是同步函数,而actions是支持异步的,这里我们在mutations中来进行定义:

3、返回store:

最后需要将这个store返回出去,以便可以在其它界面进行使用:

4、使用store保存用户信息:

接下来我们回到头部组件中,在登录成功之后使用store来保存一下用户信息,写法如下:

5、底部组件中获取用户信息:

接下来咱们就可以使用这个全局的数据了:

其中computed的含义是:如果一个响应式变量是要根据某个变量的变化而计算得来,就可以使用computed。

其中这里有个代码可以简化一下,就是它:

6、运行:

所以vuex其实就是全局响应式的变量,单纯的组件里面的响应式变量是做不到这种效果的。

最后更改一个文案:

而它应该受登录状态的控制,所以加一个显隐逻辑:

再来运行一下:

使用sessionStorage解决刷新数据丢失的问题:

说明:

接下来再来看一个问题:

刷新之后,登录态就没有了,这也反应出vuex有问题,浏览器一刷新就没有了,而要解决这个问题就需要使用sessionStorage了,度娘又来了解一下它: 

其中标红的是一些关于它值得关注的,它会随着浏览器页面的存在而存在,如果关闭页面了则数据就不存在了,在搜它的概念时,还搜到了一个在前端经常会提及的话题cookie 、sessionstorage 、localstorage三者的区别_夜晚收集者的博客-CSDN博客:

关于这块自己网上了解一下既可。

实现:

1、拷一个工具类:

为了使用sessionStorage,拷一个工具类进来:

SessionStorage = {
    get: function (key) {
        var v = sessionStorage.getItem(key);
        if (v && typeof (v) !== "undefined" && v !== "undefined") {
            return JSON.parse(v);
        }
    },
    set: function (key, data) {
        sessionStorage.setItem(key, JSON.stringify(data));
    },
    remove: function (key) {
        sessionStorage.removeItem(key);
    },
    clearAll: function () {
        sessionStorage.clear();
    }
};

为啥要封装一下呢,因为默认的sessionStorage只支持存string,这里扩展保存一个object类型,其实也就是最终会将object转换成Json字符串再保存:

2、引入工具类:

3、使用sessionStorage:

接下来我们在保存user信息时,除了使用vuex保存在全局响应式的变量当中之外,还需要保存到sessionStorage,所以好的做法就是在vuex这个文件中进行处理,先来引入工具类,引入的方法还记得不,其实在之前md5工具方法时已经使用过了,回忆一下:

所以定义一下:

其中let后面的名称取的是js中定义的它:

然后在获取用户信息时加上从sessionStorage来取的逻辑:

另外在设置user信息时,则需要将其存到SessionStorage当中:

其中有个代码可以优化一下:

都是常量,可以将其提取一下,提高维护性:

接下来运行看一下:

其中可以看到footer下的用户登录信息在浏览器刷新之后是能够被保留的,而关闭浏览器当前窗口就清空了,符合SessionStorage的定义场景,只是右上角的header用户信息在浏览器刷新时没有保留住,是因为header页面的user变量没有改成computed,如footer页面报写的这样:

所以咱们将header的代码改成这样:

将其改为:

这样就会去监听store中user的变化,不过这样改之后有个报错:

这里就没必要了,直接将user保存到store中就可以了:

接下来运行看一下效果:

成功修复浏览器刷新用户信息被清空的问题。

增加退出登录功能:

1、UserController.logout():

对于退出登录的接口其实只需要传一个token参数既可,所以定义接口如下:

其实也就是根据token将redis对应的信息给删除既可。

2、前端增加退出按钮:

而通常退出操作是需要给用户一个确认提示的,所以在它外层再包装一层:

3、调用退出登录接口:

接下来咱们就可以处理退出登录的点击事件,如下:

4、运行:

接下来运行看一下效果,发现后台报错了:

这是因为登出接口是一个get请求,而我们在后台声明的是:

修改一下:

再运行:

这里貌似界面上看着有点别扭,就是退出登录一般是在用户登录信息的右侧的,现在在左侧:

更改一下顺序:

因为login-menu的样式是:

最上面的元素就在最右侧边了,最后的样子:

增加登录校验:

有了用户登录之后, 对于页面中的功能则需要进行登录的校验了,对于管理类的入口都是需要登录之后才能预览,而对于文章查看是不需要的,所以,接下来进行登录校验的处理。

后端增加拦截器,校验token有效性:

1、 新建一个拦截器LoginInterceptor:

对于拦截器在之前已经有使用过了:

同样的,对于这个登录拦截器写法也差不多,关于这块的代码就不过多解释了,比较好理解:

package com.jiawa.wiki.interceptor;

import com.alibaba.fastjson.JSON;
import com.jiawa.wiki.resp.UserLoginResp;
import com.jiawa.wiki.util.LoginUserContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 拦截器:Spring框架特有的,常用于登录校验,权限校验,请求日志打印
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    private static final Logger LOG = LoggerFactory.getLogger(LoginInterceptor.class);

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 打印请求信息
        LOG.info("------------- LoginInterceptor 开始 -------------");
        long startTime = System.currentTimeMillis();
        request.setAttribute("requestStartTime", startTime);

        // OPTIONS请求不做校验,
        // 前后端分离的架构, 前端会发一个OPTIONS请求先做预检, 对预检请求不做校验
        if (request.getMethod().toUpperCase().equals("OPTIONS")) {
            return true;
        }

        String path = request.getRequestURL().toString();
        LOG.info("接口登录拦截:,path:{}", path);

        //获取header的token参数
        String token = request.getHeader("token");
        LOG.info("登录校验开始,token:{}", token);
        if (token == null || token.isEmpty()) {
            LOG.info("token为空,请求被拦截");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        Object object = redisTemplate.opsForValue().get(token);
        if (object == null) {
            LOG.warn("token无效,请求被拦截");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        } else {
            LOG.info("已登录:{}", object);return true;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (Long) request.getAttribute("requestStartTime");
        LOG.info("------------- LoginInterceptor 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//        LOG.info("LogInterceptor 结束");
    }
}

逻辑简单来说的话就是先来判断token是否为空,如果不为空,则再到redis中来获取一下。其中校验不通过时,返回一个401:

2、配置拦截器:

接下来要想拦截器生效,则需要到这来配置一下:

然后接下来得进行一下配置,因为有些接口是需要进行登录拦截的,如下:

package com.cexo.wiki.config;

import com.cexo.wiki.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {

    //    @Resource
//    LogInterceptor logInterceptor;//配置拦截器
    @Resource
    LoginInterceptor loginInterceptor;

    public void addInterceptors(InterceptorRegistry registry) {
//        registry.addInterceptor(logInterceptor)
//                .addPathPatterns("/**");
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/test/**",
                        "/redis/**",
                        "/user/login",
                        "/category/all",
                        "/ebook/list",
                        "/doc/all/**",
                        "/doc/vote/**",
                        "/doc/find-content/**",
                        "/ebook-snapshot/**"
                );
    }
}

其中这里用到了“**”:

则表示是任意值,好,加了拦截器之后咱们再来访问一下页面:

首页查看电子书都正常,但是点击用户管理时,页面的数据加载不出来了,查看一下后台日志,其实就是这个登录拦截器给拦了:

前端请求增加token参数:

目前后端加了token校验之后,在前端则需要在请求头中增加token信息了,很显然也需要在统一接口拦截器中来进行处理,那vue中是如何处理的呢?其实使用的网络组件axios就有相应的处理办法,这块其实在之前https://www.cnblogs.com/webor2006/p/17182186.html已经使用过了:

所以在这里加上token信息的逻辑:

此时再运行你会发现用户管理模块在登录之后内容还是出不来,这里就有一个坑需要说明一下,就是这块:

但是在用户登录时往Redis写入时:

这样类型就不匹配了,所以需要这样改一下:

此时再运行看一下效果:

可以看到在登录之后再查询用户管理的列表就出来了,可见现在的token的校验已经是好使了。

前端页面增加登录校验:

未登录时,管理菜单要隐藏:

现在对于管理界面在用户没有登录时也能被看到,很显然是不合理的,所以接下来处理一下它:

根据之前的经验,对于元素的显隐可以用它:

挺简单,但是你运行发现不好使,反而是把这个v-show加到<router-link>中可以:

但是还是有瑕疵,就是隐藏时这个会占用宽度:

此时就需要自己来写css样式了,如下处理:

解释一下:

而如果没登录,则需要将元素给隐藏,注意这里的style里面是需要定义一个json的,好,再运行看一下:

对路由做判断,防止用户通过手敲url访问管理页面:

现在还有个问题,就是虽说已经在登录菜单上已经根据登录状态进行显隐控制了,但是用户还是可以直接通过管理的路由地址来访问到,比如:

所以接下来处理它。

1、路由中增加meta信息:

为了要处理需要登录的路由地址,这里需要在路由配置中增加一个meta信息,如下:

2、路由登录拦截:

接下来咱们则需要进行路由跳转的一个拦截处理,这块的写法也比较固定,写一次就明白了:

然后接下来就是要判断路由地址中有木有这个mate信息,如下:

而条件体里面的写法如下:

3、运行:

下面来运行看一下效果:

成功进行路由的登录拦截了。

总结:

好了,花了很长的篇幅就将用户登录相关的给学习完了,还是收获很多的,比如密码的双层加层,登录的拦截等等,下次继续。

  关注个人公众号,获得实时推送

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

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

相关文章

都在谈网关,Modbus网关到底是什么

随着工业自动化的不断发展&#xff0c;各种协议和标准在行业中变得越来越重要。其中&#xff0c;Modbus协议是一种在工业自动化领域非常流行的通信协议&#xff0c;而Modbus网关是实现Modbus协议转换的关键设备。本文将介绍HiWoo Box&#xff0c;作为Modbus网关的应用和发展趋势…

2023App测试必掌握的核心测试:UI、功能测试

一、UI测试 UI即User Interface (用户界面)的简称。UI 设计则是指对软件的人机交互、操作逻辑、界面美观的整体设计。好的UI设计不仅是让软件变得有个性有品味,还要让软件的操作变得舒适、简单、自由、充分体现软件的定位和特点。手机APP从启动界面开始, 到运行过程,直至退出,…

【校招VIP】测试方案之测试需求分析

考点介绍&#xff1a; 需求分析就是要弄清楚用户需要的是什么功能&#xff0c;用户会怎样使用系统。这样我们测试的时候才能更加清楚的知道系统该怎么样运行&#xff0c;才能更好的设计测试用例&#xff0c;才能更好的测试。 测试方案之测试需求分析-相关题目及解析内容可点击…

C/C++空格分开输出 2019年12月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C空格分开输出 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C空格分开输出 2019年12月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 读入一个字符&#xff0c;一个整…

Linux知识点 -- 网络基础(二)-- 应用层

Linux知识点 – 网络基础&#xff08;二&#xff09;-- 应用层 文章目录 Linux知识点 -- 网络基础&#xff08;二&#xff09;-- 应用层一、使用协议来实现一个网络版的计算器1.自定义协议2.守护进程3.使用json来完成序列化 二、HTTP协议1.概念2.HTTP协议请求和响应的报文格式3…

DB2 JDBC连接详解(附DEMO~超详细)

DB2 JDBC连接详解 DB2 JDBC连接详解摘要引言正文1. JDBC基础2. 配置DB2JDBC连接2.1 DB2连接JDBC2.2 DB2连接JDBC是否成功2.3 DB2连接JDBC获取表信息注释等2.4 DB2连接JDBC根据表名获取字段信息注释等2.5 执行 SQL 查询2.6 执行 SQL 查询2.7 插入数据2.8 执行存储过程2.9 批处理…

Vue3 封装 element-plus 图标选择器

一、实现效果 二、实现步骤 2.1. 全局注册 icon 组件 // main.ts import App from ./App.vue; import { createApp } from vue; import * as ElementPlusIconsVue from element-plus/icons-vueconst app createApp(App);// 全局挂载和注册 element-plus 的所有 icon app.con…

Android平台下奔溃Crash和无响应ANR日志抓取分析

一、使用AndroidStudio 在logcat中查看实时日志&#xff0c;需要选择连接的手机和应用包名 AS下载链接 二、使用adb shell dumpsys dropbox命令获取 #!/bin/bash # path"/data/system/dropbox" # 在手机这个目录下存储了崩溃日志 newest_time$(adb shell dumps…

JK405R-SOP16录音芯片ic方案的测试板使用说明以及咪头如何选择

目录 2.1芯片硬件说明 注意事项 1、供电最高5.2V&#xff0c;最低2.6V。芯片的11脚是唯一的电源输入&#xff0c;供电电压越低扬声器声音越小 2、注意音频地和数字地要在供电的入口处短接 3、USB引脚一定要留出测试&#xff0c;也可以升级固件 4、10脚为芯片输出的3.3V&am…

旭雅阁私人博物馆 钱币博物馆

一、旭雅阁博物馆简介 旭雅阁博物馆&#xff0c;成立于2018年&#xff0c;是一家以古钱币、老钱币、字画等为主的专题博物馆&#xff0c;主要从事钱币的收藏、研究和展示&#xff0c;肩负有指导和推动钱币收藏、研究及宣传钱币文化的任务。旭雅阁博物馆&#xff0c;坐落于湖南…

个人博客网站一揽子:Docker建站(Nginx、Wordpress、MySql)

前言 既然安装了Docker&#xff0c;那就不妨建立一个自己的博客网站。实现内外网隔离网站部署&#xff0c;更安全。 1.创建Docker子网络 首先创建一个Docker虚拟子网&#xff1a; sudo docker network create wpnt检查是否建立成功&#xff1a; sudo docker network ls最后…

PyTorch实战:实现MNIST手写数字识别

前言 PyTorch可以说是三大主流框架中最适合初学者学习的了&#xff0c;相较于其他主流框架&#xff0c;PyTorch的简单易用性使其成为初学者们的首选。这样我想要强调的一点是&#xff0c;框架可以类比为编程语言&#xff0c;仅为我们实现项目效果的工具&#xff0c;也就是我们…

AI绘图提示词Stable Diffusion Prompt 笔记

基础 提示词分为正向提示词&#xff08;positive prompt&#xff09;和反向提示词&#xff08;negative prompt&#xff09;&#xff0c;用来告诉AI哪些需要&#xff0c;哪些不需要词缀的权重默认值都是1&#xff0c;从左到右依次减弱&#xff0c;权重会影响画面生成结果。AI …

登录认证方式汇总,例如ThreadLocal+拦截器+Redis、JWT

登录方式汇总 先讲讲传统的登录方式 1.Cookie方案 用cookie作为媒介存放用户凭证。 用户登录系统之后&#xff0c;会返回一个加密的cookie&#xff0c;当用户访问子应用的时候会带上这个cookie&#xff0c;授权以解密cookie并进行校验&#xff0c;校验通过后即可登录当前用…

基于Java的高校竞赛管理系统设计与实现(亮点:发起比赛、报名、审核、评委打分、获奖排名,可随意更换主题如蓝桥杯、ACM、王者荣耀、吃鸡等竞赛)

高校竞赛管理系统 一、前言二、我的优势2.1 自己的网站2.2 自己的小程序&#xff08;小蔡coding&#xff09;2.3 有保障的售后2.4 福利 三、开发环境与技术3.1 MySQL数据库3.2 Vue前端技术3.3 Spring Boot框架3.4 微信小程序 四、功能设计4.1 主要功能描述4.2 系统角色 五、系统…

linux常用命令(1):zip/unzip命令(压缩文件/解压缩文件)

文章目录 前言一、linux安装zip文件二、zip语法2.1、常用选项实例2.1.1、-r &#xff08;压缩文件夹&#xff0c;解决80%的场景&#xff09;2.1.2、-q2.1.3、-d&#xff08;从压缩文件中删除指定文件&#xff09;2.1.4、-u&#xff08;更新文件&#xff09;2.1.5、-f2.1.6、-m2…

leetcode1797. 设计一个验证系统(java)

设计一个验证系统 题目描述哈希表题目描述 题目描述 难度 - 中等 leetcode1797. 设计一个验证系统 你需要设计一个包含验证码的验证系统。每一次验证中&#xff0c;用户会收到一个新的验证码&#xff0c;这个验证码在 currentTime 时刻之后 timeToLive 秒过期。如果验证码被更新…

XGBoost算法讲解和公式推导

本文节选自《机器学习入门基础&#xff08;微课版&#xff09;》 10.5 XGBoost 算法 XGBoost 是 2014 年 2 月由华盛顿大学的博士生陈天奇发明的基于梯度提升算法(GBDT)的机器学习算法&#xff0c;其算法不但具有优良的学习效果&#xff0c;而且训练速度高效&#xff0c;在数据…

Linux下的系统编程——线程同步(十三)

前言&#xff1a; 在多线程编程中&#xff0c;如果多个线程同时访问和修改共享资源&#xff0c;可能会产生竞争条件和数据不一致的问题。同步机制用于协调线程之间的访问和操作&#xff0c;确保数据的正确性和一致性。为了避免多个线程同时访问和操作共享资源导致的问题&#…

小程序键盘没有【小数点】输入

<input v-model"formData.number" :auto-height"true" placeholder"请输入" confirm-type"done" type"digit" maxlength"11" input"inputNumber" />number&#xff1a;数字键盘&#xff08;没有小…