Spring Cloud + Vue前后端分离-第11章 用户管理与登录

 源代码在GitHub - 629y/course: Spring Cloud + Vue前后端分离-在线课程

Spring Cloud + Vue前后端分离-第11章 用户管理与登录

11-1 增加用户管理功能

用户表设计与基本代码生成

1.用户管理与登录:用户表设计与基本代码生成

all.sql

generatorConfig.xml

ServerGenerator.java

VueGenerator.java

admin.vue

router.js

测试

增加用户名是否已存在校验

1.用户管理与登录:增加用户名是否已存在校验

2.增加自定义业务异常

登录名是不可编辑的,登录名一般会跟其它表有关联,一旦登录名改了,这些关联信息就没有了

UserService.java

根据传入登录名到数据库中查找是否有记录,有记录就说明用户名已经存在

loginName 是唯一的,所以查出来要么没有记录,要么只有一条记录

我们要的功能是校验用户名是否存在,所以也可以把返回值改成true或false,而不是返回User对象,但是这种写法不够通用,所以我们选择返回User。

BusinessException.java

抛出业务异常时,不打印堆栈信息,一方面是提高性能,另一方面是没有业务异常没必要看堆栈信息

继承RuntimeException的一个好处就是代码不需要捕获,如果是直接继承Exception,代码需要捕获,否则编译不通过。

BusinessExceptionCode.java

大家也可以自己把code加上,定义一套异常码

ControllerExceptionHandler.java

user.vue

aria-*的属性主要用于一些读屏设备,方便残障人士使用,比如盲人

测试

这里打印的异常不会打印出堆栈信息,我们之前把堆栈的打印重写了

使用方法:

1.在BusinessExceptionCode.java增加一种异常枚举;

2.在业务代码中抛出指定枚举类型的业务异常

侧边栏激活样式优化

1.用户管理与登录:侧边栏激活样式优化

当前菜单的父菜单的同级菜单,下面所有的子菜单,清空激活样式

admin.vue

可以把active清空掉

测试

随便切换都没有问题

问题:访问跟目录时,页面显示的是admin.vue,但是右边的content部分是空的,没有路由到任何一个子路由

router.js

测试

解决从登录页面跳到控台主页时,侧边栏失效的问题

1.用户管理与登录:解决从登录页面跳到控台主页时,侧边栏失效的问题

问题:从登录页面跳转到控台主页时,菜单失效了

重新刷新,才会显示

打开登录页面时,会去加载所需的js,包括ace.min.js,这里会去做很多的初始化,包括侧边栏的点击事件,但是此时还没有侧边栏

解决方法:进入控台主页时,重新加载ace.min.js

admin.vue

测试成功,登录进来就可以点击出现子模块了

小提示:大家在用很多第三方框架或jquery插件时,如果发现有些功能不起作用,如果看不懂源码,可以尝试这种方法解决,把核心的js重新加载一遍 

11-2 密码的加密传输与加密存储

加密算法MD5与盐值

网站的数据库里已经把原值和密文都算好并存储起来了,如果刚好你输入的密文在数据库里有,就能解(查)出来。

盐值也叫salt值,加上盐值后,密文不容易被破解(查询)。

密码加密传输和加密存储

1.用户管理与登录:密码加密传输和加密存储

存成明文的话,至少程序员可以直接到生产上看到所有人的密码。

从路由器的日志,我可以看到所有人浏览的网站的地址、用户名、密码,如果密码刚好是明文传输,那就泄露了,如果是简单的md5,也很容易被破解,所以需要加个盐值。

md5.js

盐值可以是随机的一串值,但是所有用到盐值的地方必须是同一个值。如果你是做平台系统,有多个客户用你的平台,最好是一个客户一个盐值。

user.vue

这里可以考虑写个通用方法,把hex_md5+盐值包装起来

保险方案:对密码做两层加密

UserController.java

测试

增加修改密码功能

1.用户管理与登录:增加重置密码功能;编辑用户信息的时候不修改密码

修改用户信息和修改密码应该分开,做成两个功能

密码发生改变

user.vue

UserService.java

UserMapper.java

 UserMapper.xml

mybatis-generator 生成的方法里,updateByPrimaryKeySelective会对字段进行非空判断,再更新,如果值为空就不更新,原理就是利用mybatis的if拼成动态sql

测试

user.vue

<div id="edit-password-modal" class="modal fade" tabindex="-1" role="dialog">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
            <h4 class="modal-title">修改密码</h4>
          </div>
          <div class="modal-body">
            <form class="form-horizontal">
              <div class="form-group">
                <label class="control-label col-lg-3">密码</label>
                <div class="col-lg-9">
                  <input class="form-control" type="password" v-model="user.password" name="password">
                </div>
              </div>
            </form>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-white btn-default btn-round" data-dismiss="modal">
              <i class="ace-icon fa fa-times"></i>
              取消
            </button>
            <button type="button" class="btn btn-white btn-info btn-round" v-on:click="savePassword()">
              <i class="ace-icon fa fa-plus blue"></i>
              保存密码
            </button>
          </div>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->

UserService.java

UserController.java

测试

11-3基本的登录功能开发

基本的登录功能开发

1.用户管理与登录:基本的登录功能开发,校验用户名密码

思考:我现在存到数据库里面的密码是密文,那用户登录的时候,我数据库里面的密码解密不出来了,我怎么知道用户输入的密码对不对?

login.vue

UserController.java

UserService.java

登录验证思考:是否是根据用户名+密码到数据中去查找记录?

用户名+密码去数据库查找的话,程序不知道是用户名不对,还是密码不对。程序应该要能知道,比如我如果发现有大量的用户名不对的报错,说明有人正在不断的探测我系统的用户名。

再次思考:如果用户名不对,提示给前端的是:用户名不存在。如果是密码不对,提示给前端的是:密码不对。是否是这样?

如果你直接告诉前端说用户名不存在,我作为一个黑客的话,可以拿着现成的一堆用户名,包括手机号邮箱,不断的探测哪些用户名是你系统里有了,不要给别人任何机会获取你系统的关键信息。

BusinessExceptionCode.java

LoginUserDto.java

package com.course.server.dto;


public class LoginUserDto {

    /**
    * id
    */
    private String id;

    /**
    * 登录名
    */
    private String loginName;

    /**
    * 昵称
    */
    private String name;
    

    public String getId() {
    return id;
    }

    public void setId(String 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;
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", id=").append(id);
        sb.append(", loginName=").append(loginName);
        sb.append(", name=").append(name);
        sb.append("]");
        return sb.toString();
    }
}

UserService.java 

UserController.java

测试

登录后保存登录信息

1.用户管理与登录:登录后前端保存登录信息并显示

前端的信息保存有多种选择:h5的localStorage, sessionStorage; js的全局变量,vue 的store等。

sessionStorage在页面刷新的时候,信息不会丢;关闭页面后,信息自动清空,适合用来存储登录信息。

localStorage在关闭页面后,登录信息还是在的,适合一些内网使用的系统保存登录信息。

用js 全局变量或vue 的store,刷新浏览器的时候,信息会丢失。

login.vue

admin.vue

把登录信息的保存和读取做成通用的方法。

tool.js

小技巧:在获取一些对象的时候,加上|| {},避免获取属性值时报错。

session-storage.js

login.vue

admin.vue

测试

11-4 退出登录与记住登录

增加退出登录功能

1.用户管理与登录:增加退出登录功能,清空前后端的会话缓存

它会动态的改变大小,适应屏幕

退出登录:清空当前登录的缓存信息,并跳到登录页面

admin.vue

一般登录信息在前后端都会保存

Constants.java

UserController.java

测试

增加记住登录信息功能

1.用户管理与登录:增加记住登录信息功能

使用localStorage来保存输入的用户名密码

login.vue

能获取到缓存的值,说明上一次有勾选“记住我” 

如果不清空本地缓存,重新打开页面时,会再次显示记住的用户名密码

测试之前要把浏览器自带的记住用户名密码清除

安全加固,本地缓存保存密码密文

1.用户管理与登录:增加记住登录信息功能,安全加固,本地缓存保存密码密文

local-storage.js

login.vue

测试

从刚才的记住我这个功能,大家可以看出来,一个是记住明文,一个是记住密文,虽然实现的功能是一样的,但是安全性上不一样,所以我们写程序不只是把功能写出来,还要严谨,不要留下坑

11-5 增加登录图形验证码

集成图形验证码kaptcha

1.用户管理与登录:集成图形验证码kaptcha

pom.xml(course)

pom.xml(server)

KaptchaConfig.java

package com.course.server.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.util.Config;

import java.util.Properties;

@Configuration
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha getDefaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "no");
//        properties.setProperty("kaptcha.border.color", "105,179,90");
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        properties.setProperty("kaptcha.image.width", "90");
        properties.setProperty("kaptcha.image.height", "32");
        properties.setProperty("kaptcha.textproducer.font.size", "24");
        properties.setProperty("kaptcha.session.key", "code");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.textproducer.font.names", "Arial");
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    //如果项目中有多个页面会用到验证码图片,且图片的大小,颜色等都不一样,就可以增加多个生成验证码图片方法
    @Bean
    public DefaultKaptcha getWebKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "no");
//        properties.setProperty("kaptcha.border.color", "105,179,90");
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        properties.setProperty("kaptcha.image.width", "90");
        properties.setProperty("kaptcha.image.height", "45");
        properties.setProperty("kaptcha.textproducer.font.size", "30");
        properties.setProperty("kaptcha.session.key", "code");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.textproducer.font.names", "Arial");
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

如果项目中有多个页面会用到验证码图片,且图片的大小,颜色等都不一样,就可以增加多个生成验证码图片方法

KaptchaController.java

package com.course.system.controller.admin;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;

@RestController
@RequestMapping("/admin/kaptcha")
public class KaptchaController {

    @Qualifier("getDefaultKaptcha")
    @Autowired
    DefaultKaptcha defaultKaptcha;

    @GetMapping("/image-code/{imageCodeToken}")
    public void imageCode(@PathVariable(value = "imageCodeToken") String imageCodeToken,
    HttpServletRequest request, HttpServletResponse httpServletResponse) throws Exception{
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        try {
            // 生成验证码字符串
            String createText = defaultKaptcha.createText();

            // 将生成的验证码放入会话缓存中,后续验证的时候用到
            request.getSession().setAttribute(imageCodeToken, createText);

            // 使用验证码字符串生成验证码图片
            BufferedImage challenge = defaultKaptcha.createImage(createText);
            ImageIO.write(challenge, "jpg", jpegOutputStream);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
        byte[] captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaChallengeAsJpeg);
        responseOutputStream.flush();
        responseOutputStream.close();
    }
}

测试http://localhost:9000/system/admin/kaptcha/image-code/123

alt+enter,将字符加到字典里,就不会有波浪线警告了 

页面显示验证码及刷新验证码

1.用户管理与登录:页面显示验证码及刷新验证码

login.vue

tool.js

radix 默认63
10个数字+26个大小字母+26个小写字母,共62个字符,可以用来表示62进制的数值,也可以加入一些特殊字符,组成更大进制的数。
原理:以62进制为例,随机生成一个0~61的数值,比如41,那边就取chars数组中的第41个字符,这样重复做8遍,就生成了8位的62进制数,重复的概览是62的8次方。也可以生成更长的数值。

登录增加验证码校验

1.用户管理与登录:登录增加验证码校验

2.解决每次ajax请求,对应的sessionId不一致的问题

UserDto.java

增加属性后,记得alt+insert生成get, set, toString()方法。比如lombok,代码侵入性太强,如果我用了插件,那大家都得安装这个插件,否则会报错。

UserController.java

在登录里面,增加验证码校验:通过token去缓存中获取验证码字符串,并和用户输入的字符串做比较。

main.js

登录验证出错时,将密码清空,同时刷新验证码图片 

login.vue

 

 

测试

刷新验证码会让网站更安全,但是会牺牲一点用户体验,需要折中选择

测试一种场景:刷新过的验证码,还能不能用。

正规的项目中,都会有专门的测试团队来编写测试用例。如果是个人开发,只能自己设计测试用例,要尽可能的覆盖各种使用场景

11-6 单点登录功能开发

单点登录解决方案介绍

生产发布时,至少是双节点,防止单台宕机。

不管是在哪一台做的登录,登录完成后,会把登录信息保存到redis中。当业务请求进来时,再到redis中获取登录信息,能获取到就表示已登录,未获取到就表示未登录,拦截掉请求。

功能:只要在其中一个产品中登录过,其他关联的产品都不需要再登录。

token:登录标识,每个用户每次登录,都会生成不同的token。

单点登录(Single Sign On),简称为SSO,核心功能:session共享。

需要解决Session共享的场景:

1.同个应用多节点共享登录信息;

2.多个项目间共享登录信息。

一般我们通常说的单点登录系统,是用来解决场景2的。

集成redis

1.用户管理与登录:集成redis,图片验证码的存储从session改为redis

pom.xml(server)

application.properties

KaptchaController.java

UserController.java

回归验证码功能,更换缓存不要对原有流程产生影响

测试

生成登录token并存储到redis中

1.用户管理与登录:生成登录token并存储到redis中,退出登录时删除token

LoginUserDto.java

UserController.java

这里也可以直接保存loginUserDto对象,但是需要序列化。如果是跨应用使用的,比如A应用存,B应用取,一般会把值转成JSON字符串

admin.vue

KaptchaController.java

测试

11-7 前后端登录拦截

基于Vue路由的登录拦截

1.用户管理与登录:基于Vue路由的登录拦截

此时,直接访问user页面,是可以直接跳过身份验证,这是绝对不允许的

router.js

只需要在父路由增加拦截就可以了,子路由就会都有这个拦截,不需要子路由再一个一个添加 

main.js

测试

但是现在还有一个问题:界面拦住了,但是所有的接口都可以直接访问,非常危险,这个就是基于后端的请求拦截

在请求headers 中统一增加token

1.用户管理与登录:在请求headers 中统一增加token

main.js

可以用这种方法给所有请求加了统一的系统参数,比如在header里加上请求流水,请求时间等。

测试

gateway增加登录拦截

1.用户管理与登录:在gateway中增加登录拦截

application.properties

LoginAdminGatewayFilter.java

LoginAdminGatewayFilterFactory.java

测试

不是所有的请求都需要做登录拦截,比如登录接口、验证码图片接口

gateway实现控台登录拦截功能

1.用户管理与登录:gateway实现控台登录拦截功能

pom.xml(course)

pom.xml(gateway)

gateway模块并没有依赖server模块,所以有些jar包如redis , json等,需要单独在pom.xml中增加依赖

application.properties

LoginAdminGatewayFilter.java

package com.course.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;

@Component
public class LoginAdminGatewayFilter implements GatewayFilter, Ordered {
    private static final Logger LOG = LoggerFactory.getLogger(LoginAdminGatewayFilter.class);

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();

        //请求地址中不包含/admin/的,不是控台请求,不需要拦截
        if (!path.contains("/admin/")){
            return chain.filter(exchange);
        }
        if (path.contains("/system/admin/user/login")
         || path.contains("/system/admin/user/logout")
         || path.contains("/system/admin/kaptcha")){
            LOG.info("不需要控台登录验证:{}",path);
            return chain.filter(exchange);
        }

        //获取header的token参数
        String token = exchange.getRequest().getHeaders().getFirst("token");
        LOG.info("控台登录验证开始,token:{}",token);
        if (token == null || token.isEmpty()){
            LOG.info("token为空,请求被拦截");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        Object object = redisTemplate.opsForValue().get(token);
        if (object == null){
            LOG.warn("token无效,请求被拦截");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }else {
            LOG.info("已登录:{}",object);
            return chain.filter(exchange);
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

http协议中有一些约定好的状态码,比如401未授权,404未找到,200正常处理等。

HttpStatus.java

测试

 直接访问这个地址,是会报错的

11-8 用户登录流程图

白底是前端部分,蓝底是后端部分

验证码流程

开始->生成验证码token唯一标识->调用服务端图形验证码接口->生成验证码字符串->以token为key将字符串放入redis中,设置时效->返回验证码图片->结束

登录流程

开始->输入用户名、密码、验证码->前端密码加密(防止数据传输时泄漏)->组装登录参数包含验证码token->调用登录接口->密码加密->根据token到redis获取正确的验证码->

验证码正确?(这里的验证码错误分为两种,验证码已过期和验证码不正确)

1.->清除redis验证码根据用户名查询->用户存在?

        -是>比较密码正确?

                -是>生成登录token->以token为key将登录信息放入缓存中->返回用户信息-----success=true----->

                -否>打印日志:密码错->返回用户名不存在或密码错误----- success=false----->success=true----->

        -否>打印日志:用户名不存在->返回用户名不存在或密码错误

2.->打印日志并返回验证码错误----- success=false----->success=true----->

success=true?

-是>保存用户信息到h5 session缓存->勾选记住我

        -是>保存用户名密码到h5 local缓存中->结束

        -否>清空h5 local缓存中的用户密码->结束

-否>弹出登录失败提示框->结束

                                                                                                                                     |

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络编程套接字(Socket)

文章目录 1 重点知识2 预备知识2.1 理解源IP地址和目的IP地址2.2 认识端口号2.3 理解 "端口号" 和 "进程ID"2.4 理解源端口号和目的端口号2.5 认识TCP协议2.6 认识UDP协议2.7 网络字节序 3 socket编程接口3.1 socket 常见API3.2 sockaddr结构 4 简单的UDP网…

安卓(雷电)模拟器清除屏幕密码

1、设置磁盘可写 启动模拟器&#xff0c;然后在模拟器的设置界面&#xff0c;设置磁盘共享为可写入&#xff0c;重启模拟器&#xff0c;如下图&#xff1a; 2、找到模拟器目录 返回桌面&#xff0c;右键模拟器图标&#xff0c;打开文件所在目录&#xff0c;如下图&#xff1a…

应用在植物生长照明中的LED照明灯珠

植物照明是指利用LED植物照明灯来促进植物生长。植物照明一般采用LED植物生长灯&#xff0c;是一种以LED&#xff08;发光二极管&#xff09;为发光体&#xff0c;满足植物光合作用所需光照条件的人造光源。LED植物生长灯对植物的生长有很大的好处&#xff0c;能促进壮根、助长…

IPV6学习记录

IPV6的意义 从广义上来看IPV6协议包含的内容很多: IPV6地址的生成与分配 IPV6的报头的功能内容 IPV4网络兼容IPV6的方案 ICMPv6的功能(融合了arp和IGMP功能) IPV6的路由方式 ipv6的诞生除了由于ipv4的地址枯竭外&#xff0c;很大程度上也是因为ipv4多年的发展产生了很多…

Redis 内存淘汰策略有哪些?过期数据如何删除?

Redis 在面试中出现的概率非常大&#xff0c;毕竟后端项目如果用到分布式缓存的话&#xff0c;一般用的都是 Redis。目前&#xff0c;还没有出现一个能够取代 Redis 的分布式缓存解决方案。 这篇文章中&#xff0c;我会分享几道 Redis 内存管理相关的问题&#xff0c;都很常见…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷⑤

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷5 目录 需要竞赛软件包环境以及备赛资源可私信博主&#xff01;&#xff01;&#xff01; 2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷5 模块一 …

【中国联通协办】第六届下一代数据驱动网络国际学术会议(NGDN 2024)

第六届下一代数据驱动网络国际学术会议&#xff08;NGDN 2024&#xff09; The Sixth International Conference on Next Generation Data-driven Networks 基于前几届在英国埃克塞特 (ISPA 2020) 、中国沈阳 (TrustCom 2021) 和中国武汉(IEEETrustCom-2022)成功举办的经验&a…

TF-IDF(Term Frequency-Inverse Document Frequency)算法详解

目录 概述 术语解释 词频&#xff08;Term Frequency&#xff09; 文档频率&#xff08;Document Frequency&#xff09; 倒排文档频率&#xff08;Inverse Document Frequency&#xff09; 计算&#xff08;Computation&#xff09; 代码语法 代码展示 安装相关包 测…

ChatGPT知名开源项目有哪些

ChatGPT-Next-Web&#xff1a;基于ChatGPT API的私有化部署网页聊天系统 主要功能&#xff1a; 只需在 1 分钟内即可在 Vercel 上一键免费部署&#xff0c;支持私有服务器快速部署&#xff0c;支持使用私有域名支持ChatGPT3.5、4等常见模型Linux/Windows/MacOS 上的紧凑型客户…

vulhub中的Apache HTTPD 多后缀解析漏洞详解

Apache HTTPD 多后缀解析漏洞 1.查看python版本 这里python版本很重要&#xff0c;因为版本过低可能会导致后面的结果运行不成功 这里我就遇到了因为版本过低而执行不了docker-compose up -d的情况 查看python版本 cd /usr/bin ls -al python* 当版本过低时安装高版本的 …

uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -创建图文投票实现

锋哥原创的uniapp微信小程序投票系统实战&#xff1a; uniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibiliuniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )共计21条视频…

论文封面表格制作

原文参考&#xff1a;【【论文排版】论文封面完美对齐 强迫症重度患者的经验分享】https://www.bilibili.com/video/BV18f4y1p7hc?vd_source046491acdcff4b39fed20406b36a93e2 视频里up主介绍很详细。我自己也记录一下。 介绍一下如何完成论文封面信息的填写。 创建一个3列…

C++并发编程实战第2版笔记

文章目录 p19 某个线程只可以join()一次p22 只有当joinable()返回true时才能调用detach()P21 在std::thread对象析构前&#xff0c;必须明确是等待还是分离线程P25 移动语义P25 将类的成员函数设定为线程函数 p19 某个线程只可以join()一次 只要调用了join()&#xff0c;隶属于…

Vue-11、Vue计算属性

Vue计算属性是Vue实例的属性&#xff0c;用来根据已有的数据进行计算得到新的数据。计算属性的值会根据它的依赖缓存起来&#xff0c;在依赖没有发生改变时直接返回缓存的值&#xff0c;提高了性能。 计算属性的定义方式为在Vue实例中使用computed关键字&#xff0c;并将计算属…

次氯酸HClO荧光探针的结构特点-星戈瑞单品

次氯酸荧光探针是一种用于检测次氯酸盐&#xff08;ClO⁻&#xff09;存在的化合物&#xff0c;通常被设计为荧光分子&#xff0c;其荧光性质在与次氯酸反应时发生变化。这类荧光探针的设计通常考虑到以下结构特点&#xff1a; **1.含有感应基团&#xff1a;**探针分子通常包含…

推荐优秀的大学数学课程

今天在B站看 R-S积分 发现这个老师讲的不错&#xff1a;Riemann-Stieltjes Integrals_哔哩哔哩_bilibili 可以用优秀来说&#xff0c;板书也不错&#xff01;授课老师&#xff1a;吴庆堂老师&#xff08;国立交通大学&#xff0c;目前台湾阳明大学和台湾交通大学合并而成的台湾…

哔哩哔哩浏览器 AI 助手:bilibili subtitle

分享一个好用不火的浏览器插件&#xff0c;能够让我们在浏览 B 站视频的时候体验更棒。 写在前面 B 站视频时间越来越长的今天&#xff0c;在打开视频的时候&#xff0c;如果能够清晰直观的看到视频字幕&#xff0c;当我们点击带有时间轴的字幕就能够一键跳转到自己想看的视频…

轻松get压力测试指南

身为后端程序员怎么也要会一点压力测试相关的技术吧, 不然无脑上线项目万一项目火了进来大量请求时出现程序执行缓慢, 宕机等情况你肯定稳稳背锅, 而且这个时候短时间内还没办法解决, 只能使用物理扩容CPU, 内存, 更换网络等几种方式来解决问题, 妥妥的为公司增加支出好吧, 下一…

中年危机与自我成长职业女性的心理转型之路

中年危机与自我成长&#xff1a;职业女性的心理转型之路 案例&#xff1a;李女士&#xff0c;45岁&#xff0c;职业女性 李女士是一位职业女性&#xff0c;人到中年&#xff0c;她突然感到自己的生活仿佛失去了方向。她来找我咨询&#xff0c;希望能够找到一条心理转型的道路&a…

第87讲:XtraBackup备份工具的核心技术要点及全库备份、恢复案例

文章目录 1.XtraBackup备份工具的简介2.XBK备份工具的安装3.XBK备份工具的使用语法4.XBK备份前的准备5.使用XBK对全库进行备份5.1.XBK备份全库数据的语法格式5.2.使用XBK进行全库备份5.3.查看XBK备份的数据文件5.4.备份过程中生产的XBK文件 6.模拟故障案例并使用XBK恢复备份的数…