基于Springboot2+Vue3的Partner交友项目(二)

本文主要是介绍基于Springboot2+Vue3的Partner交友项目(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • 登陆注册
      • 集成Redis
      • 集成sa-token
      • 问题集
      • 密码加密
      • 接口限流

登陆注册

集成Redis

之前存验证码,在后端用Map存,重启服务器就会消失,所以存在第三方Redis中

Redis学习三部曲

  1. 使用StringRedisTemplate
  2. 使用RedisTemplate
  3. 使用RedisUtils封装

集成Redis过程

用之前先启动 ->定义枚举获取email发送类型和做判断 -> Constants 做了email.code 的key

-> RedisConfig.java -> RedisUtil -> .yml文件中配置 -> pom中添加两个依赖(工厂和本身的配置)

pom.xml引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-dao-redis-jackson</artifactId><version>1.33.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>

application.yml配置:

spring:redis:# Redis数据库索引(默认为0)database: 0# Redis服务器地址host: 127.0.0.1# Redis服务器连接端口port: 6379# Redis服务器连接密码(默认为空)# password:# 连接超时时间timeout: 10slettuce:pool:# 连接池最大连接数max-active: 200# 连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms# 连接池中的最大空闲连接max-idle: 10# 连接池中的最小空闲连接min-idle: 0

Redis优化

问题一:之前的优化:第一次访问接口会很慢 原因:数据库,Tomcat等都没初始化。引入Redis后的访
问接口又变慢了,原因是因为从redis拿数据,需要连接。

优化一:初始化redis连接,对Redis预先查询(和数据库初始化一样) 结果:从约1s到100ms

优化二:sendEmail方法中将某些操作异步,异步实现发邮件和写入缓存,因为用户收到验证码后在写入Redis才有意义

问题二:忘记密码时,输入数据库没有的邮箱会浪费一次发邮件的机会,且接口容易被攻击。解决:后台做验证。

问题三:虽然验证了邮箱,但请求还是发送了出去。

解决:前端将倒计时结果放在请求成功后。

集成sa-token

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证权限认证Session会话单点登录OAuth2.0微服务网关鉴权 等一系列权限相关问题。

pom.xml引入依赖:

<properties><sa-token.version>1.33.0</sa-token.version>
</properties>
<!-- redis  -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 解决LocalDateTime类型数据无法序列化 -->
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.14.0</version>
</dependency><!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>${sa-token.version}</version>
</dependency><!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-dao-redis-jackson</artifactId><version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-jwt</artifactId><version>${sa-token.version}</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>

application.yml配置:

sa-token:token-name: Authorization# token有效期,单位s 默认2小时, -1代表永不过期timeout: 7200# 是否允许同一账号并发登录is-concurrent: true# 在多人登录同一账号时,是否共用一个tokenis-share: true# token风格token-style: simple-uuid# 是否输出操作日is-log: false# token前缀  注意必须是 Bearer {token}, Bearer后面加空格token-prefix: Bearer# jwt秘钥jwt-secret-key: qwertyuiop[]\';lkjhgfdsazxcvbnm,./

代码配置 (注意:redis配置除外,单独配):

SaTokenConfigure.java

import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.stp.StpLogic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class SaTokenConfigure {// Sa-Token 整合 jwt (Simple 简单模式)@Beanpublic StpLogic getStpLogicJwt() {return new StpLogicJwtForSimple();}
}

MyWebMvcConfig.java

@Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {@Overrideprotected void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())).addPathPatterns("/**").excludePathPatterns("/", "/login", "/register", "/email", "/password/reset").excludePathPatterns("/swagger**/**", "/webjars/**", "/v3/**", "/doc.html", "");  // 排除 swagger拦截}
}

全局异常处理

@ExceptionHandler(value = NotLoginException.class)
public Result saTokenExceptionError(NotLoginException e) {return Result.error("401", e.getMessage());
}

使用

// 第1步,先登录上
StpUtil.login(dbUser.getUid());
// 设置用户对象到缓存
StpUtil.getSession().set("userInfo", dbUser);
// 第2步,获取 Token  相关参数
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
log.info("tokenInfo: {}", tokenInfo);
// 获取 token
String tokenValue = tokenInfo.getTokenValue();// 获取当前登录的用户信息
User userInfo = StpUtil.getSession().getModel("userInfo", User.class);

问题集

问题二:输入账号密码登录时也出现了权限验证。报错:未获取有效的token。

解决:因为登陆时没有返回token,二是返回接口时没加token。token是一个加密的字符串,像一个令
牌,有他就可以获取后台被保护的数据。前后端项目不能使

问题三:用内存里的token,重启后之前的token就失效了。

解决:应该用第三方存储,Redis。

问题四:待解决:登陆跳转主页,注册不跳转主页。原因: user hasUser 都没取到值 均为undefined
解决:数据的结构不一样了,包含了两层。在前端存数据时需要定义宁外的值

在这里插入图片描述

​ 上述添加setLoginInfo,问题还是没被解决。前端debugger一下,发现是后端,注册返回的接口不对还是res。逻辑上注册和登录是两个逻辑,注册后还是需要用户自己登录,这里就不实现注册后自动登录的功能了。注册也就不返 回信息了。token的发放都放在登录里面,只有通过登录才能拿到token,而注册拿不到。

问题五:

在这里插入图片描述

解决:前端格式错误

在这里插入图片描述

问题六:用户信息的创建时间和修改时间是数组格式,应该为一个时间戳

解决:redis序列化初始化问题。创建 LDTConfig.java。前端拿到时间戳可以转化为任意时间类型。代码生成器改了后重新生成,但原来的代码记得备份一份。User / UserController /… 上传到git来的,也可以点击还原,但只能还原到上一次写之前,新添的没保存的不能恢复。

在这里插入图片描述

密码加密

// 1 AES对称加密
String key = "QWER+TYU/=IO_PAL=SKDJF.GHZMXNCBV";
String ciphertext = SaSecureUtil.aesEncrypt(key, "123");
System.out.println("AES加密后:" + ciphertext);
// 解密
String text2 = SaSecureUtil.aesDecrypt(key, ciphertext);
System.out.println("AES解密后:" + text2);// 2 BCrypt加密
user.setPassword(BCrypt.hashpw(user.getPassword())); 
dbUser.setPassword(BCrypt.hashpw(newPass));
if (!BCrypt.checkpw(user.getPassword(), dbUser.getPassword())) { // 明文 数据库中的加密密码throw new ServiceException("用户名或密码错误");
}

数据库清空,ID重置从一开始

TRUNCATE sys_user;

接口限流

接口暴露后被第三方工具等恶意请求,会造成接口的线程阻塞,新的用户就没法请求。启动项目后一定记得启动redis。

过滤器实现 MyFilter.java。软件Apifox测试是否拦截成功。注意并发问题。

package com.partner.boot.common;import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;/***  限流操作** @author 小熊* @since 2023-03-07*/
@Component // 才能加入到spring的组件里才能生效
@Slf4j
public class MyFilter implements Filter {// 时间窗口// 1秒 之内只允许通过  2个  请求private static volatile long startTime = System.currentTimeMillis(); // volatile 关键字去更新内存最近的值private static final long windowTime = 1000L;private static final int door = 100;private static final AtomicInteger bear = new AtomicInteger(0); // 并发桶@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {int count = bear.incrementAndGet();  // 只要来了一个人,就+1 而不是5个线程来了都是0+1if (count == 1) {   // 并发安全的   //  3个请求计算都是2000 msstartTime = System.currentTimeMillis();}// 发生了请求long now = System.currentTimeMillis();log.info("拦截了请求, count: {}", count);//  0 -> 1  1 -> 2  2 -> 3log.info("时间窗口: {}ms, count: {}", (now - startTime), count);if (now - startTime <= windowTime) {if (count > door) {  // 超过了阈值// 就要进行限制  限流操作log.info("拦截了请求, count: {}", count);HttpServletResponse response = (HttpServletResponse) servletResponse;response.setStatus(HttpStatus.OK.value());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding(StandardCharsets.UTF_8.toString());response.getWriter().print(JSONUtil.toJsonStr(Result.error("402", "接口请求太频繁")));return;   // 超过两个请求不让访问}} else {// 重新进入下一个窗口startTime = System.currentTimeMillis();bear.set(1);}//  将请求放行HttpServletRequest request = (HttpServletRequest) servletRequest;filterChain.doFilter(servletRequest, servletResponse);log.info("接口请求的路径:{}", request.getServletPath());}@Overridepublic void destroy() {Filter.super.destroy();}}

并发请求测试类

package com.partner.boot;import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;@Slf4j
public class HttpTest {@Testpublic void test() throws InterruptedException {}public static void main(String[] args) throws InterruptedException {log.info("开始执行");String json = "{\"username\": \"xx\", \"password\": \"1234\"}";int count = 5;CountDownLatch countDownLatch = new CountDownLatch(count);List<String> list = new ArrayList<>();// 模拟并发请求for (int i = 0; i < count; i++) {int finalI = i;new Thread(() -> {String res = HttpUtil.post("http://localhost:9090/login", json);list.add(LocalDateTime.now() + "第" + finalI + "次执行: " + res);try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {throw new RuntimeException(e);}countDownLatch.countDown();}).start();}countDownLatch.await();list.forEach(System.out::println);}}

问题:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

这篇关于基于Springboot2+Vue3的Partner交友项目(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/822834

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p