Spring Boot 登录实现:JWT 与 Session 全面对比与实战讲解

Spring Boot 登录实现:JWT 与 Session 全面对比与实战讲解

2025.5.21-23:11今天在学习黑马点评时突然发现用的是与苍穹外卖jwt不一样的登录方式-Session,于是就想记录一下这两种方式有什么不同

在实际开发中,登录认证是后端最基础也是最重要的模块之一。常见的实现方式主要有两种:Session 登录JWT(JSON Web Token)登录

下面将从原理、使用场景、优缺点、代码结构等角度,来对比这两种方案,然后给出 Spring Boot 中的实际应用建议,帮助在实际项目中做出合理选择。

一、登录认证基本流程

无论是 JWT 还是 Session,登录的本质流程都是:

  1. 用户发送用户名和密码;
  2. 后端验证成功后,生成认证信息;
  3. 后端将认证信息返回给客户端;
  4. 客户端携带认证信息访问受保护接口;
  5. 后端验证该认证信息是否合法。

二、Session 登录机制

1. 原理说明

  • 用户第一次登录成功后,服务器创建一个 Session,并在服务器内存或 Redis 中保存用户信息;
  • 同时将 Session ID 写入到浏览器的 Cookie 中;
  • 用户每次请求都会自动携带该 Cookie,服务器根据 Session ID 获取用户信息。
    在这里插入图片描述

2. 代码实现示例

登录接口:
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO, HttpSession session) {User user = userService.login(loginDTO);session.setAttribute("user", user);return Result.success();
}
拦截器判断是否登录:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {Object user = request.getSession().getAttribute("user");if (user == null) {response.setStatus(401);return false;}return true;
}

三、JWT 登录机制

1. 原理说明

  • 用户登录成功后,服务器签发一个加密的 JWT Token;
  • 客户端将 Token 保存在 LocalStorage 或 Cookie;
  • 每次请求都将 Token 放在 Authorization 请求头中;
  • 服务器解析并验证 Token,从中读取用户信息。
    在这里插入图片描述

2. JWT Token 的组成

JWT 一般由三部分组成:

Header.Payload.Signature

例如:

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEyMywidXNlcm5hbWUiOiJsaTIzIn0.sD_Kpdi2M...

3. JWT 登录代码示例

登录生成 Token:
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO) {User user = userService.login(loginDTO);String token = JwtUtil.generateToken(user); // 自定义工具类生成 tokenreturn Result.success(token);
}
前端请求携带 Token:
GET /user/info HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
拦截器验证 Token:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String token = request.getHeader("Authorization");Claims claims = JwtUtil.parseToken(token);if (claims == null) {response.setStatus(401);return false;}return true;
}

在这里插入图片描述

四、Session vs JWT 对比总结

特性SessionJWT
存储位置服务端(内存/Redis)客户端(LocalStorage/Cookie)
状态性有状态无状态
跨域支持不友好(需处理 Cookie)友好
性能频繁读写服务端存储只需签名验证
安全性较高(信息在服务端)中等(需防止 Token 泄露)
适用场景传统 Web 应用前后端分离、微服务、移动端

五、实战项目推荐

在实际项目中,建议根据以下场景选择:

  • 中小型 Web 应用:推荐使用 Session,实现简单,安全性高;
  • 前后端分离项目:推荐使用 JWT,无状态,跨域友好;
  • 大型系统:考虑 JWT + Session 混合使用,结合两者优势。

六、JWT 常见安全建议

  1. 设置过期时间(exp),避免 token 永久有效;
  2. 使用 HTTPS,防止 Token 被中间人截获;
  3. 配合 Refresh Token 实现续签;
  4. 用户登出时将 Token 加入 Redis 黑名单;
  5. Token 不应包含敏感信息(如密码、身份证号等)。

七、总结

Session 和 JWT 各有优缺点,选择时需根据项目实际情况权衡:

  • Session 适合传统 Web 应用,安全性高,实现简单;
  • JWT 适合前后端分离、微服务架构,无状态,扩展性强。

在实际开发中,还可以结合两者的优势,实现更灵活、安全的认证方案。

八、登录超时控制

1. Session 登录的过期控制

# application.yml 中配置 Session 失效时间(单位分钟)
server:servlet:session:timeout: 30

Session 登录通常依赖浏览器 Cookie,每次请求自动续期,适合长期在线场景。

2. JWT 的过期控制

JWT 自带 exp 字段,在生成 Token 时设置过期时间:

// JwtUtil 生成 Token 示例
public static String generateToken(User user) {Date now = new Date();Date expireDate = new Date(now.getTime() + EXPIRE_TIME); // 设置过期时间return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(user.getId().toString()).setIssuedAt(now).setExpiration(expireDate).claim("username", user.getUsername()).signWith(SignatureAlgorithm.HS512, SECRET) // 签名加密.compact();
}

九、JWT 的刷新机制(Refresh Token)

为了避免用户频繁登录,可以实现 Refresh Token 机制:

  1. 登录时生成两个 Token:AccessToken(短过期)和 RefreshToken(长过期);
  2. AccessToken 过期后,使用 RefreshToken 重新获取 AccessToken;
  3. RefreshToken 也过期时,才需要用户重新登录。
// 登录接口返回两个 Token
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO) {User user = userService.login(loginDTO);String accessToken = JwtUtil.generateAccessToken(user);String refreshToken = JwtUtil.generateRefreshToken(user);Map<String, String> tokens = new HashMap<>();tokens.put("accessToken", accessToken);tokens.put("refreshToken", refreshToken);return Result.success(tokens);
}// 刷新 Token 接口
@PostMapping("/refreshToken")
public Result refreshToken(@RequestBody RefreshTokenDTO dto) {// 验证 RefreshToken 有效性Claims claims = JwtUtil.parseRefreshToken(dto.getRefreshToken());if (claims == null) {return Result.error("登录已过期,请重新登录");}// 重新生成 AccessTokenUser user = userService.getById(Long.valueOf(claims.getSubject()));String accessToken = JwtUtil.generateAccessToken(user);return Result.success(accessToken);
}

十、登出与 Redis 黑名单机制

JWT 本身无法主动失效,但可以通过 Redis 黑名单实现:

// 登出接口
@PostMapping("/logout")
public Result logout(HttpServletRequest request) {String token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {token = token.substring(7);// 解析 Token 获取过期时间Claims claims = JwtUtil.parseToken(token);if (claims != null) {Date expireDate = claims.getExpiration();long expireSeconds = (expireDate.getTime() - System.currentTimeMillis()) / 1000;// 将 Token 加入 Redis 黑名单,直到过期redisTemplate.opsForValue().set("jwt:blacklist:" + token, "invalid", expireSeconds, TimeUnit.SECONDS);}}return Result.success();
}// 拦截器中检查黑名单
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {token = token.substring(7);// 检查黑名单if (redisTemplate.hasKey("jwt:blacklist:" + token)) {response.setStatus(401);return false;}// 验证 TokenClaims claims = JwtUtil.parseToken(token);if (claims == null) {response.setStatus(401);return false;}// 将用户信息存入请求request.setAttribute("userId", claims.getSubject());}return true;
}

十一、实际开发中推荐的 JWT 完整实现流程图

用户登录请求
│
▼
后端验证用户名密码
│
▼
验证成功
│
├─┬─ 生成 AccessToken(含用户 ID、角色等)
│ │
│ ├─ 生成 RefreshToken(与用户 ID 绑定)
│ │
│ └─ 将 RefreshToken 存入 Redis(设置过期时间)
│
▼
返回 AccessToken 和 RefreshToken 给前端
│
▼
前端保存 Token(如 LocalStorage)
│
▼
前端每次请求携带 AccessToken(放在 Header)
│
▼
后端拦截器验证 AccessToken
│
├─ 验证失败 ──┐
│             │
│             ▼
│      返回 401 未授权
│             │
│             ▼
│  前端使用 RefreshToken 请求新 AccessToken
│             │
│             ▼
│  后端验证 RefreshToken
│             │
│     ┌───────┴───────┐
│     │               │
│  有效              无效
│     │               │
│     ▼               ▼
│  生成新 Token     用户重新登录
│     │
│     ▼
返回新 AccessToken

十二、附录:JWT 工具类参考实现

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Component
public class JwtUtil {@Value("${jwt.secret}")private String secret;@Value("${jwt.accessTokenExpireTime}")private long accessTokenExpireTime; // 分钟@Value("${jwt.refreshTokenExpireTime}")private long refreshTokenExpireTime; // 天/*** 生成 Access Token*/public String generateAccessToken(User user) {Map<String, Object> claims = new HashMap<>();claims.put("username", user.getUsername());claims.put("roles", user.getRoles());return generateToken(user.getId().toString(), claims, accessTokenExpireTime * 60 * 1000);}/*** 生成 Refresh Token*/public String generateRefreshToken(User user) {return generateToken(user.getId().toString(), null, refreshTokenExpireTime * 24 * 60 * 60 * 1000);}/*** 生成 Token*/private String generateToken(String subject, Map<String, Object> claims, long expireTime) {Date now = new Date();Date expireDate = new Date(now.getTime() + expireTime);return Jwts.builder().setHeaderParam("typ", "JWT").setClaims(claims).setSubject(subject).setIssuedAt(now).setExpiration(expireDate).signWith(SignatureAlgorithm.HS512, secret).compact();}/*** 解析 Token*/public Claims parseToken(String token) {try {return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();} catch (Exception e) {return null;}}/*** 验证 Token 是否过期*/public boolean isTokenExpired(String token) {Claims claims = parseToken(token);if (claims == null) {return true;}Date expiration = claims.getExpiration();return expiration.before(new Date());}
}

十三、JWT 与 Session 混合使用的实践方案

在一些大型系统中,可能存在不同端口或子系统使用不同的认证方式(如后台管理系统用 Session,移动端用 JWT),这时可以采用 混合认证策略

1. 使用建议

模块建议认证方式
管理后台(单体、Spring MVC)使用 Session(Cookie 自动管理,方便权限控制)
移动端 / 小程序 / H5使用 JWT(无状态认证,跨域安全,适合 API)
多端统一用户体系JWT + Session 混合,并通过 Redis 存储登录状态

2. 核心实现思路

  • 登录成功时,服务端生成 JWT,同时创建一个短 Session,用于后台系统交互;
  • Redis 中统一存储用户 Token 状态,便于管理和失效控制;
  • 拦截器判断请求来源(如是否含 Authorization),自动适配认证方式。

十四、常见问题 FAQ

Q1:JWT 一旦泄露是否会被无限使用?

是的,如果没有设置过期时间或失效机制(如黑名单),Token 会一直有效。所以必须:

  • 设置过期时间;
  • 使用 HTTPS;
  • 实现登出逻辑(Redis 黑名单);

Q2:JWT 是不是比 Session 更安全?

不完全正确。JWT 只是在无状态架构下更合适,但它暴露信息在客户端,若加密不严密,反而更容易被篡改或伪造。而 Session 只存在服务端,反而更隐蔽和安全。

Q3:JWT 必须存放在 LocalStorage 吗?

不一定。也可以存放在:

  • LocalStorage(常见方式,刷新页面不丢失);
  • SessionStorage(更安全,但刷新页面会清空);
  • HttpOnly Cookie(防 XSS,但不支持 JS 访问,需配合后端设置跨域 Cookie);

十六、推荐学习与实践路径

1. 学会使用 Spring MVC + Session 登录机制

在 Spring MVC 项目中,Session 登录是一种传统且有效的认证方式。
登录接口实现
通过 HttpSession 对象将用户信息存入 Session:

@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password, HttpSession session) {User user = userService.login(username, password);if (user != null) {session.setAttribute("user", user); // 存入 Sessionreturn "redirect:/home"; // 登录成功跳转}return "login"; // 登录失败返回登录页
}

拦截器验证登录状态
编写拦截器检查 Session 中是否存在用户信息:

@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession();User user = (User) session.getAttribute("user");if (user == null) { // 未登录则重定向到登录页response.sendRedirect("/login");return false;}return true; // 已登录,允许访问}
}

注册拦截器
在 Spring MVC 配置类中指定拦截路径(如 /api/**)和排除路径(如 /login):

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/api/**") // 拦截所有 API 接口.excludePathPatterns("/login", "/static/**"); // 排除登录页和静态资源}
}

2. 掌握前后端分离项目中 JWT 的基本使用方式

JWT 工具类实现
生成和解析 Token,包含过期时间和签名加密:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;public class JwtUtil {private static final String SECRET = "your_secret_key_keep_it_secure"; // 密钥(需保密)private static final long EXPIRATION_TIME = 3600000; // 1小时(毫秒)// 生成 Tokenpublic static String generateToken(String username) {Claims claims = Jwts.claims().setSubject(username); // 载荷存储用户标识Date now = new Date();Date expiration = new Date(now.getTime() + EXPIRATION_TIME); // 设置过期时间return Jwts.builder().setClaims(claims).setIssuedAt(now).setExpiration(expiration).signWith(SignatureAlgorithm.HS256, SECRET) // HS256 签名算法.compact();}// 解析 Tokenpublic static Claims parseToken(String token) {try {return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); // 成功解析返回载荷} catch (Exception e) {return null; // 解析失败返回 null}}
}

登录接口返回 Token
验证用户信息后生成 Token 并返回给前端:

@PostMapping("/login")
public ResponseEntity<Map<String, String>> login(@RequestBody UserLoginRequest request) {User user = userService.authenticate(request.getUsername(), request.getPassword());if (user != null) {String token = JwtUtil.generateToken(user.getUsername());Map<String, String> result = new HashMap<>();result.put("token", token);return ResponseEntity.ok(result); // 返回 Token}return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null); // 认证失败
}

后端验证 Token
通过拦截器从请求头中获取 Token 并解析验证:

public class JwtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {token = token.substring(7); // 去除 "Bearer " 前缀Claims claims = JwtUtil.parseToken(token);if (claims != null) {// 解析成功,将用户信息存入请求(如用户 ID、角色)request.setAttribute("userId", claims.getSubject());return true;}}response.setStatus(HttpStatus.UNAUTHORIZED); // 未认证或 Token 无效return false;}
}

3. 实现 Token 过期 + 刷新机制

双 Token 设计

  • AccessToken:短有效期(如 1 小时),用于接口认证;
  • RefreshToken:长有效期(如 7 天),用于刷新 AccessToken。

登录时返回双 Token

@PostMapping("/login")
public ResponseEntity<Map<String, String>> login(@RequestBody UserLoginRequest request) {User user = userService.authenticate(request.getUsername(), request.getPassword());if (user != null) {String accessToken = JwtUtil.generateToken(user.getUsername(), "access"); // 短过期String refreshToken = JwtUtil.generateToken(user.getUsername(), "refresh"); // 长过期Map<String, String> tokens = new HashMap<>();tokens.put("accessToken", accessToken);tokens.put("refreshToken", refreshToken);return ResponseEntity.ok(tokens);}return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
}

刷新 Token 接口
使用 RefreshToken 生成新的 AccessToken:

@PostMapping("/refresh-token")
public ResponseEntity<Map<String, String>> refreshToken(@RequestHeader("Refresh-Token") String refreshToken) {Claims claims = JwtUtil.parseToken(refreshToken);if (claims != null && "refresh".equals(claims.get("type"))) { // 验证 Token 类型String username = claims.getSubject();String newAccessToken = JwtUtil.generateToken(username, "access"); // 生成新 AccessTokenMap<String, String> result = new HashMap<>();result.put("accessToken", newAccessToken);return ResponseEntity.ok(result);}return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
}

在这里插入图片描述

4. 实现 Redis 黑名单、登录限制、踢出机制

Redis 黑名单
用户登出时将 Token 加入 Redis 黑名单,设置与 Token 剩余有效期一致的过期时间:

@Autowired
private RedisTemplate<String, Object> redisTemplate;// 登出逻辑
public void logout(String token) {Claims claims = JwtUtil.parseToken(token);if (claims != null) {long expireTime = claims.getExpiration().getTime() - System.currentTimeMillis();if (expireTime > 0) {redisTemplate.opsForValue().set("blacklist:" + token, "invalid", expireTime, TimeUnit.MILLISECONDS);}}
}// 拦截器中检查黑名单
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String token = request.getHeader("Authorization");if (redisTemplate.hasKey("blacklist:" + token)) { // 存在于黑名单则拒绝访问response.setStatus(HttpStatus.UNAUTHORIZED);return false;}// 其他验证逻辑...return true;
}

登录频率限制
使用 Redis 记录用户登录次数,限制每分钟最多 5 次尝试:

public boolean checkLoginFrequency(String username) {String key = "login:attempts:" + username;Integer count = (Integer) redisTemplate.opsForValue().get(key);if (count != null && count >= 5) { // 超过限制return false; // 禁止登录}// 次数加 1,设置过期时间 1 分钟redisTemplate.opsForValue().increment(key, 1);redisTemplate.expire(key, 1, TimeUnit.MINUTES);return true;
}

管理员踢出用户
将用户所有有效 Token 加入黑名单(需结合用户 ID 批量操作):

public void kickUser(String userId) {// 查询用户所有有效 Token(需业务系统存储 Token 与用户的映射)List<String> tokens = tokenRepository.findByUserId(userId);tokens.forEach(token -> logout(token)); // 批量加入黑名单
}

5. 掌握 Spring Security 对 JWT 的整合

自定义 JWT 认证过滤器
继承 OncePerRequestFilter,解析 Token 并设置安全上下文:

public class JwtAuthenticationFilter extends OncePerRequestFilter {private final JwtUtil jwtUtil;public JwtAuthenticationFilter(JwtUtil jwtUtil) {this.jwtUtil = jwtUtil;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {token = token.substring(7);Claims claims = jwtUtil.parseToken(token);if (claims != null) {String username = claims.getSubject();// 构建认证对象(可添加角色权限)Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));SecurityContextHolder.getContext().setAuthentication(authentication); // 设置安全上下文}}filterChain.doFilter(request, response); // 继续执行后续过滤器}
}

Spring Security 配置
禁用 CSRF,添加 JWT 过滤器并配置权限规则:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {private final JwtUtil jwtUtil;public SecurityConfig(JwtUtil jwtUtil) {this.jwtUtil = jwtUtil;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable() // 前后端分离场景禁用 CSRF.authorizeRequests().antMatchers(HttpMethod.POST, "/login").permitAll() // 允许登录接口.antMatchers("/admin/**").hasRole("ADMIN") // 管理员接口需 ADMIN 角色.anyRequest().authenticated() // 其他接口需认证.and().addFilterBefore(new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class); // 添加 JWT 过滤器}
}

通过 Spring Security 的整合,可更便捷地实现细粒度权限控制(如 @PreAuthorize 注解)和安全策略管理。

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

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

相关文章

Vue中的 VueComponent

VueComponent 组件的本质 Vue 组件是一个可复用的 Vue 实例。每个组件本质上就是通过 Vue.extend() 创建的构造函数&#xff0c;或者在 Vue 3 中是由函数式 API&#xff08;Composition API&#xff09;创建的。 // Vue 2 const MyComponent Vue.extend({template: <div…

使用 FFmpeg 将视频转换为高质量 GIF(保留原始尺寸和帧率)

在制作教程动图、产品展示、前端 UI 演示等场景中,我们经常需要将视频转换为体积合适且清晰的 GIF 动图。本文将详细介绍如何使用 FFmpeg 工具将视频转为高质量 GIF,包括: ✅ 保留原视频尺寸或自定义缩放✅ 保留原始帧率或自定义帧率✅ 使用调色板优化色彩质量✅ 降低体积同…

【自然语言处理与大模型】大模型Agent四大的组件

大模型Agent是基于大型语言模型构建的智能体&#xff0c;它们能够模拟独立思考过程&#xff0c;灵活调用各类工具&#xff0c;逐步达成预设目标。这类智能体的设计旨在通过感知、思考与行动三者的紧密结合来完成复杂任务。下面将从大模型大脑&#xff08;LLM&#xff09;、规划…

《软件工程》第 11 章 - 结构化软件开发

结构化软件开发是一种传统且经典的软件开发方法&#xff0c;它强调将软件系统分解为多个独立的模块&#xff0c;通过数据流和控制流来描述系统的行为。本章将结合 Java 代码示例、可视化图表&#xff0c;深入讲解面向数据流的分析与设计方法以及实时系统设计的相关内容。 11.1 …

初步尝试AI应用开发平台——Dify的本地部署和应用开发

随着大语言模型LLM和相关应用的流行&#xff0c;在本地部署并构建知识库&#xff0c;结合企业的行业经验或个人的知识积累进行定制化开发&#xff0c;是LLM的一个重点发展方向&#xff0c;在此方向上也涌现出了众多软件框架和工具集&#xff0c;Dify就是其中广受关注的一款&…

高阶数据结构——哈希表的实现

目录 1.概念引入 2.哈希的概念&#xff1a; 2.1 什么叫映射&#xff1f; 2.2 直接定址法 2.3 哈希冲突&#xff08;哈希碰撞&#xff09; 2.4 负载因子 2.5 哈希函数 2.5.1 除法散列法&#xff08;除留余数法&#xff09; 2.5.2 乘法散列法&#xff08;了解&#xff09…

7.安卓逆向2-frida hook技术-介绍

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取码&#xff1…

DB-GPT扩展自定义Agent配置说明

简介 文章主要介绍了如何扩展一个自定义Agent&#xff0c;这里是用官方提供的总结摘要的Agent做了个示例&#xff0c;先给大家看下显示效果 代码目录 博主将代码放在core目录了&#xff0c;后续经过对源码的解读感觉放在dbgpt_serve.agent.agents.expand目录下可能更合适&…

Android 架构演进之路:从 MVC 到 MVI,拥抱单向数据流的革命

在移动应用开发的世界里&#xff0c;架构模式的演进从未停歇。从早期的 MVC 到后来的 MVP、MVVM&#xff0c;每一次变革都在尝试解决前一代架构的痛点。而今天&#xff0c;我们将探讨一种全新的架构模式 ——MVI&#xff08;Model-View-Intent&#xff09;&#xff0c;它借鉴了…

【YOLOv8-pose部署至RK3588】模型训练→转换RKNN→开发板部署

已在GitHub开源与本博客同步的YOLOv8_RK3588_object_pose 项目&#xff0c;地址&#xff1a;https://github.com/A7bert777/YOLOv8_RK3588_object_pose 详细使用教程&#xff0c;可参考README.md或参考本博客第六章 模型部署 文章目录 一、项目回顾二、文件梳理三、YOLOv8-pose…

集成30+办公功能的实用工具

软件介绍 本文介绍的软件是千峰办公助手。 软件功能概述与开发目的 千峰办公助手集成了自动任务、系统工具、文件工具、PDF工具、OCR图文识别、文字处理、电子表格七个模块&#xff0c;拥有30余项实用功能。作者开发该软件的目的是解决常见办公痛点&#xff0c;把机械操作交…

IDEA启动报错:Cannot invoke “org.flowable.common.engine.impl.persistence.ent

1.问题 项目启动报错信息 java.lang.NullPointerException: Cannot invoke "org.flowable.common.engine.impl.persistence.ent 2.问题解析 出现这个问题是在项目中集成了Flowable或Activiti工作流&#xff0c;开启自动创建工作流创建的表&#xff0c;因为不同环境的数据…

网络安全--PHP第三天

今天学习文件上传的相关知识 上传的前端页面如下 upload.html <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"&g…

【愚公系列】《生产线数字化设计与仿真》004-颜色分类站仿真(基础概念)

🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! 👉 江湖人称"愚公搬代码",用七年如一日的精神深耕技术领域,以"…

基于 uni-app + <movable-view>拖拽实现的标签排序-适用于微信小程序、H5等多端

在实际业务中&#xff0c;我们经常遇到「标签排序」或「菜单调整」的场景。微信小程序原生的 movable-view 为我们提供了一个简单、高效的拖拽能力&#xff0c;结合 Vue3 uni-app 的组合&#xff0c;我们可以实现一个体验良好的标签管理界面。 核心组件&#xff1a;<movab…

一些较好的学习方法

1、网上有一些非常经典的电路&#xff0c;而且有很多视频博主做了详细的讲解。 2、有一部分拆解的UP主&#xff0c;拆解后会还原该器件的原理图&#xff0c;并一步步做讲解。 3、有两本书&#xff0c;数电、模电&#xff0c;这两本书中的内容很多都值得学习。 5、某宝上卖的…

《1.1_4计算机网络的分类|精讲篇|附X-mind思维导图》

网络相关知识 按使用范围分类 公用网 由电信部门或其他提供通信服务的经营部门组建、管理和控制&#xff0c;向全社会提供服务的网络。 专用网 由某个单位或部门组建、仅供本单位或部门内部使用的网络。 按传输介质分类 有线网络 如&#xff1a;双绞线、同轴电缆、光纤…

Git 和 GitHub 学习指南本地 Git 配置、基础命令、GitHub 上传流程、企业开发中 Git 的使用流程、以及如何将代码部署到生产服务器

Windows 上 Git 安装与配置 下载安装&#xff1a;访问 Git 官方网站下载适用于 Windows 的安装程序。运行安装包时会出现许可协议、安装目录、组件选择等界面&#xff08;如下图&#xff09;。在“Select Components”页面建议勾选 Git Bash Here 等选项&#xff0c;以便在资源…

航空航天领域对滚珠丝杆的精度要求有多高?

航空航天领域对滚珠丝杆的精度要求非常高&#xff0c;尤其是飞行器、火箭和卫星等载具的导航和定位系统都需要高精度的滚珠丝杆&#xff0c;以确保高精度的位置控制和稳定的导航性能。那么&#xff0c;航空航天领域对滚珠丝杆的精度要求有多高&#xff1f; 1、定位精度&#xf…

技术篇-2.5.Matlab应用场景及开发工具安装

Matlab 在数学建模和数值分析等领域具有无可替代的地位。它几乎涵盖所有常见数学算法的内置函数库&#xff0c;使得从数据预处理、方程求解到优化算法的实现&#xff0c;无需编写大量底层代码即可快速完成&#xff1b;同时&#xff0c;Matlab 强大的可视化能力&#xff0c;可以…