JJWT 0.12.x实战:从零构建安全登录系统与JwtUtils工具类优化

Spring Boot项目实战:用JJWT 0.11.5搞定用户登录与API鉴权(附完整代码) 本文详细介绍了如何在Spring Boot 3.x项目中利用JJWT 0.11.5实现企业级JWT认证体系,涵盖密钥管理、令牌签发、接口鉴权等核心功能。通过实战代码演示了JWT工具类封装、认证过滤器实现以及令牌刷新机制,帮助开发者构建安全高效的API认证方案,适用于微服务架构下的分布式系统。 阅读详情

1. 为什么我们需要JWT?从登录痛点说起

大家好,我是老张,在前后端分离的项目里摸爬滚打了十来年。不知道你有没有遇到过这样的场景:用户登录后,你需要在服务端维护一个Session,每次请求都得去查一下这个Session是否有效。用户量一上来,服务器内存就告急,而且一旦要做集群部署,Session共享又是个让人头疼的大问题。更别提移动端或者跨域请求了,Cookie那一套玩不转。

这时候,JWT(JSON Web Token)就该登场了。你可以把它想象成一张“数字身份证”。用户第一次登录(好比去办证中心),服务器核实了你的身份(用户名密码正确),就会给你签发这张“身份证”。这张身份证上,用加密的方式写明了你是谁(用户ID)、什么时候办的(签发时间)、什么时候过期(有效时间)。之后,你每次去办事(访问其他接口),只需要出示这张身份证就行,服务器一看签名,就知道这是它自己发的、没被伪造过,直接读取上面的信息就知道你是谁,根本不用再去翻它的“户口本”(数据库或Session存储)。

这带来的好处是巨大的:服务端彻底无状态了。它不用再记住谁来过,压力瞬间减轻。无论是扩容成十台服务器,还是对接小程序、APP,这张“身份证”都能畅通无阻。我们今天要聊的,就是如何用Java领域最流行的库——JJWT的最新版0.12.x,亲手打造这套安全、高效的“数字身份证”系统,并封装一个你拿到手就能用的、经过优化的JwtUtils工具类。

2. JJWT 0.12.x:你必须知道的巨变

如果你之前用过JJWT,比如0.9.x甚至更老的版本,那么我要给你提个醒:0.12.x是一个颠覆性的更新。很多网上流传的“祖传代码”直接拿来用会报错,因为API设计理念发生了根本变化。我刚开始迁移的时候也踩了不少坑,但用熟之后发现,新API其实更安全、更清晰。

最大的变化在于密钥(Key)的处理。在老版本里,你可能会直接用字符串secret.getBytes(),然后配合SignatureAlgorithm.HS256来签名。但在0.12.x里,这个做法被彻底废弃了。新版本强制要求你使用io.jsonwebtoken.security.Keys工具类来生成一个标准的java.security.Key对象。这么做的好处是,它强制开发者使用符合密码学规范的密钥长度,避免了因密钥太弱而导致的安全风险。

举个例子,HS256算法要求密钥长度至少达到256位(32字节)。如果你随便用一个短字符串“123456”,老版本可能就让你过了,但新版的Keys.hmacShaKeyFor()方法会帮你检查,并确保生成的密钥是足够强壮的。这就像从“手工打造一把可能生锈的钥匙”,升级到了“必须使用国家认证的保险柜锁芯”,安全性上了一个大台阶。

另一个显著变化是API的链式调用更加流畅。生成Token的Jwts.builder()和解析Token的Jwts.parser(),其方法命名和调用顺序都更加直观。解析后返回的对象也从直接的Claims变成了Jws<Claims>,这其实是一个更严谨的封装,Jws代表了“已签名的JWT”,你从中可以分别获取头部、载荷和签名信息,虽然我们最常用的还是载荷(Payload)里的数据。

3. 从零开始:项目环境与依赖配置

光说不练假把式,我们直接上手。首先,创建一个Spring Boot项目,这个大家应该都很熟了。然后,打开你的pom.xml文件,添加JJWT的依赖。

这里有个关键点:一定要去Maven中央仓库确认最新版本。我写这篇文章时,最新稳定版是0.12.5。依赖项长这样:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId> <!-- 如果你用Jackson处理JSON -->
    <version>0.12.5</version>
    <scope>runtime</scope>
</dependency>

看到没?和以前一个jjwt包打天下不同,现在它分成了三个模块。jjwt-api提供了所有你在编译时需要的接口和类;jjwt-impl是默认的实现,在运行时才需要;jjwt-jackson是负责JSON序列化和反序列化的处理器(默认就是用它)。这种拆分让依赖更清晰,也给了未来替换底层JSON库的可能性。

依赖加好后,我们还需要一个地方来统一管理JWT的配置,比如密钥和过期时间。我强烈推荐使用Spring Boot的@ConfigurationProperties,把配置写到application.yml里,清晰又容易维护。

我们先创建一个配置类JwtProperties:

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "jwt") // 前缀定为jwt
public class JwtProperties {
    /**
     * 用于生成和验证Token的密钥。
     * 务必保证足够复杂且长度足够(建议32位以上字符),并严格保密。
     */
    private String secretKey;

    /**
     * Token的有效时间,单位是毫秒。
     * 例如7200000毫秒 = 2小时。
     */
    private Long ttl;

    /**
     * 前端传递Token时使用的请求头名称。
     * 例如:Authorization
     */
    private String tokenHeader;
}

然后在application.yml中配置:

jwt:
  secret-key: your-super-long-and-ultra-secure-secret-key-at-least-32-chars-here
  ttl: 7200000 # 2小时
  token-header: Authorization

注意那个secret-key,我强烈建议你使用一个非常长的、无规律的字符串,可以用一些随机字符串生成器来创建。千万别用“123456”、“admin”这种,那等于把家门钥匙放在地毯下面。

3.1 关于密钥安全的最佳实践

这里多聊两句密钥安全。在实际生产环境中,直接把密钥明文写在配置文件里是有风险的。更安全的做法是:

  1. 环境变量:将密钥设置为服务器的环境变量,在代码中通过System.getenv("JWT_SECRET")读取。
  2. 配置中心:使用Spring Cloud Config、Apollo、Nacos等配置中心管理,实现动态更新和加密存储。
  3. 密钥轮换:定期更换密钥。这意味着新旧Token会有一段时间同时有效,需要在验证逻辑里做兼容,复杂度较高,但对安全性要求极高的系统是必要的。

对于我们当前的学习和中小型项目,放在配置文件里并确保仓库中不提交真实的application.yml(用application.yml.example代替)是一个可行的起点。

4. 核心武器:手把手打造JwtUtils工具类

工具类的好处是“一次编写,到处运行”,把复杂的JJWT API封装成简单的一两个方法,让业务代码清爽无比。下面这个JwtUtils是我在多个项目中打磨出来的,直接复制粘贴就能用,并且已经适配了JJWT 0.12.x。

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

@Slf4j
@Component
public class JwtUtils {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 生成JWT Token
     *
     * @param claims 自定义载荷内容(存放用户ID、用户名等)
     * @return 加密后的Token字符串
     */
    public String generateToken(Map<String, Object> claims) {
        // 1. 计算过期时间点
        long expirationMillis = System.currentTimeMillis() + jwtProperties.getTtl();
        Date expirationDate = new Date(expirationMillis);

        // 2. 根据配置的密钥字符串,生成安全的HMAC密钥对象
        // 这是0.12.x的核心变化!不再直接使用字符串字节数组。
        SecretKey key = Keys.hmacShaKeyFor(jwtProperties.getSecretKey().getBytes(StandardCharsets.UTF_8));

        // 3. 使用链式调用构建JWT
        return Jwts.builder()
                .claims(claims) // 设置自定义载荷(claims)
                .expiration(expirationDate) // 设置过期时间
                .issuedAt(new Date()) // 设置签发时间(iat claim)
                .signWith(key) // 设置签名算法和密钥
                .compact(); // 生成最终的字符串
    }

    /**
     * 解析并验证JWT Token
     *
     * @param token 客户端传来的Token字符串
     * @return 载荷(Claims)对象,从中可获取存放的数据
     * @throws JwtException 如果Token过期、签名无效、格式错误等,会抛出此异常
     */
    public Claims parseToken(String token) throws JwtException {
        // 1. 同样,先根据密钥生成Key对象
        SecretKey key = Keys.hmacShaKeyFor(jwtProperties.getSecretKey().getBytes(StandardCharsets.UTF_8));

        // 2. 构建解析器,并设置验证所需的密钥
        JwtParser parser = Jwts.parser()
                .verifyWith(key) // 设置验证密钥
                .build();

        // 3. 解析Token,得到一个已签名的JWT对象(Jws)
        Jws<Claims> jws = parser.parseSignedClaims(token);

        // 4. 返回载荷部分
        return jws.getPayload();
    }

    /**
     * 从Token中获取指定的Claims值(简化操作)
     *
     * @param token Token字符串
     * @param claimKey 要获取的Claims键名
     * @return 对应的值,如果不存在或解析失败则返回null
     */
    public Object getClaimFromToken(String token, String claimKey) {
        try {
            Claims claims = parseToken(token);
            return claims.get(claimKey);
        } catch (JwtException e) {
            log.warn("从Token解析Claim失败: {}, claimKey: {}", e.getMessage(), claimKey);
            return null;
        }
    }

    /**
     * 验证Token是否有效(未过期且签名正确)
     *
     * @param token Token字符串
     * @return 有效返回true,否则返回false
     */
    public boolean validateToken(String token) {
        try {
            parseToken(token); // 如果解析不抛异常,说明Token有效
            return true;
        } catch (JwtException e) {
            log.debug("Token验证失败: {}", e.getMessage());
            return false;
        }
    }
}

我来解释一下这个工具类的几个优化点:

  1. 自动注入配置:工具类本身是一个Spring Component,直接注入JwtProperties,不用每次调用方法都传密钥和过期时间。
  2. 清晰的异常处理:parseToken方法会抛出JwtException。它的子类很丰富,比如ExpiredJwtException(过期)、MalformedJwtException(格式错误)、SignatureException(签名无效)。我们在上层(如拦截器)可以根据具体异常类型给前端返回更精确的错误码。
  3. 便捷方法:提供了getClaimFromToken和validateToken这两个非常常用的方法,让业务代码写起来更顺手。
  4. 日志记录:使用@Slf4j记录了必要的警告和调试信息,方便出问题时排查。

5. 实战登录与令牌签发

工具类准备好了,我们来看看怎么在登录流程里用它。假设我们有一个非常简单的UserController。

首先,定义一个登录的DTO(数据传输对象):

@Data
public class LoginDTO {
    private String username;
    private String password;
}

然后,在Controller里处理登录请求:

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private JwtUtils jwtUtils; // 注入我们的工具类
    @Autowired
    private UserService userService; // 假设有一个服务来验证用户

    @PostMapping("/login")
    public Result login(@RequestBody LoginDTO loginDTO) {
        // 1. 验证用户名密码(这里简化了,实际应从数据库查询比对)
        User user = userService.authenticate(loginDTO.getUsername(), loginDTO.getPassword());
        if (user == null) {
            return Result.error("用户名或密码错误");
        }

        // 2. 准备存放在Token里的信息(Payload)
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getId()); // 用户唯一标识
        claims.put("username", user.getUsername());
        claims.put("role", user.getRole()); // 用户角色,用于后续权限控制
        // 注意:不要存放密码等敏感信息!

        // 3. 调用工具类生成JWT Token
        String token = jwtUtils.generateToken(claims);

        // 4. 构造返回结果
        Map<String, String> data = new HashMap<>();
        data.put("token", token);
        data.put("tokenType", "Bearer"); // 约定好的Token类型
        data.put("expiresIn", String.valueOf(jwtProperties.getTtl() / 1000)); // 过期时间(秒)

        return Result.success("登录成功", data);
    }
}

这样,用户登录成功后,前端就会收到一个类似这样的响应:

{
  "code": 200,
  "msg": "登录成功",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEyMywidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsInJvbGUiOiJBRE1JTiIsImV4cCI6MTcxMjM0NTYwMCwiaWF0IjoxNzEyMzQyMDAwfQ.some-long-signature-here",
    "tokenType": "Bearer",
    "expiresIn": "7200"
  }
}

前端需要将这个token保存起来,通常放在localStorage、sessionStorage或者Vuex/Pinia(Vue)、Redux(React)的状态管理里。并在后续的每一次请求中,将其放入HTTP请求的Header中。这就是我们之前在配置里定义的Authorization头,格式为:Authorization: Bearer <你的token>。

6. 守卫大门:实现JWT认证拦截器

Token发出去了,接下来的问题就是:其他需要认证的接口,如何验证这个Token?我们不可能在每个Controller方法里都写一遍解析代码。这时,Spring的拦截器(Interceptor)就派上用场了。

我们创建一个JwtAuthenticationInterceptor:

@Component
public class JwtAuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtUtils jwtUtils;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 检查请求方法,OPTIONS预检请求直接放行
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            return true;
        }

        // 2. 从请求头中获取Token
        String authHeader = request.getHeader(jwtProperties.getTokenHeader()); // 例如 "Authorization"
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            sendError(response, HttpStatus.UNAUTHORIZED.value(), "缺少有效的认证Token");
            return false;
        }

        // 3. 提取Token(去掉"Bearer "前缀)
        String token = authHeader.substring(7);

        // 4. 验证并解析Token
        try {
            Claims claims = jwtUtils.parseToken(token);
            // 5. 将解析出的用户信息存入请求上下文,供后续Controller使用
            // 通常我们会把userId、username等关键信息放进去
            String userId = claims.get("userId", String.class);
            request.setAttribute("currentUserId", userId);
            // 也可以放入Spring Security的SecurityContextHolder,这里简化处理
            return true;
        } catch (ExpiredJwtException e) {
            sendError(response, HttpStatus.UNAUTHORIZED.value(), "登录凭证已过期,请重新登录");
            return false;
        } catch (JwtException e) {
            // 捕获其他所有JWT异常(签名无效、格式错误等)
            sendError(response, HttpStatus.UNAUTHORIZED.value(), "无效的登录凭证");
            return false;
        }
    }

    private void sendError(HttpServletResponse response, int status, String msg) throws IOException {
        response.setStatus(status);
        response.setContentType("application/json;charset=UTF-8");
        Result result = Result.error(status, msg);
        response.getWriter().write(new ObjectMapper().writeValueAsString(result));
    }
}

拦截器写好了,还得把它注册到Spring MVC的配置里:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private JwtAuthenticationInterceptor jwtAuthenticationInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtAuthenticationInterceptor)
                .addPathPatterns("/api/**") // 拦截所有/api开头的请求
                .excludePathPatterns(
                        "/api/auth/login",
                        "/api/auth/register",
                        "/doc.html", // 放行Swagger/knife4j文档
                        "/webjars/**",
                        "/swagger-resources/**"
                ); // 排除登录、注册等公开接口
    }
}

现在,整个流程就串起来了。用户访问受保护的/api/user/profile接口时,请求会先经过拦截器。拦截器检查Header里的Token,验证通过就把用户ID塞到请求属性里,然后放行。在Controller里,你就可以直接取出来用了:

@GetMapping("/api/user/profile")
public Result getProfile(HttpServletRequest request) {
    String currentUserId = (String) request.getAttribute("currentUserId");
    // 根据currentUserId去数据库查询用户详情...
    return Result.success(userProfile);
}

7. 避坑指南:0.12.x迁移与常见问题

在我从老版本迁移到0.12.x的过程中,遇到了几个典型的“坑”,这里分享给你,希望能帮你节省时间。

第一个大坑:signWith方法参数巨变。 老版本(0.9.x)你可能这样写:.signWith(SignatureAlgorithm.HS256, secretKey.getBytes())。 在新版本(0.12.x),必须分两步:先用Keys.hmacShaKeyFor生成SecretKey对象,再调用.signWith(key)。而且signWith不再接受算法参数,算法由密钥类型自动推断。

第二个坑:setSigningKey已废弃。 解析Token时,老版本用.setSigningKey(secret)。新版本统一改为.verifyWith(key),语义更准确,就是“用这个密钥去验证签名”。

第三个坑:parseClaimsJws方法名变更。 老版本解析签名的JWT用parseClaimsJws。新版本改为parseSignedClaims,返回Jws<Claims>对象。你需要调用jws.getPayload()来获取Claims。

第四个坑:自动验证过期时间。 这是一个好的变化!在老版本,即使Token过期了,如果你只是解析而不手动检查exp字段,程序可能不会报错。但在0.12.x,只要你调用了parser.parseSignedClaims(token),如果Token过期,它会自动抛出ExpiredJwtException,安全性更高了。所以你的异常处理必须考虑到这一点。

第五个注意点:时钟偏差(Clock Skew)。 有时候服务器之间、服务器与客户端之间可能存在几秒钟的时间差。一个刚刚过期的Token,在另一台服务器看来可能还没过期。JJWT提供了容忍度设置:

JwtParser parser = Jwts.parser()
    .verifyWith(key)
    .clockSkewSeconds(60) // 允许60秒的时钟偏差
    .build();

这样,即使Token过期了60秒以内,仍然会被认为是有效的。这个值需要根据你的业务场景谨慎设置。

8. 进阶思考:安全、性能与无状态之踵

JWT用起来很爽,但它也不是银弹,有几个问题需要我们深入思考。

1. 令牌的“失效”问题 这是JWT被问得最多的一点:用户退出登录,或者管理员禁用了某个用户,如何让他的Token立刻失效?因为服务端不存储状态,它无法主动作废一个已签发的、尚未过期的Token。 我常用的解决方案是引入一个短期的黑名单或Token版本号。

  • 黑名单:用户退出或禁用时,将该Token的唯一标识(JWT里的jti字段)或剩余有效时间较长的Token本身,存入Redis,并设置一个较短的过期时间(比如5分钟)。在拦截器验证Token时,增加一步检查Redis黑名单。这只适用于需要“立即失效”的高安全场景,且会增加一点复杂度和Redis开销。
  • Token版本号:在用户表中增加一个tokenVersion字段。生成JWT时,把版本号也放进Payload。当用户退出或密码修改时,递增这个版本号。验证Token时,不仅验证签名和过期时间,还要从数据库或缓存中取出用户当前的tokenVersion,与Token里的对比,不一致则拒绝。这是一个折中的方案。

2. 不要往Payload里塞太多东西 JWT的Payload内容虽然可以自定义,但它是Base64编码的,任何人拿到Token都可以解码看到内容(除非你用JWE加密,但那会复杂很多)。所以,绝对不能存放密码、手机号、身份证号等敏感信息。只存放必要的、非敏感的用户标识和授权信息,比如userId、username、role。

3. 性能考量 每次请求都要进行签名验证,这是一个密码学操作,虽然HS256很快,但在超高并发下仍可能有压力。可以考虑:

  • 将JwtUtils中的SecretKey对象缓存起来,避免每次验证都重新生成。
  • 对于解析出的Claims,如果后续多个地方要用,可以将其存入请求属性,避免在拦截器和Controller里重复解析。

4. 关于“无状态”的再认识 JWT让服务端无状态,这既是优点也是限制。它意味着你无法方便地实现“强制某用户下线”、“查看当前在线用户”等功能。如果你的业务强烈需要这些功能,那么纯粹的JWT可能不是最佳选择,可能需要结合服务端的会话存储,或者考虑OAuth 2.0等更复杂的协议。

最后,工具类虽好,但一定要理解其背后的原理和安全考量。我提供的这个JwtUtils是一个坚实的起点,你可以根据自己项目的实际情况,添加令牌刷新机制、多端登录管理(为APP、Web端颁发不同有效期的Token)、或者集成Spring Security等更完整的安全框架。安全无小事,每一步都值得仔细推敲。

SpringSecurity与JWT构建RBAC权限认证实战 在现代分布式系统中,认证授权是保障服务安全的核心机制。JWT(JSON Web Token)作为一种无状态的令牌技术,通过数字签名实现安全的身份验证,相比传统Session方案更适应微服务架构。其核心原理是将用户信息编码为JSON对象,通过Header.Payload.Signature三段式结构确保数据完整性。结合SpringSecurity框架,可以快速实现基于角色的访问控制(RBAC),典型应用场景包括前后端分离系统的权限管理、跨服务调用的身份传递等。实战中需特别注意Token过期策略、权限拦截顺序等 阅读详情

相关推荐

Spring Boot+Vue音乐网站实战:从架构到部署的全栈开发指南

在实际 Java Web 项目中,一个完整的音乐网站涉及的技术栈远比简单的增删改查复杂。它需要处理音频文件的存储与播放、用户交互的实时性、前后端数据的分离与协作,以及后台管理的便捷性。对于希望深入理解 Spring Boot 和 Vue 如何协同工作,并构建一个具备完整业务闭环项目的开发者来说,一个前后端分离的音乐网站是一个绝佳的实战选择。本文将围绕一个典型的 Spring Boot + Vue 音乐网站项目,从零开始,手把手带你理解其核心架构、搭建开发环境、配置关键模块、实现核心功能,并最终部署运行。无论

chulilang8944的博客 451

SpringBoot 3.x + JWT 登录校验实战:拦截器 vs 过滤器,3种方案性能对比

本文深入对比了SpringBoot 3.x项目中JWT登录校验的三种实现方案:过滤器、拦截器和Spring Security,通过性能测试数据展示各方案的优缺点。过滤器方案性能最优,适合高性能场景;拦截器适合需要Spring集成的应用;Spring Security提供完整安全体系但性能开销较大。文章还提供了技术选型决策树和优化建议,帮助开发者根据实际需求选择最佳方案。

weixin_30407613的博客 450

SpringBoot+Vue+MySQL花店销售系统:毕设项目完整设计与实现解析

在Java后端开发领域,SpringBoot作为主流的企业级框架,以其简化配置和快速集成的特性成为众多项目的首选。而Vue作为前端渐进式框架,通过组件化开发与前后端分离架构,让现代Web应用的构建更加高效。关系型数据库MySQL则保证了结构化数据的稳定存储与事务一致性。当这三者结合,便能构建出覆盖商品管理、购物车、订单流转、库存扣减与销售统计的完整电商业务闭环。这种轻量级电商系统不仅能清晰展示增删改查、接口设计、权限控制等核心技术点,也具备良好的工程实践价值。对于正在准备毕业设计或想提升项目经验的开发者而言

weixin_34384557的博客 411

【SpringBoot篇】登录校验 — JWT令牌的实战进阶:从生成到失效的全链路设计

本文深入探讨了SpringBoot中JWT令牌的全链路设计与实现,从基础原理到生产级安全策略。详细介绍了令牌生成、校验、存储及黑名单机制,并提供了微服务架构下的高可用解决方案。通过实战案例分享,帮助开发者规避常见问题,构建高效安全的身份验证系统。

weixin_33725270的博客 343

基于Spring Boot+Vue的智慧食堂平台设计与部署全流程解析

在前后端分离架构日益普及的今天,Java Web项目的开发早已从单纯的CRUD转向工程化、体系化的综合实践。Spring Boot作为后端领域的主流框架,凭借自动装配与生态整合能力简化了服务端开发;Vue则以前端组件化和响应式数据流提升了交互体验。而数据库设计更是整个系统的地基,合理的表结构、字段约束与索引策略直接决定业务逻辑的清晰度和数据一致性。此类技术组合广泛适用于餐饮管理、企业OA、校园服务等信息化场景,既考验开发者对分层架构、接口规范、状态管理的理解,也涉及从本地联调到线上部署的全链路落地能力。围绕

weixin_33951761的博客 326

SpringBootWeb案例(结合Mybatis)

前面我们已经完了基础的分页查询,大家会发现:分页查询功能编写起来比较繁琐。在Mapper接口中定义两个方法执行两条不同的SQL语句:查询总记录数指定页码的数据列表在Service当中,调用Mapper接口的两个方法,分别获取:总记录数、查询结果列表,然后在将获取的数据结果封装到PageBean对象中。大家思考下:在未来开发其他项目,只要涉及到分页查询功能(例:订单、用户、支付、商品),都必须按照以上操作完成功能开发结论:原始方式的分页查询,存在着"步骤固定"、"代码频繁"的问题。

weixin_45210565的博客 1769

Spring Security JWT无状态认证实战:从原理到分布式应用集成

在分布式系统和微服务架构中,传统的基于Session的有状态认证面临扩展性和单点故障的挑战。JWT作为一种开放标准,通过将认证信息编码为自包含的令牌,实现了无状态认证,有效解决了跨服务、跨域的身份验证问题。其核心原理基于三段式结构,通过数字签名确保令牌的完整性和可信性,常用的HS256和RS256算法分别适用于不同安全要求的场景。这种技术为API安全、前后端分离应用提供了轻量级解决方案,尤其在Spring Security框架下,通过自定义过滤器和配置,可以高效集成JWT,实现生产可用的认证流程。本文聚焦于

weixin_30500105的博客 340

电池、AA‑CAES 与抽水蓄能多目标优化调度研究(三目标:成本、峰谷差、碳排放)(Matlab代码实现)

内容概要:本文针对电池、先进绝热压缩空气储能(AA-CAES)与抽水蓄能三种储能技术,构建了一个多目标优化调度模型,旨在协同优化电力系统的运行成本、负荷峰谷差和碳排放三个关键指标。研究采用Matlab进行代码实现,建立了综合考虑各类储能系统运行特性的精细化数学模型,并通过多目标优化算法求解Pareto最优前沿,深入分析了不同优化目标间的内在权衡与博弈关系。文中系统阐述了各储能单元的数学建模方法、系统运行约束条件的设定以及多目标优化问题的规范化处理流程,最终通过详细的仿真算例验证了所提模型与方法的有效性,充分展示了该调度策略在促进新能源消纳、降低系统综合运行成本和减少碳排放方面的巨大潜力与应用价值。; 适合人群:具备电力系统、能源经济或优化理论基础知识的研究生、科研人员及从事能源规划与调度的工程技术人员。; 使用场景及目标:①研究包含多种新型储能技术的综合能源系统协调调度问题;②学习如何建立电力系统多目标优化模型(涵盖经济性、环保性和运行稳定性);③掌握利用Matlab实现多目标优化算法并进行结果可视化分析与决策的方法。; 阅读建议:此资源以Matlab代码为核心,不仅提供了严谨的理论模型,更有完整的可运行代码实现。读者在学习时应结合代码深入理解模型构建的每一个细节,包括变量的定义、复杂约束的处理以及目标函数的加权聚合方法,并建议尝试修改模型参数或调整目标权重,以观察系统行为的动态变化,从而深化对多目标优化本质和工程应用的理解。

别再让用户重新登录了!Spring Security + JWT双Token刷新机制的完整配置与避坑指南

本文详细介绍了Spring Security与JWT双Token刷新机制的最佳实践,通过refresh_token实现前后端分离架构下的安全认证与实时刷新。文章涵盖从基础配置到生产环境优化的完整方案,包括Token生成、存储策略、前端Axios集成及安全增强措施,帮助开发者提升系统安全性和用户体验。

weixin_30785593的博客 367

产业园区运营负责人如何精准匹配企业技术需求与科研成果?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

A股分钟数据涵盖股票、ETF、可转债及指数四大类 每条数据记录包含精确到分钟的时间戳,以及开盘价、最高价、最低价、收盘价、成交量、成交额六个核心字段 部分数据还提供前收盘价作为参考 股票与ETF数据细

A股分钟数据涵盖股票、ETF、可转债及指数四大类。每条数据记录包含精确到分钟的时间戳,以及开盘价、最高价、最低价、收盘价、成交量、成交额六个核心字段。部分数据还提供前收盘价作为参考。股票与ETF数据细分至1分钟、5分钟等周期,可转债在交易时段内逐笔生成分钟快照,指数数据则同步覆盖上证、深证等主要宽基指数,便于观察市场整体走势。 数据源:CMES金融数据库

SpringBoot+Vue实战:高校实习信息管理系统设计与实现

信息管理系统是解决组织内数据分散、流程不透明问题的核心工具,其基本原理是通过统一的数据模型和业务流程,将分散的信息孤岛整合为协同工作的数字平台。在技术实现上,采用前后端分离架构已成为现代Web开发的主流范式,它能有效提升开发效率、保障系统可维护性。SpringBoot作为Java后端开发的利器,以其自动配置和快速启动的特性,极大地简化了RESTful API服务的构建;而Vue.js作为渐进式前端框架,凭借其响应式数据绑定和组件化开发模式,能够高效构建交互丰富的用户界面。这种技术组合的价值在于,它能支撑起从

weixin_34080571的博客 431

产业园区运营负责人如何借助平台实现创新资源的动态配置与精准对接?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

SpringBoot整合AI大模型:影视评论舆情分析与可视化平台实战

在软件工程实践中,特别是毕业设计场景中,如何将主流后端框架与前沿人工智能技术深度融合,是开发者普遍关注的热点。SpringBoot作为Java生态中高效的后端开发框架,提供了一套标准化的RESTful API构建能力,能够快速实现业务逻辑与数据存储的整合。而AI大模型的崛起,尤其是自然语言处理能力的提升,使得文本情感分析和舆情监测从传统词典匹配跃升到深度学习语义理解阶段。将两者结合,利用大模型对海量评论进行情感极性判断、关键词抽取,再通过可视化技术展示趋势、占比与焦点,能够构建一个从数据采集到智能分析再到直

weixin_34297704的博客 388

西门子PLC1200 1214C DC/DC/DC 驱动步进电机

控制电机旋转的指令在“指令”中的“工艺”下的“Motion Control”(若出现程序块无法调用情况,则看备注3的第4条),本篇只介绍常用指令(若想学习详细与更多指令,请参考指令手册)。

2301_79553921的博客 1万+

Jasypt 开源加密库使用教程

Jasypt 加密框架概述 1、Jasypt Spring Boot 为 spring boot 应用程序中的属性源提供加密支持,出于安全考虑,Spring boot 配置文件中的敏感信息通常需要对它进行加密/脱敏处理,尽量不使用明文,要实现这一点,办法有很多,自己手动对敏感信息进行加解密也是可以的。 2、有需求就有人奉献,Jasypt 开源安全框架就是专门用于处理 Spring boot 属性加密的,在配置文件中直接配置密文,然后应用启动的时候,Jasypt 会自动将密码解密成明文供程序使用。 3、

蚩尤后裔-汪茂雄 3万+

SpringBoot+Vue学生信息管理系统:从部署到二次开发的完整实践指南

前后端分离架构已成为现代Web应用开发的主流模式,它将前端用户界面与后端业务逻辑解耦,通过RESTful API进行通信,提升了开发效率和系统可维护性。其核心原理在于前端负责视图渲染与交互,后端专注于数据处理与API提供,两者独立部署,协同工作。这种架构的技术价值在于实现了关注点分离,便于团队协作和技术栈选型自由。在诸如学生信息管理、电商后台、OA系统等应用场景中,前后端分离模式能高效支撑复杂的业务需求。本文以SpringBoot和Vue技术栈构建的“学生信息管理系统”为例,深入剖析其部署流程、核心代码模块

weixin_34209851的博客 482

VisionMaster4.2.0与C#控件开发实战:从入门到精通的完整指南

本文详细介绍了VisionMaster4.2.0与C#控件开发的实战指南,从基础环境搭建到核心控件使用,再到高级功能实现。通过VmFrontendControl、VmGlobalToolControl等核心控件的深入解析,帮助开发者快速掌握机器视觉应用开发技巧,提升开发效率。特别适合需要集成VisionMaster4.2.0 SDK进行视觉系统开发的C#程序员。

weixin_42665725的博客 383
上一篇: 群晖DSM7.0最新版Python环境配置避坑指南(含国内镜像加速)
下一篇: OneTab插件历史数据一键清理指南(附Python自动化脚本)
人间清醒863
博客等级 码龄3年 33粉丝 1544原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值