当前位置: 首页 > news >正文

SpringBoot 使用 JWT 保护 Rest Api 接口

用 spring-boot 开发 RESTful API 非常的方便,在生产环境中,对发布的 API 增加授权保护是非常必要的。现在我们来看如何利用 JWT 技术为 API 增加授权保护,保证只有获得授权的用户才能够访问 API。

一、Jwt 介绍

  JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,
用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
Jwt 主要应用场景:授权
  Authorization (授权) : 这是使用 JWT 的最常见场景。一旦用户登录,后续每个请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的 JWT 的
一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  在认证的时候,当用户用他们的凭证成功登录以后,一个 JSON Web Token 将会被返回。此后,token 就是用户凭证了。为什么不用 session:因为做了完全的前后端分离,前段页面每次发出 Ajax 请求都会建立一个新的回话请求,没办法通过 session 来记录跟踪用户回话状态。所以采用 JWT,来完成回话跟踪、身份验证。Session 是在服务器端的,而 JWT 是在客户端的。

JWT 使用流程:

  • 用户携带用户名和密码请求访问
  • 服务器校验用户凭据
  • 应用提供一个 token 给客户端
  • 客户端存储 token,并且在随后的每一次请求中都带着它
  • 服务器校验 token 并返回数据

二、搭建基础 SpringBoot 工程

2.1、新建一个 SpringBoot 工程,引入所需依赖包

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> 
</dependency> 

2.2、编写测试 Controller HelloController


package com.offcn.controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RestController; 
@RestController 
public class HelloController { @RequestMapping("/hello") public String hello(){ return "hello"; } 
}

2.3、编写应用主启动类

package com.offcn; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.context.annotation.Bean; 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 
@SpringBootApplication 
public class SpringbootSecurityJwtApplication {
public static void main(String[] args) { SpringApplication.run(SpringbootSecurityJwtApplication.class, args); } 
}

2.4、测试应用
  访问地址:http://localhost:8080/hello
  至此,我们的接口就开发完成了。但是这个接口没有任何授权防护,任何人都可以访问,这
样是不安全的,下面我们开始加入授权机制。

三、增加用户注册功能

3.1、导入数据库所需依赖包

<!-- spring-data-jpa --> 
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> 
</dependency> 
<!-- mysql --> 
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> 
<version>5.1.30</version> 
</dependency> 
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> 
</dependency>

3.2、修改配置文件 application.yml 配置数据库连接

spring: datasource: url: jdbc:mysql://localhost:3306/springboot-security?serverTimezone=GMT%2B8 username: root password: 123 driver-class-name: com.mysql.jdbc.Driver jpa:hibernate: ddl-auto: update show-sql: true application: name: demo1 #配置应用名称 

3.3、新建一个实体类 User


@Entity 
@Table(name = "tb_user") 
@Data 
@NoArgsConstructor 
@AllArgsConstructor 
public class User { 
@Id 
@GeneratedValue private  Long id; private String username; private String password; 
}

3.4、新建一个 dao 实现 JpaRepository 接口

package com.offcn.dao; 
import com.offcn.po.User; 
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User,Long> { User findByUsername(String username); 
}

3.5、新建 UserController 类中增加注册方法,实现用户注册的接口


package com.offcn.controller; 
import com.offcn.dao.UserDao; 
import com.offcn.po.User; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.PostMapping; 
import org.springframework.web.bind.annotation.RequestBody; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.ResponseBody; 
@Controller 
@RequestMapping("/users") 
public class UserController { @Autowired private UserDao userDao; /*** 该方法是注册用户的方法,默认放开访问控制 * @param user */@PostMapping("/signup") @ResponseBody public String signUp(@RequestBody User user) { user.setPassword(user.getPassword()); try {userDao.save(user); return "success"; } catch (Exception e) { e.printStackTrace(); return "error";} } 
}

3.6 测试用户注册
  请求地址:http://localhost:8080/users/signup

四、添加 JWT 认证

  用户填入用户名密码后,与数据库里存储的用户信息进行比对,如果通过,则认证成功。传统的方法是在认证通过后,创建 sesstion,并给客户端返回 cookie。现在我们采用 JWT 来处理用户名密码的认证。区别在于,认证通过后,服务器生成一个 token,将 token 返回给客户端,客户端以后的所有请求都需要在 http 头中指定该 token。服务器接收的请求后,会对
token 的合法性进行验证。验证的内容包括:内容是一个正确的 JWT 格式 、检查签名 、检查 claims 、检查权限。

4.1、导入 JWT 及 SpringSecurity 所需依赖包


<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> 
</dependency> 
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> 
</dependency> 

4.2、编写登录处理过滤器类 JWTLoginFilter
核心功能是在验证用户名密码正确后,生成一个 token,并将 token 返回给客户端 该类继承自 UsernamePasswordAuthenticationFilter,重写了其中的 2 个方法:

  • attemptAuthentication :接收并解析用户凭证。
  • successfulAuthentication :用户成功登录后,这个方法会被调用,我们在这个方法里生成 token。
/**
* 验证用户名密码正确后,生成一个 token,并将 token 返回给客户端
* 
该类继承自 UsernamePasswordAuthenticationFilter,重写了其中的 2 个方法 
* 
attemptAuthentication :接收并解析用户凭证。 
* successfulAuthentication :用户成功登录后,这个方法会被调用,我们在这个方法里生成 
token。 
*
*/
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter 
{ private AuthenticationManager authenticationManager; public JWTLoginFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; }// 接收并解析用户凭证 @Override public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException { try {User user = new ObjectMapper() .readValue(req.getInputStream(), User.class); return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( user.getUsername(), user.getPassword(), new ArrayList<>()) ); } catch (IOException e) { throw new RuntimeException(e); } 
}
// 用户成功登录后,这个方法会被调用,我们在这个方法里生成 token 
@Override 
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException {String token = Jwts.builder() .setSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername()) .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 24 * 1000)) .signWith(SignatureAlgorithm.HS512, "MyJwtSecret") .compact(); res.addHeader("Authorization", "Bearer " + token); } 
}

4.3、编写 Token 校验过滤器类 JWTAuthenticationFilter
用户一旦登录成功后,会拿到 token,后续的请求都会带着这个 token,服务端会验证 token的合法性。该类继承自 BasicAuthenticationFilter,在 doFilterInternal 方法中,从 http 头的 Authorization 项读取 token 数据,然后用 Jwts 包提供的方法校验 token 的合法性。如果校验通过,就认为这是一个取得授权的合法请求。


/**
* 
token 的校验 
* 
该类继承自 BasicAuthenticationFilter,在 doFilterInternal 方法中, 
* 
从 http 头的 Authorization 项读取 token 数据,然后用 Jwts 包提供的方法校验 token 的合 
法性。
* 如果校验通过,就认为这是一个取得授权的合法请求 
*
*/
public class JWTAuthenticationFilter extends BasicAuthenticationFilter { public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); }@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain 
chain) throws IOException, ServletException { String header = request.getHeader("Authorization");if (header == null || !header.startsWith("Bearer ")) { chain.doFilter(request, response); return; }UsernamePasswordAuthenticationToken authentication = getAuthentication(request); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); }private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {String token = request.getHeader("Authorization"); if (token != null) { // parse the token. String user = Jwts.parser() .setSigningKey("MyJwtSecret") .parseClaimsJws(token.replace("Bearer ", "")) .getBody() .getSubject(); if (user != null) { return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); }return null; }return null; } 
}

五、SpringSecurity 配置集成 JWT 认证

5.1、编写 SpringSecurity 用户及权限验证类UserDetailServiceImpl


@Service("userDetailServiceImpl") 
public class UserDetailServiceImpl implements UserDetailsService { 
@Autowired 
private UserDao userDao; 
@Override 
public UserDetails loadUserByUsername(String username) throws 
UsernameNotFoundException { 
//根据用户名查询用户信息 
com.offcn.po.User user = userDao.findByUsername(username); 
//为用户授权(暂时未验证权限) 
List<GrantedAuthority> grantedAuthorityList=new ArrayList<>(); 
grantedAuthorityList.add(new SimpleGrantedAuthority("ROLE_USER")); 
return new User(username,user.getPassword(),grantedAuthorityList); 
} 
}

5.2、修改程序主启动类,增加密码加密生成器配置

@SpringBootApplication 
public class SpringbootSecurityJwtApplication { 
public static void main(String[] args) { 
SpringApplication.run(SpringbootSecurityJwtApplication.class, args); 
}
@Bean 
public BCryptPasswordEncoder bCryptPasswordEncoder() { 
return new BCryptPasswordEncoder(); 
} 
}

5.3、编写 SpringSecurity 配置类 WebSecurityConfig
  通过 SpringSecurity 的配置,将上面的 JWT 过滤器类组合在一起。


/**
* 
SpringSecurity 的配置 
* 
通过 SpringSecurity 的配置,将 JWTLoginFilter,JWTAuthenticationFilter 组合在一起 
*
*/
@Configuration 
@Order(SecurityProperties.DEFAULT_FILTER_ORDER) 
public class WebSecurityConfig 
extends WebSecurityConfigurerAdapter { 
@Autowired 
private UserDetailsService 
userDetailServiceImpl; 
@Autowired 
private BCryptPasswordEncoder bCryptPasswordEncoder; 
@Override 
protected void configure(HttpSecurity http) throws Exception { 
http.cors().and().csrf().disable().authorizeRequests() 
.antMatchers(HttpMethod.POST, "/users/signup").permitAll() 
.anyRequest().authenticated() 
.and() 
.addFilter(new 
JWTLoginFilter(authenticationManager())) 
.addFilter(new 
JWTAuthenticationFilter(authenticationManager())); 
}
@Override 
public void configure(AuthenticationManagerBuilder auth) throws Exception { 
auth.userDetailsService(userDetailServiceImpl).passwordEncoder(bCryptPasswordEncod 
er);
} 
}

5.4、修改 UserController 类 增加注册加密密码


@Controller 
@RequestMapping("/users") 
public class UserController { 
@Autowired 
private BCryptPasswordEncoder bCryptPasswordEncoder; 
@Autowired 
private UserDao userDao; 
/**
* 
该方法是注册用户的方法,默认放开访问控制 
* 
@param user 
*/
@PostMapping("/signup") 
@ResponseBody 
public String signUp(@RequestBody User user) { 
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); 
try {
userDao.save(user); 
return "success"; 
} catch (Exception e) { 
e.printStackTrace(); 
return "error"; 
} } }

六、测试 Token

6.1、测试请求 hello 接口
请求地址:http://localhost:8080/hello

6.2、重新注册一个账号
清空数据表
重新注册一个账号:
请求地址:http://localhost:8080/users/signup

查看数据库

6.3、测试登录
请求地址:http://localhost:8080/login
发 post 请求

响应的请求头 Authorization 的值就是 token
Bearer
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0IiwiZXhwIjoxNTY1MjY0OTQxfQ.CW-QwtE1Q2
Z69NNUnH_wPIaJjJpTFnh8eR3z03ujw-hb3aMO61yuir6w-T0X0FdV9k2WQrj903J9VDz6ijPJt
Q
6.4、用登录后的 token 再次请求 hello 接口
注意:在请求头中携带 token
请求头名称:Authorization
携带对应的 token 值。
可以看到正常响应结果。

相关文章:

SpringBoot 使用 JWT 保护 Rest Api 接口

用 spring-boot 开发 RESTful API 非常的方便&#xff0c;在生产环境中&#xff0c;对发布的 API 增加授权保护是非常必要的。现在我们来看如何利用 JWT 技术为 API 增加授权保护&#xff0c;保证只有获得授权的用户才能够访问 API。 一、Jwt 介绍 JSON Web Token (JWT)是一个开…...

大蟒蛇(Python)笔记(总结,摘要,概括)——第10章 文件和异常

目录 10.1 读取文件 10.1.1 读取文件的全部内容 10.1.2 相对文件路径和绝对文件路径 10.1.3 访问文件中的各行 10.1.4 使用文件的内容 10.1.5 包含100万位的大型文件 10.1.6 圆周率中包含你的生日吗 10.2 写入文件 10.2.1 写入一行 10.2.2 写入多行 10.3 异常 10.3.1 处理Ze…...

使用JDBC操作数据库(IDEA编译器)

目录 JDBC的本质 ​ JDBC好处 JDBC操作MySQL数据库 1.创建工程导入驱动jar包 2.编写测试代码 ​相关问题 JDBC的本质 官方(sun公司) 定义的一套操作所有关系型数据库的规则&#xff0c;即接口各个数据库厂商去实现这套接口&#xff0c;提供数据库驱动jar包我们可以使用这…...

Vue图片浏览组件v-viewer,支持旋转、缩放、翻转等操作

Vue图片浏览组件v-viewer&#xff0c;支持旋转、缩放、翻转等操作 之前用过viewer.js&#xff0c;算是市场上用过最全面的图片预览。v-viewer&#xff0c;是基于viewer.js的一个图片浏览的Vue组件&#xff0c;支持旋转、缩放、翻转等操作。 基本使用 安装&#xff1a;npm安装…...

大蟒蛇(Python)笔记(总结,摘要,概括)——第2章 变量和简单的数据类型

目录 2.1 运行hello_world.py时发生的情况 2.2 变量 2.2.1 变量的命名和使用 2.2.2 如何在使用变量时避免命名错误 2.2.3 变量是标签 2.3 字符串 2.3.1 使用方法修改字符串的大小写 2.3.2 在字符串中使用变量 2.3.3 使用制表符或换行符来添加空白 2.3.4 删除空白 2.3.5 删除…...

SpringCloud-Gateway网关的使用

本文介绍如何再 SpringCloud 项目中引入 Gateway 网关并完成网关服务的调用。Gateway 网关是一个在微服务架构中起到入口和路由控制的关键组件。它负责处理客户端请求&#xff0c;进行路由决策&#xff0c;并将请求转发到相应的微服务。Gateway 网关还可以实现负载均衡、安全认…...

想要学习编程,有什么推荐的书籍吗

如果你要变得更好&#xff0c;C语言是一个极佳的选择&#xff0c;其原因有二。首先&#xff0c;C语言缺乏任何现代的安全功能&#xff0c;这意味着你必须更为警惕&#xff0c;时刻了解真正发生的事情。如果你能写出安全、健壮的C代码&#xff0c;那你就能用任何编程语言写出安全…...

LWM(LargeWorldModel)大世界模型-可文字可图片可视频-多模态LargeWorld-视频问答成功运行-实现循环问答多次问答

Large World Model&#xff08;LWM&#xff09;现在大火&#xff0c;其最主要特点是不仅能够针对文本进行检索交互&#xff0c;还能对图片、视频进行问答交互&#xff0c;自从上文《LWM(LargeWorldModel)大世界模型-可文字可图片可视频-多模态LargeWorld-详细安装记录》发出后&…...

线阵相机之帧超时

1 帧超时的效果 在帧超时时间内相机若未采集完一张图像所需的行数&#xff0c;则相机会直接完成这张图像的采集&#xff0c;并自动将缺失行数补黑出图&#xff0c;机制有以下几种选择&#xff1a; 1. 丢弃整张补黑的图像 2. 保留补黑部分出图 3.丢弃补黑部分出图...

模型转换案例学习:等效替换不支持算子

文章介绍 Qualcomm Neural Processing SDK &#xff08;以下简称SNPE&#xff09;支持Caffe、ONNX、PyTorch和TensorFlow等不同ML框架的算子。对于某些特定的不支持的算子&#xff0c;我们介绍一种算子等效替换的方法来完成模型转换。本案例来源于https://github.com/quic/qidk…...

js 数组排序的方式

var numberList [5, 100, 94, 71, 49, 36, 2, 4]; 冒泡排序&#xff1a; 相邻的数据进行两两比较&#xff0c;小数放在前面&#xff0c;大数放在后面&#xff0c;这样一趟下来&#xff0c;最小的数就被排在了第一位&#xff0c;第二趟也是如此&#xff0c;如此类推&#xff0…...

手机连接电脑后资源管理器无法识别(识别设备但无法访问文件)

问题描述 小米8刷了pixel experience系统,今天用电脑连接后无法访问手机文件,但是手机选择了usb传输模式为文件传输 解决办法 在设备和打印机页面中右键选择属性 点击改变设置 卸载驱动,注意勾选删除设备的驱动程序软件 卸载后重新连接手机,电脑弹出希望对设备进行什么操作时…...

安装unget包 sqlsugar时报错,完整的报错解决

前置 .net6的开发环境 问题 ? 打开unget官网&#xff0c;搜索报错的依赖Oracle.ManagedDataAccess.Core unget官网 通过unget搜索Oracle.ManagedDataAccess.Core查看该依赖的依赖 发现应该是需要的依赖Oracle.ManagedDataAccess.Core(>3.21.100)不支持.net6的环境 解…...

oracle数据库事务的四大特性与隔离级别与游标

数据库事务的四大特性: 这里提到了 ACID 四个特性&#xff0c;分别是&#xff1a; A&#xff08;Atomicity&#xff09;&#xff1a; 原子性&#xff0c;确保事务中的所有操作要么全部执行成功&#xff0c;要么全部不执行&#xff0c;不存在部分执行的情况。 C&#xff08;…...

Day25--learning English

一、积累 1.crab 2.scrape 3.crude 4.infect 5.blinds 6.plunk 7.fart 8.expel 9.stamp 10.tongs 11.utensil 12.sticky 13.yolk 14.snap 15.fuzz 16.chuck 17.hamper 18.panel 19.prod 20.eyebrow 二、练习 1.牛津原译 scrape /skreɪp/ REMOVE 除去 1.to remove sth from…...

职业技能鉴定服务中心前端静态页面(官网+证书查询)

有个朋友想做职业技能培训&#xff0c;会发证书&#xff0c;证书可以在自己网站可查。想做一个这样的网站&#xff0c;而且要特别土&#xff0c;一眼看上去像xxx官方网站&#xff0c;像jsp .net技术开发的网站。用htmlcssjquery还原了这样子一个前端页面&#xff0c;这里分享给…...

第六十六天 API安全-接口安全阿里云KEY%postmanDVWSXEE鉴权泄露

第66天 API安全-接口安全&阿里云KEY%postman&DVWS&XEE&鉴权&泄露 知识点 1.HTTP类接口-测评 2.RPC类接口-测评 3.Web Service类-测评 参考链接&#xff1a;https://www.jianshu.com/p/e48db27d7c70 内容点&#xff1a; SOAP(Simple Object Access Prot…...

在Vue3 + Vite项目中使用less

在Vue3 Vite项目中使用less&#xff0c;需要安装less和less-loader两个依赖。 首先&#xff0c;在项目根目录下执行以下命令安装less和less-loader&#xff1a; npm install less less-loader --save-dev安装完成后&#xff0c;在vite.config.js配置文件中添加以下代码&…...

this的指向问题总结

this一般会出现在函数里面&#xff0c;但是一般情况下只有在函数被调用执行时&#xff0c;才能确定this指向哪个对象。一般情况下this是指调用函数的对象。 1.在全局作用域下或者普通函数中this的指向一般都是window对象 window.fn&#xff08;&#xff09;&#xff0c;普通函…...

jQuery的应用(二)

对上一节内容的补充。 jQuery选择器 jQuery选择器类似于CSS选择器,用来选取网页中的元素 jQuery选择器功能强大,种类也很多,分类如下 通过CSS选择器选取元素: 基本选择器层次选择器属性选择器通过过滤选择器选择元素: 基本过滤选择器可见性过滤选择器表单对象过滤选择器…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...