保护你的应用:Spring Boot与JWT的黄金组合

🎏:你只管努力,剩下的交给时间

🏠 :小破站

保护你的应用:Spring Boot与JWT的黄金组合

    • 前言
    • 第一:项目整合
      • 基本配置
      • token验证配置
      • 拦截器配置
      • 依赖的pom坐标
    • 第二:Spring Security与JWT的完美协作
      • 步骤1: 引入依赖
      • 步骤2: 配置Spring Security
      • 步骤3: 实现JWT工具类
      • 步骤4: 集成JWT到身份验证过滤器
      • 步骤5: 配置Spring Security使用JWT过滤器
    • 第三:实际应用场景
      • 1. 前后端分离应用
      • 2. 微服务架构
      • 3. 单点登录(SSO
      • 4. Token刷新
      • 5. 动态权限调整
      • 6. 无状态服务
      • 7. 跨域资源共享(CORS)
      • 8. 移动应用身份验证
    • 第四:使用公钥/私钥对JWT进行签名
      • 1. 生成密钥对
        • 使用keytool生成密钥对:
        • 使用开发库生成密钥对:
      • 2. 在Spring Boot中应用密钥对
        • application.properties(或application.yml)中配置密钥:
        • 配置类中加载密钥:
      • 3. 在JWT签名和验证中使用密钥对
        • 生成JWT并签名:
        • 验证JWT签名:

前言

在网络的世界中,数据安全是应用开发的头等大事。而Spring Boot与JWT的完美结合,就像是为你的应用添加了一把坚实的安全之锁。从今天开始,让我们一同踏上这场奇妙的旅程,揭开Spring Boot整合JWT的神秘面纱。

第一:项目整合

基本配置

package test.bo.springbootjwt.util;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;import java.util.Calendar;
import java.util.Map;public class JwtUtil {private static final String sign="XS4B#2&1!*";//生成token   header   payload    signpublic static String getToken(Map<String,String> map){Calendar instance = Calendar.getInstance();instance.add(Calendar.HOUR,24);//默认一天过期//创建jwt   builderJWTCreator.Builder builder = JWT.create();//payloadmap.forEach((k,v)->{builder.withClaim(k,v);});String token = builder.withExpiresAt(instance.getTime())//指定令牌过期时间.sign(Algorithm.HMAC256(sign));return token;}//验证tokenpublic static DecodedJWT verify(String token){return JWT.require(Algorithm.HMAC256(sign)).build().verify(token);}//获取token信息方法public static DecodedJWT getTokerInfo(String token){DecodedJWT verify = JWT.require(Algorithm.HMAC256(sign)).build().verify(token);return verify;}
}

token验证配置

package test.bo.springbootjwt.interceptors;import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import test.bo.springbootjwt.entity.Result;
import test.bo.springbootjwt.util.JwtUtil;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JWTInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException, ServletException {if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {System.out.println("OPTIONS请求,放行");return true;}response.setContentType("application/json;charset=UTF-8");Result result = new Result();//获取请求头中令牌String token = request.getHeader("token0001");System.out.println(token);try {JwtUtil.verify(token);//验证令牌return true;}catch (SignatureVerificationException e){e.printStackTrace();result.setMsg("无效签名");}catch (TokenExpiredException e){e.printStackTrace();result.setMsg("token过期");}catch (AlgorithmMismatchException e){e.printStackTrace();result.setMsg("token算法不一致");}catch (Exception e){e.printStackTrace();result.setMsg("token无效");}result.setState(false);/* String string = new ObjectMapper().writeValueAsString(result);response.setContentType("application/json;charset=utf8");response.getWriter().println(string);*/return false;}}

拦截器配置

package test.bo.springbootjwt.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import test.bo.springbootjwt.interceptors.JWTInterceptor;@Configuration
@CrossOrigin
public class InterceptorConfig implements WebMvcConfigurer {public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS").maxAge(36000).allowedHeaders("*").allowCredentials(true);}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JWTInterceptor()).addPathPatterns("/**")    //其他接口token验证.excludePathPatterns("/user/login"); //所有用户都放行}
}

依赖的pom坐标

<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.10.3</version>
</dependency>

第二:Spring Security与JWT的完美协作

Spring Security和JWT的结合在现代Web应用中是一种常见的做法,它提供了一种安全的身份验证和授权机制。以下是配置Spring Security以使用JWT进行身份验证的一般步骤:

步骤1: 引入依赖

确保项目中引入了Spring Security和JWT的相关依赖。可以使用Maven或Gradle进行引入,例如:

Maven:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> <!-- 替换为最新版本 -->
</dependency>

Gradle:

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.9.1' // 替换为最新版本

步骤2: 配置Spring Security

在Spring Boot应用的配置类中配置Spring Security,指定哪些路径需要进行身份验证,哪些路径不需要。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/public/**").permitAll() // 不需要身份验证的路径.anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用Session}
}

步骤3: 实现JWT工具类

实现一个用于生成和解析JWT的工具类。可以使用JJWT库来简化这个过程。

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;public class JwtUtil {private static final String SECRET_KEY = "your_secret_key";public static String generateToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();return createToken(claims, userDetails.getUsername());}private static String createToken(Map<String, Object> claims, String subject) {return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // Token有效期10小时.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}public static Boolean validateToken(String token, UserDetails userDetails) {final String username = extractUsername(token);return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));}private static Boolean isTokenExpired(String token) {return extractExpiration(token).before(new Date());}private static Date extractExpiration(String token) {return extractClaim(token, Claims::getExpiration);}private static <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {final Claims claims = extractAllClaims(token);return claimsResolver.apply(claims);}private static Claims extractAllClaims(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();}private static String extractUsername(String token) {return extractClaim(token, Claims::getSubject);}
}

步骤4: 集成JWT到身份验证过滤器

创建一个自定义的过滤器,用于在Spring Security的过滤链中验证JWT,并将用户信息添加到Spring Security上下文。

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class JwtRequestFilter extends UsernamePasswordAuthenticationFilter {private final UserDetailsService userDetailsService;private final JwtUtil jwtUtil;public JwtRequestFilter(UserDetailsService userDetailsService, JwtUtil jwtUtil) {this.userDetailsService = userDetailsService;this.jwtUtil = jwtUtil;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String authorizationHeader = request.getHeader("Authorization");String username = null;String jwt = null;if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {jwt = authorizationHeader.substring(7);username = jwtUtil.extractUsername(jwt);}if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);if (jwtUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}

步骤5: 配置Spring Security使用JWT过滤器

在配置类中将自定义的JwtRequestFilter添加到Spring Security的过滤器链中。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtUtil jwtUtil;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/authenticate").permitAll() // 不需要身份验证的路径.anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用Sessionhttp.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);}@Beanpublic JwtRequestFilter jwtRequestFilter() {return new JwtRequestFilter(userDetailsService, jwtUtil);}@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

以上是一个简单的集成Spring Security和JWT的例子,实际项目中可能需要根据具体需求进行更复杂的配置和定制。这个例子涵盖了基本的认证、授权和JWT生成、解析的流程。

第三:实际应用场景

Spring Boot整合JWT在实际应用中有许多灵活的应用场景,特别适用于前后端分离和微服务架构。以下是一些实际应用场景的示例:

1. 前后端分离应用

在前后端分离的应用中,前端和后端是独立开发和部署的,通过API进行通信。JWT可以在这种情境下提供简便的身份验证和授权机制。

  • 用户认证: 用户在前端登录,后端验证用户名和密码,如果通过,后端生成JWT并返回给前端。
  • 后续请求: 前端在每个请求的头部携带JWT,后端通过JWT验证用户身份和权限。

2. 微服务架构

在微服务架构中,每个服务都可以独立验证和授权用户,而不需要在每个服务中保存用户的状态。JWT在这种场景下提供了轻量级的、无状态的身份验证方式。

  • 用户认证: 一个服务处理用户登录请求,生成JWT,返回给客户端。
  • 微服务间通信: 客户端在与其他微服务通信时,携带JWT进行身份验证,每个微服务独立验证JWT。

3. 单点登录(SSO

JWT可以用于实现单点登录,使用户只需在系统中进行一次登录,即可访问多个关联系统。

  • 登录过程: 用户在一个系统中登录,获取JWT。
  • 访问其他系统: 用户在其他系统中携带JWT,其他系统通过验证JWT即可知晓用户身份。

4. Token刷新

JWT的过期机制使得可以轻松实现Token刷新,提高了系统的安全性。

  • 刷新流程: 在JWT即将过期时,客户端使用刷新令牌或重新提供用户凭证来获取新的JWT,而无需重新输入用户名和密码。

5. 动态权限调整

在某些场景下,用户的权限可能会在登录后发生变化。JWT中包含的信息可以灵活地调整用户的权限。

  • 权限调整: 当用户的权限发生变化时,系统可以主动撤销旧的JWT,生成包含新权限信息的JWT。

6. 无状态服务

JWT是无状态的,服务端不需要存储用户的状态信息,这对于水平扩展和微服务的部署非常有利。

  • 水平扩展: 可以轻松地将请求路由到不同的服务实例,而不需要共享用户的状态。

7. 跨域资源共享(CORS)

在前后端分离的应用中,处理跨域问题是常见的挑战。JWT可以在跨域环境中通过在响应头中包含Token来解决跨域问题。

  • CORS处理: 后端在响应头中包含JWT,前端在每次请求中都携带JWT,以解决浏览器的同源策略限制。

8. 移动应用身份验证

JWT也可以用于移动应用的身份验证,提供安全的身份验证机制,同时减少对服务器的频繁请求。

  • 移动应用: 移动应用通过安全的方式将用户凭证传递给服务器,获取JWT。
  • JWT在请求中携带: 移动应用在后续请求中携带JWT,服务端验证并处理请求。

这些场景展示了Spring Boot整合JWT在不同应用场景下的灵活应用。通过使用JWT,可以实现简单、安全且可扩展的身份验证和授权机制。

第四:使用公钥/私钥对JWT进行签名

使用公钥/私钥对JWT进行签名可以提高系统的安全性,确保只有拥有私钥的服务端才能生成有效的签名,而其他服务端或客户端则可以通过公钥验证签名的合法性。以下是生成密钥对并在Spring Boot中应用的步骤:

1. 生成密钥对

首先,你需要生成公钥/私钥对。可以使用Java的keytool工具或者使用一些开发库来生成。

使用keytool生成密钥对:
keytool -genkeypair -alias myjwtkey -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore myjwtkeystore.p12 -validity 3650

这将生成一个2048位的RSA密钥对,并存储在名为myjwtkeystore.p12的文件中。

使用开发库生成密钥对:

在Java中,你也可以使用一些开发库(例如Bouncy Castle)生成密钥对。以下是一个使用Bouncy Castle库的示例:

import org.bouncycastle.jce.provider.BouncyCastleProvider;import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;public class KeyPairGeneratorExample {public static void main(String[] args) throws NoSuchProviderException, NoSuchAlgorithmException {Security.addProvider(new BouncyCastleProvider());KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");generator.initialize(2048); // key sizeKeyPair keyPair = generator.generateKeyPair();// keyPair.getPrivate() 返回私钥// keyPair.getPublic() 返回公钥}
}

2. 在Spring Boot中应用密钥对

在Spring Boot中,可以通过配置文件将密钥加载到应用中,以便在JWT的签名和验证过程中使用。

application.properties(或application.yml)中配置密钥:
# 密钥的存储类型(JKS或PKCS12等)
jwt.key-store-type=PKCS12
# 密钥库文件的路径
jwt.key-store=classpath:myjwtkeystore.p12
# 密钥库的密码
jwt.key-store-password=mypassword
# 别名
jwt.key-alias=myjwtkey
# 密钥的密码
jwt.key-password=mypassword
配置类中加载密钥:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.KeyStore;@Configuration
public class JwtConfig {@Value("${jwt.key-store-type}")private String keyStoreType;@Value("${jwt.key-store}")private String keyStore;@Value("${jwt.key-store-password}")private String keyStorePassword;@Value("${jwt.key-alias}")private String keyAlias;@Value("${jwt.key-password}")private String keyPassword;@Beanpublic KeyPair keyPair() throws Exception {KeyStore keystore = KeyStore.getInstance(keyStoreType);keystore.load(getClass().getClassLoader().getResourceAsStream(keyStore), keyStorePassword.toCharArray());KeyPair keyPair = new KeyPair(keystore.getCertificate(keyAlias).getPublicKey(),(PrivateKey) keystore.getKey(keyAlias, keyPassword.toCharArray()));return keyPair;}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

这个配置类中的keyPair()方法通过加载密钥库,获取公钥和私钥的方式创建了一个KeyPair对象,以便在JWT的签名和验证中使用。

3. 在JWT签名和验证中使用密钥对

使用Spring Security和JWT库,可以在签发和验证JWT时使用密钥对。

生成JWT并签名:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class JwtUtil {@Autowiredprivate KeyPair keyPair;public String generateToken(UserDetails userDetails) {return Jwts.builder().setSubject(userDetails.getUsername()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours.signWith(SignatureAlgorithm.RS256, keyPair.getPrivate()).compact();}
}
验证JWT签名:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class JwtUtil {@Autowiredprivate KeyPair keyPair;public Boolean validateToken(String token, UserDetails userDetails) {Claims claims = Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody();return userDetails.getUsername().equals(claims.getSubject()) && !isTokenExpired(token);}private Boolean isTokenExpired(String token) {Date expirationDate = Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody().getExpiration();return expirationDate.before(new Date());}
}

在这个例子中,SignatureAlgorithm.RS256指定了RSA算法,使用keyPair.getPrivate()进行签名,keyPair.getPublic()进行验证。

使用公钥/私钥对JWT进行签名和验证提供了更高的安全性,确保只有拥有私钥的服务端才能生成有效的签名,而其他服务端或客户端则可以通过公钥验证签名的合法性。

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

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

相关文章

软件测试 —— 冒烟测试(Smoke Test,ST)

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

Android帝国之进程杀手--lmkd

本文概要 这是Android系统启动的第三篇文章&#xff0c;本文以自述的方式来讲解lmkd进程&#xff0c;通过本文您将了解到lmkd进程在安卓系统中存在的意义&#xff0c;以及它是如何杀进程的。&#xff08;文中的代码是基于android13&#xff09; 我是谁 init&#xff1a;“大…

SpringCloudAlibaba微服务 【实用篇】| Nacos配置管理

目录 一&#xff1a;Nacos配置管理 1. 统一配置管理 2. 配置热更新 3. 配置共享 4. 搭建Nacos集群 tips&#xff1a;前些天突然发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c;感兴趣的同学可以进…

Zemax光学设计——单透镜设计

单透镜系统参数&#xff1a; 入瞳直径&#xff1a;20mm F/#&#xff08;F数&#xff09;&#xff1a;10 全视场&#xff1a;10 波长&#xff1a;587nm 材料&#xff1a;BK7 优化方向&#xff1a;最佳均方根光斑直径 设计步骤 一、单透镜系统参数 步骤一&#xff1a;入…

【Spring系列】DeferredResult异步处理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

PDF文件如何限制打印?限制打印清晰度?

想要限制PDF文件的打印功能&#xff0c;想要限制PDF文件打印清晰度&#xff0c;都可以通过设置限制编辑来达到目的。 打开PDF编辑器&#xff0c;找到设置限制编辑的界面&#xff0c;切换到加密状态&#xff0c;然后我们就看到 有印刷许可。勾选【权限密码】输入一个PDF密码&am…

安装Qt6.2 在Ubuntu 22.04系统

先看目录&#xff0c;了解整体流程&#xff01; 先看目录&#xff0c;了解整体流程&#xff01; 先看目录&#xff0c;了解整体流程&#xff01; 文章目录 下载下载对应系统的下载器为下载器指定镜像源下载长期支持版本(比较稳定)添加到系统环境变量验证项目中使用 Troublesho…

vue3随机生成8位字母+数字

// 随机生成8位字母数字 export const autoPassword: any () > {// console.log("自动生成");//可获取的字符串const chars ABCDEFGHIJKLMNOPQRSTUVWSYZabcdefghijklmnopqrstuvwsyz0123456789;const list [];//通过随机获取八个字符串的索引下标for (let i 0;…

Hive_last_value()

在SQL中&#xff0c;LAST_VALUE()函数是一个窗口函数&#xff0c;用于返回窗口内的最后一个值。窗口函数允许你在一组行上执行计算&#xff0c;这组行与当前行有某种关系。可以将它们想象为与当前行相关的“窗口”。 LAST_VALUE()函数通常与OVER()子句一起使用&#xff0c;后者…

IBM ServeRAID M1015阵列卡 支持RAID5,需要配件

最近一台IBM x3400m3的旧服务器&#xff0c;折腾一下&#xff0c;要添置硬盘&#xff0c;重做RAID&#xff0c;阵列卡是ServeRAID M1015&#xff0c;默认进去WebBIOS&#xff0c;只有RAID0和RAID1&#xff0c;没有RAID5&#xff0c;参考官方原版的手册&#xff0c;如下图&#…

R语言30分钟入门

1. 环境&安装 R是支持win、linux合macos的 完整参考&#xff1a;https://zhuanlan.zhihu.com/p/596324321?utm_id0 主要是安装&#xff1a;1、R环境&#xff1b;2、rstudio开发环境&#xff08;后面主要是用rstudio&#xff0c;也可以用vscode&#xff09; 1.1. rstud…

【软件安装】conda

conda是什么 conda是一个开源的软件包管理系统和环境管理系统&#xff0c;用于安装和管理多个软件包和其依赖关系。它能够在各种操作系统上工作&#xff0c;包括Windows、macOS和Linux。 conda的主要功能 创建和管理虚拟环境&#xff1a;可以通过conda创建多个隔离的Python环…

绘制折扇-第11届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第11讲。 绘制折扇&#xf…

Docker本地部署Firefox火狐浏览器并远程访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

分割掩模 VS 掩膜

掩膜 Mask分割掩模 Segmentation Mask总结示例 掩膜 Mask “掩膜” 是指一种用于 标识或遮蔽图像中特定区域 的 图像。 在图像处理中&#xff0c;掩膜通常是一个 二值图像&#xff0c;其中的 像素值为 0 或 1。binary Mask 叫做二元掩膜&#xff0c;如下图所示&#xff1a; 这…

transformer模型和Multi-Head Attention

参考英文文献&#xff1a; Understanding and Coding the Self-Attention Mechanism of Large Language Models From Scratch https://www.tensorflow.org/text/tutorials/transformer 解读参考链接&#xff1a;Understanding and Coding the Self-Attention Mechanism of Lar…

Python面试破解:return和yield的细腻差别

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是涛哥&#xff0c;今天为大家分享 Python面试破解&#xff1a;return和yield的细腻差别&#xff0c;全文3000字&#xff0c;阅读大约10钟。 在Python的函数编程中&#xff0c;return和yield是两个常用的关键词…

springboot实现邮箱发送功能

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 邮箱效果图一、pom配置二、页面编写三、配置yml四、邮件工具类五、测试发送 邮箱效果图 1.可以利用在出现问题进行邮箱提醒 2.编写html 用于在邮箱中展示的样式 提示…

数据结构-04-队列

1-队列的结构和特点 生活中我们排队买票&#xff0c;先来的先买&#xff0c;后来的人只能站末尾&#xff0c;不允许插队。先进者先出&#xff0c;这就是典型的"队列"。队列跟栈非常相似&#xff0c;支持的操作也很有限&#xff0c;最基本的操作也是两个&#xff1a;入…

记录一次前后端传参方式不一致异常

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 报错以及Bug ✨特色专栏&#xff1a; …