SpringBootWeb登录认证

JWT令牌

JSON Web Token JSON Web Tokens - jwt.ioJSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).icon-default.png?t=N7T8https://jwt.io/JSON Web Token(JWT)是一种基于JSON的开放标准(RFC 7519),用于在各方之间安全地传输信息。以下是对JWT进行详细介绍:

  • JWT的工作原理:JWT的工作原理基于令牌(Token)的生成和验证两个主要过程。一旦用户通过身份验证,服务器会生成一个包含用户信息的JWT。这个令牌由三部分组成:标头(Header)、有效载荷(Payload)以及签名(Signature)。标头通常包含令牌的类型和所使用的签名算法;有效载荷则包含了实际的数据声明,如发行人、到期时间、主题、用户等信息;签名是为了确保令牌在传输过程中不被篡改。
  • JWT的优点:JWT提供了无状态、可扩展且安全的认证解决方案,特别适用于分布式环境和移动应用。由于JWT自包含所有用户信息,服务端不需要存储session信息,从而减轻了服务器的压力。同时,JWT可以跨域使用,支持单点登录(SSO),并且不依赖于Cookie,因此也避免了CSRF攻击的风险。
  • JWT的缺点:尽管JWT具有许多优点,但它也存在一些缺点。例如,一旦JWT被窃取,攻击者就可以访问所有的用户资源,直到令牌过期。此外,如果JWT未加密,通过某些手段可能会被解码并修改其内容。

在考虑使用JWT时,还需要注意以下几个方面:

  • 安全性:虽然JWT可以加密并签名以保证数据的安全性和完整性,但默认情况下JWT是不加密的。因此,在使用JWT传输敏感信息时,应采取额外的安全措施,如使用HTTPS协议进行传输。
  • 有效期:为了减少被盗用的风险,JWT的有效期不宜设置过长。对于一些重要操作,应要求用户频繁进行身份验证。
  • 存储:虽然JWT通常存储在客户端,但也可以选择存储在服务器端,这取决于特定的应用场景和安全需求。

综上所述,JSON Web Token(JWT)是一个功能强大的工具,它提供了一种轻量级的解决方案,用于在现代Web应用程序中安全地处理身份验证和信息交换。通过了解JWT的工作原理、优缺点以及正确的使用方法,开发者可以有效地利用这一技术来提高应用的安全性和用户体验。

 依赖配置

pom.xml引入JWT依赖

<!-- JWT依赖-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

生成JWT令牌

public void genJwt(){Map<String,Object> claims = new HashMap<>();claims.put("id",1);claims.put("username","Tom");String jwt = Jwts.builder().setClaims(claims) //自定义内容(载荷)          .signWith(SignatureAlgorithm.HS256, "密钥") //签名算法        .setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期   .compact();System.out.println(jwt);
}

 输出的结果就是生成的JWT令牌,,通过英文的点分割对三个部分进行分割。可以打开JWT官网进行校验。

校验JWT令牌

public void parseJwt(){Claims claims = Jwts.parser().setSigningKey("密钥")//指定签名密钥.parseClaimsJws("JWT令牌内容").getBody();System.out.println(claims);
}

将JWT令牌封装成工具类

public class JwtUtils {private static String signKey = "wfit";private static Long expire = 43200000L;/*** 生成JWT令牌* @param claims JWT第二部分负载 payload 中存储的内容* @return*/public static String generateJwt(Map<String, Object> claims){String jwt = Jwts.builder()//添加内容荷载.addClaims(claims)//签名算法.signWith(SignatureAlgorithm.HS256, signKey)//令牌时间.setExpiration(new Date(System.currentTimeMillis() + expire))//转化字符串.compact();return jwt;}/*** 解析JWT令牌* @param jwt JWT令牌* @return JWT第二部分负载 payload 中存储的内容*/public static Claims parseJWT(String jwt){Claims claims = Jwts.parser()//签名密钥.setSigningKey(signKey)//JWT令牌.parseClaimsJws(jwt).getBody();return claims;}
}

过滤器 Filter

过滤器(Filter)是Java Web应用中的一个重要组件,它主要用于请求到达Servlet之前或响应返回客户端之前对请求和响应进行处理。具体分析如下:

  1. 基本介绍:过滤器可以对进入的应用请求进行预处理,也可以对出去的响应进行后处理。这种机制使得开发人员可以在请求到达目标之前,以及响应被发送给客户端之前,执行一些特定的操作。例如,可以用于实现权限验证、请求内容的转换、日志记录等。
  2. 过滤器原理:过滤器的工作原理基于一种链式结构,即过滤器链。当一个请求到达时,它会按照配置的顺序通过每个过滤器。每个过滤器都可以修改请求或响应,或者决定是否将请求/响应传递到链中的下一个过滤器或目标资源。
  3. 过滤器接口:在Java中,创建过滤器需要实现javax.servlet.Filter接口,该接口定义了init()doFilter(), 和 destroy()三个方法。init()方法在过滤器初始化时调用,doFilter()方法用于实际的过滤操作,而destroy()方法在过滤器销毁前调用。
  4. 使用过滤器:要使用过滤器,首先需要创建一个实现了Filter接口的Java类,然后通过注解@WebFilter指定过滤规则,或者在web.xml文件中配置过滤器及其拦截路径。过滤器的具体逻辑实现在doFilter()方法中完成。
  5. 配置过滤器:过滤器的配置可以通过两种方式完成:一是使用@WebFilter注解直接在过滤器类上指定拦截路径;二是在项目的web.xml文件中配置过滤器及其拦截的URL模式。这两种方式都允许灵活地控制哪些请求应该被拦截和处理。
  6. 过滤器生命周期:过滤器的生命周期由Web容器管理。当Web应用启动时,过滤器被初始化;接收到请求时,执行doFilter()方法;在Web应用关闭时,执行destroy()方法以释放资源。
  7. 多个Filter的执行顺序:当使用多个过滤器时,它们的执行顺序非常重要。这个顺序可以通过在web.xml中配置的顺序或使用@WebFilter注解指定的顺序参数来确定。正确的执行顺序确保了过滤器能够按照预期的方式工作。

此外,在开发过程中,需要注意以下几点:

  • 正确配置:确保过滤器及其拦截路径正确配置,避免拦截不应该被拦截的请求。
  • 性能考虑:虽然过滤器提供了强大的功能,但不当使用可能会影响应用性能。应尽量减少过滤器的数量,并优化过滤逻辑。
  • 线程安全问题:由于过滤器对象可能在多线程环境下被并发访问,因此需要确保过滤器的实现是线程安全的。

总的来说,过滤器是Java Web开发中一个非常强大且灵活的工具,能够帮助开发者实现许多高级功能,提高代码的模块化和重用性。通过合理使用过滤器,可以极大地提升Web应用的安全性、效率和可维护性。

启动类注解

@ServletComponentScan//识别service
@SpringBootApplication
public class WebManagementApplication {public static void main(String[] args) {SpringApplication.run(TliasWebManagementApplication.class, args);}}

只有添加了@ServletComponentScan,Spring才能正常使用Filter。

设计拦截器实现Filter接口,重新其中方法即可。

执行流程

@WebFilter(urlPatterns = "/*") 
public class DemoFilter implements Filter {@Override //初始化方法, 只调用一次public void init(FilterConfig filterConfig) throws ServletException {System.out.println("init 初始化方法执行了");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("DemoFilter   放行前逻辑.....");//放行请求filterChain.doFilter(servletRequest,servletResponse);System.out.println("DemoFilter   放行后逻辑.....");}@Override //销毁方法, 只调用一次public void destroy() {System.out.println("destroy 销毁方法执行了");}
}

 拦截路径参数

拦截路径urlPatterns值含义
拦截具体路径/login只有访问 /login 路径时,才会被拦截
目录拦截/emps/*访问/emps下的所有资源,都会被拦截
拦截所有/*访问所有资源,都会被拦截

 使用拦截器和JWT令牌实现登录认证案例

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//类型请求对象HttpServletRequest ser= (HttpServletRequest) servletRequest;HttpServletResponse serp= (HttpServletResponse) servletResponse;//获取请求URLString url = ser.getRequestURL().toString();log.info("url:{}",url);//判断url是否包含login关键字,如果有就放行if (url.contains("login")){log.info("登录操作,放行...");filterChain.doFilter(servletRequest,servletResponse);return;}//获取请求头中的令牌String jwt = ser.getHeader("token");//判断令牌是否存在//否if (!StringUtils.hasLength(jwt)){log.info("token为空,未登录");Result error = Result.error("NOT_LOGIN");//手动将对象转换为JSON --->阿里巴巴fastJSONString jsonString = JSONObject.toJSONString(error);//将JSON返回给浏览器serp.getWriter().write(jsonString);return;}//是try {JwtUtils.parseJWT(jwt);} catch (Exception e) {e.printStackTrace();log.info("令牌解析失败");Result error = Result.error("NOT_LOGIN");//手动将对象转换为JSON --->阿里巴巴fastJSONString jsonString = JSONObject.toJSONString(error);//将JSON返回给浏览器serp.getWriter().write(jsonString);return;}//放行log.info("令牌合法,放行");filterChain.doFilter(servletRequest,servletResponse);}}

拦截器Interceptor

 

拦截器(Interceptor)是一种用于在控制器处理请求之前或之后执行某些操作的机制。以下将深入介绍拦截器的相关信息:

拦截器的基本概念

  • 拦截器(Interceptor)在Spring框架中,主要用于对Controller层的处理方法进行拦截,即可以在方法执行前后加入自定义的操作。拦截器与过滤器(Filter)相比,过滤器更广泛地用于请求和响应的预处理和后处理,而拦截器则更加关注于具体的处理器(Controller)方法。

拦截器的实现原理

  • 拦截器通常通过代理方式或反射机制实现。在Spring MVC应用中,拦截器通过动态代理来实现对Controller方法的增强。当一个HTTP请求到达时,如果配置了拦截器,那么这个请求在被目标Controller处理之前,会先经过一系列拦截器的处理。

拦截器的主要作用

  • 日志记录:记录请求信息,包括请求的URL、IP地址、时间等,便于监控和日志分析。
  • 权限检查:例如检查用户是否已登录,是否有权访问某个特定的资源或服务。
  • 性能监控:计算请求的处理时间,帮助开发者优化应用性能。
  • 通用行为增强:如提取用户信息放入请求中,方便后续流程使用;或者根据不同的用户设置不同的主题和语言。

拦截器的自定义实现

  • 要创建自定义的拦截器,需要实现HandlerInterceptor接口或继承HandlerInterceptorAdapter类,并重写preHandle()postHandle(), 和 afterCompletion()三个方法。这些方法分别在请求处理前、视图渲染前、以及整个请求结束后被调用。

拦截器的使用场景举例

  • 在电商平台中,可以使用拦截器来检查用户是否登录,如果没有登录则重定向到登录页面。
  • 对于需要计费的API,可以利用拦截器来计算调用次数和时长,从而实现计费功能。
  • 在内容管理系统中,使用拦截器来限制只有特定角色的用户才能访问某些管理功能。

此外,考虑到拦截器的强大功能及其灵活性,开发者在使用时应注意以下几点:

  • 正确配置:确保拦截器及其拦截规则正确配置,避免影响正常的业务流程。
  • 性能考虑:虽然拦截器提供了强大的功能,但不当使用可能会影响应用性能。应尽量减少拦截器的数量,并优化其逻辑。
  • 线程安全问题:由于拦截器对象可能在多线程环境下被并发访问,因此需要确保拦截器的实现是线程安全的。

综上所述,拦截器在Spring MVC应用中扮演着至关重要的角色,它不仅能够增强方法处理的能力,还能提高代码的模块化和重用性。通过合理使用拦截器,可以极大地提升Web应用的安全性、效率和可维护性。

自定义拦截器  

需要实现HandlerInterceptor接口,并重写其所有方法

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {//目标资源方法执行前执行。 返回true:放行    返回false:不放行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle .... ");return true; //true表示放行}//目标资源方法执行后执行@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle ... ");}//视图渲染完毕后执行,最后执行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion .... ");}
}

 注意:

preHandle方法:目标资源方法执行前执行。 返回true:放行 返回false:不放行

postHandle方法:目标资源方法执行后执行

afterCompletion方法:视图渲染完毕后执行,最后执行

注册配置拦截器  

@Configuration  
public class WebConfig implements WebMvcConfigurer {//自定义的拦截器对象@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)}
}

拦截路径

拦截路径含义举例
/*一级路径能匹配/depts,/emps,/login,不能匹配 /depts/1
/**任意级路径能匹配/depts,/depts/1,/depts/1/2
/depts/*/depts下的一级路径能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/**/depts下的任意级路径能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1
@Configuration  
public class WebConfig implements WebMvcConfigurer {//拦截器对象@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**")//设置拦截器拦截的请求路径( /** 表示拦截所有请求).excludePathPatterns("/login");//设置不拦截的请求路径}
}

 使用过滤器和JWT令牌实现登录认证案例

注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**")//拦截什么样的路径.excludePathPatterns("/login");//不拦截什么路径}
}

定义拦截器

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {@Override //目标资源方法运行前运行,true放行public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String url = request.getRequestURL().toString();log.info("拦截路径:{}",url);String token = request.getHeader("token");if (!StringUtils.hasLength(token)){log.info("token为空");Result error = Result.error("NOT_LOGIN");String s = JSONObject.toJSONString(error);response.getWriter().write(s);return false;}try {JwtUtils.parseJWT(token);} catch (Exception e) {log.info("令牌有误");response.getWriter().write(JSONObject.toJSONString(Result.success("NOT_LOGIN")));return false;}log.info("令牌正确,执行登录");return true;}@Override //目标资源方法运行后运行public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Override //视图渲染完毕后执行public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

扩展——执行流程

  • 当我们打开浏览器来访问部署在web服务器当中的web应用时,此时我们所定义的过滤器会拦截到这次请求。拦截到这次请求之后,它会先执行放行前的逻辑,然后再执行放行操作。而由于我们当前是基于springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我们所定义的controller当中的接口方法。

  • Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环境中提供了一个非常核心的Servlet:DispatcherServlet(前端控制器),所有请求都会先进行到DispatcherServlet,再将请求转给Controller。

  • 当我们定义了拦截器后,会在执行Controller的方法之前,请求被拦截器拦截住。执行preHandle()方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller中的方法也不会执行)。

  • 在controller当中的方法执行完毕之后,再回过来执行postHandle()这个方法以及afterCompletion() 方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放行后的这一部分逻辑的逻辑。执行完毕之后,最终给浏览器响应数据。

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

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

相关文章

文件系统和日志分析

文件系统 概述 文件是存储在硬盘上的。硬盘上的最小存储单位是扇区&#xff0c;每个扇区的大小是512字节。 inode号&#xff1a;又叫索引号&#xff0c;保存的是元信息&#xff08;主要有文件的属性 &#xff1a;包括权限&#xff0c;创建者&#xff0c;创建日期等&#xff…

Renesas MCU之使用e² studio搭建开发环境

目录 概述 1 e studio介绍 2 搭建Renesas MUC开发环境 2.1 软件版本信息 2.2 安装软件 3 创建工程 3.1 板卡硬件接口 3.2 FSP配置IO 4 Generate Project 4.1 项目目录介绍 4.2 LED接口相关驱动 5 调试 5.1 测试代码 5.2 J-Link调试代码 5.3 硬件结构 概述 本文主…

ES6-01-简介

一、什么是ES6&#xff1f; 每年一个版本o(╥﹏╥)o。 二、javaScript新特性的特点 1、语法简洁&#xff0c;功能丰富&#xff1b; 2、框架开发应用。 3、岗位需求&#xff01; 三、let关键字 3-1、声明变量 let a;let a,b;let e100;let f521, gmilk-love, h[]; 3-2、声明的…

python采集汽车价格数据

python采集汽车价格数据 一、项目简介二、完整代码一、项目简介 本次数据采集的目标是车主之家汽车价格数据,采集的流程包括寻找数据接口、发送请求获取响应、解析数据和持久化存储,先来看一下数据情况,完整代码附后: 二、完整代码 #输入请求页面url #返回html文档 imp…

属性(property)

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 1 创建用于计算的属性 在Python中&#xff0c;可以通过property&#xff08;装饰器&#xff09;将一个方法转换为属性&#xff0c;从而实现用于计算…

几种更新 npm 项目依赖的实用方法

引言 在软件开发的过程中&#xff0c;我们知道依赖管理是其中一个至关重要的环节。npm&#xff08;Node Package Manager&#xff09; 是 Node.js 的包管理器&#xff0c;它主要用于 Node.js 项目的依赖管理和包发布。随着项目的不断发展&#xff0c;依赖库的版本更新和升级成…

基于Win11下的Wireshark的安装和使用

Wireshark的安装和使用 前言一、Wireshark是什么简介 二、下载Wireshark下载过程查看自己电脑配置 三、安装Wireshark安装过程安装组件创建快捷方式winPacpNpcap 打开检验 四、使用Wireshark实施抓包捕获数据包 五、基于Wireshark使用显示过滤器简介使用方法注意ICMP的请求和应…

目标检测算法综述

1 研究背景 1.1 概述 目标检测是计算机视觉的重要分支&#xff0c;主要任务是在给定的图片中精确找到物体所在位置&#xff0c;并标注出物体的类别&#xff0c;即包含了目标定位与目标分类两部分。在计算机视觉领域中的目标跟踪、图像分割、事件检测、场景理解等的任务都以目标…

Java线程几种常用方法详细说明

在Java编程中&#xff0c;多线程编程是一个非常重要的主题。它允许我们同时运行多个任务&#xff0c;提高程序的性能和响应速度。在这篇博客中&#xff0c;我们将介绍一些常用的Java线程方法和构造器&#xff0c;并通过示例代码展示如何使用它们。 Thread提供的常用方法 publi…

SpringBoot与Spring Framework提供的缓存抽象

目录 缓存 项目总结 新建一个SpringBoot项目 pom.xml application.properties CacheConfig Book BookRepository接口 BookService服务类 BookController控制器 SpringbootCacheApplication启动类 启动项目&#xff0c;使用Postman测试 参考博文&#xff1a; 1、使用…

llvm 3.5 源码分析 clang for x86 001 之搭环境

0&#xff0c;目标 编译 针对x86 的&#xff0c;debug 的 c语言的编译器 1&#xff0c;下载代码 git clone --recursive 。。。llvm-project.git $ cd llvm-project 2&#xff0c;预备代码 llvm 3.5 版本的源代码&#xff0c;早期版本&#xff0c;可能比较小比较容易debug $…

Unity MiniCPM-V 让引擎拥有视觉

Unity MiniCPM-V 让引擎拥有视觉 前言项目Python环境布置Unity场景布置代码编写添加并设置脚本总结 鸣谢AI提示 前言 新发布的MiniCPM-V&#xff0c;忍不住玩一下&#xff0c;可以让之前制作的语音助手拥有一定的视觉能力&#xff08;不是OpenCV不行&#xff0c;而是AI更加符合…

深入理解Java关系运算符,避免常见错误!

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

复现CELL文章图片,在线绘制scRNA-seq多个cluster的差异火山图

导读 火山图表示两组间的差异&#xff0c;而在scRNA-seq中&#xff0c;同时会有多个cluster&#xff08;vs其他cluster&#xff09;的差异。如果用常规火山图展示&#xff0c;可能需要绘制十几个图。有没有更高效的展示方法呢&#xff1f;今天给大家带来CELL文章“A Spatiotem…

打造SimPO新算法,微调8B模型超越Claude 3 Opus

前言 大型语言模型&#xff08;LLM&#xff09;近年来取得了巨大进展&#xff0c;但要将其与人类价值观和意图相一致&#xff0c;使其变得有用、诚实和无害&#xff0c;仍然是一个挑战。强化学习从人类反馈中&#xff08;RLHF&#xff09;是一种常用的方法&#xff0c;通过微调…

Charles的安装和web端抓包配置

1.Charles的安装 通过官网下载&#xff1a;https://www.charlesproxy.com/download/&#xff0c;我之前下载的是4.6.2版本&#xff0c;下载成功后点击安装包&#xff0c;点击下一步下一步即可安装成功。 ​​ ​ 安装成功后打开charles页面如下所示。 ​ 2.乱码问题解决 打开…

【C++】STL:栈和队列模拟实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

绘画参数配置及使用

绘画参数配置及使用 路径&#xff1a;站点后台-功能-AI绘画 进入参数配置 接口选择&#xff1a;多种接口自主选择&#xff08;需自己准备key&#xff09;&#xff0c;对应接口的key对话和绘画通用 存储空间&#xff1a; 位置在超管后台-存储空间 自主选择存储&#xff08;需…

【数据结构与算法】七大排序算法(下)

【数据结构与算法】七大排序算法(下) &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;数据结构与算法&#x1f345; &#x1f33c;文章目录&#x1f33c; 2.3 交换排序 2.3.1 冒泡排序 2.3.2 快速排序 2.3.3 快速排序(非递归) 2.4 归并…

fpga入门 串口定时1秒发送1字节

一、 程序说明 FPGA通过串口定时发送数据&#xff0c;每秒发送1字节&#xff0c;数据不断自增 参考小梅哥教程 二、 uart_tx.v timescale 1ns / 1psmodule uart_tx(input wire sclk,input wire rst_n,output reg uart_tx);parameter …