网关整合验签

Golang JWT验签与生成的五大底层陷阱及生产级实践 JWT(JSON Web Token)是一种广泛用于身份认证与信息交换的开放标准,其核心原理基于数字签名或MAC验证,确保payload不可篡改且来源可信。技术价值体现在无状态鉴权、跨域信任传递与微服务间轻量凭证共享。典型应用场景包括API网关鉴权、多租户SaaS系统、IoT设备认证及前后端分离架构。然而在Golang实践中,开发者常因忽略密钥加载时机、ECDSA PEM解析字节序、时钟偏移(clock skew)配置、自定义claims序列化规范及RS256/ES256算法选型边界,导致验签失败、性能瓶颈 阅读详情

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.example</groupId>
    <artifactId>api-gateway</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/>
    </parent>
    
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.example.gateway.ApiGatewayApplication</mainClass>
                    <javaVersion>11</javaVersion>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

验签拦截

package com.example.gateway.filter;

import com.example.gateway.util.SignatureUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Component
public class SignatureGlobalFilter implements GlobalFilter, Ordered {
    
    private static final String SIGNATURE_HEADER = "X-Signature";
    private static final String TIMESTAMP_HEADER = "X-Timestamp";
    private static final String NONCE_HEADER = "X-Nonce";
    private static final String APP_ID_HEADER = "X-App-Id";
    private static final long TIMESTAMP_VALIDITY = 5 * 60 * 1000;
    
    private final RedisTemplate<String, String> redisTemplate;
    
    public SignatureGlobalFilter(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        String signature = getHeader(request, SIGNATURE_HEADER);
        String timestamp = getHeader(request, TIMESTAMP_HEADER);
        String nonce = getHeader(request, NONCE_HEADER);
        String appId = getHeader(request, APP_ID_HEADER);
        
        // 基础参数校验
        if (!validateBasicParams(signature, timestamp, nonce, appId)) {
            return setErrorResponse(exchange, HttpStatus.BAD_REQUEST, "Missing required headers");
        }
        
        // 时间戳校验
        if (!validateTimestamp(timestamp)) {
            return setErrorResponse(exchange, HttpStatus.BAD_REQUEST, "Invalid timestamp");
        }
        
        // 防重放攻击校验
        if (!validateNonce(nonce, timestamp)) {
            return setErrorResponse(exchange, HttpStatus.BAD_REQUEST, "Duplicate request");
        }
        
        // 签名验证
        if (!validateSignature(request, signature, appId)) {
            return setErrorResponse(exchange, HttpStatus.UNAUTHORIZED, "Invalid signature");
        }
        
        // 记录nonce到Redis,防止重放
        String nonceKey = "nonce:" + appId + ":" + nonce;
        redisTemplate.opsForValue().set(nonceKey, timestamp, java.time.Duration.ofMinutes(10));
        
        return chain.filter(exchange);
    }
    
    @Override
    public int getOrder() {
        return -1; // 高优先级
    }
    
    private String getHeader(ServerHttpRequest request, String headerName) {
        return request.getHeaders().getFirst(headerName);
    }
    
    private boolean validateBasicParams(String signature, String timestamp, 
                                       String nonce, String appId) {
        return signature != null && !signature.isEmpty() &&
               timestamp != null && !timestamp.isEmpty() &&
               nonce != null && !nonce.isEmpty() &&
               appId != null && !appId.isEmpty();
    }
    
    private boolean validateTimestamp(String timestampStr) {
        try {
            long timestamp = Long.parseLong(timestampStr);
            long currentTime = System.currentTimeMillis();
            return Math.abs(currentTime - timestamp) <= TIMESTAMP_VALIDITY;
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    private boolean validateNonce(String nonce, String timestamp) {
        String nonceKey = "nonce:" + nonce;
        return !Boolean.TRUE.equals(redisTemplate.hasKey(nonceKey));
    }
    
    private boolean validateSignature(ServerHttpRequest request, 
                                     String receivedSignature, 
                                     String appId) {
        try {
            String appSecret = getAppSecret(appId);
            if (appSecret == null) {
                return false;
            }
            
            String dataToSign = buildDataToSign(request);
            String calculatedSignature = SignatureUtil.calculateSignature(dataToSign, appSecret);
            
            return SignatureUtil.safeEquals(calculatedSignature, receivedSignature);
        } catch (Exception e) {
            return false;
        }
    }
    
    private String buildDataToSign(ServerHttpRequest request) {
        StringBuilder sb = new StringBuilder();
        
        sb.append(request.getMethod()).append("\n");
        sb.append(request.getURI().getPath()).append("\n");
        sb.append(getHeader(request, TIMESTAMP_HEADER)).append("\n");
        sb.append(getHeader(request, NONCE_HEADER)).append("\n");
        sb.append(getHeader(request, APP_ID_HEADER)).append("\n");
        
        // 添加查询参数
        request.getQueryParams().entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .forEach(entry -> {
                String key = entry.getKey();
                String value = entry.getValue().get(0);
                sb.append(key).append("=").append(value).append("\n");
            });
        
        return sb.toString();
    }
    
    private String getAppSecret(String appId) {
        // 从Redis或数据库获取应用密钥
        String redisKey = "app_secret:" + appId;
        return redisTemplate.opsForValue().get(redisKey);
    }
    
    private Mono<Void> setErrorResponse(ServerWebExchange exchange, 
                                         HttpStatus status, String message) {
        exchange.getResponse().setStatusCode(status);
        exchange.getResponse().getHeaders().setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
        
        String jsonResponse = String.format(
            "{\"code\":%d,\"message\":\"%s\",\"timestamp\":%d}", 
            status.value(), message, System.currentTimeMillis()
        );
        
        byte[] bytes = jsonResponse.getBytes(StandardCharsets.UTF_8);
        return exchange.getResponse().writeWith(Mono.just(
            exchange.getResponse().bufferFactory().wrap(bytes)
        ));
    }
}


<code_end>
<code_start project_name=java_api_gateway filename=src/main/java/com/example/gateway/ApiGatewayApplication.java title=网关应用主入口 entrypoint=true runnable=true project_final_file=false>
package com.example.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
}
<code_end>
<code_start project_name=java_api_gateway filename=src/main/java/com/example/gateway/filter/SignatureGlobalFilter.java title=全局签名验证过滤器 entrypoint=false runnable=false project_final_file=false>

<code_end>
<code_start project_name=java_api_gateway filename=src/main/java/com/example/gateway/util/SignatureUtil.java title=签名工具类 entrypoint=false runnable=false project_final_file=false>
package com.example.gateway.util;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

public class SignatureUtil {
    
    private static final String HMAC_SHA256 = "HmacSHA256";
    
    public static String calculateSignature(String data, String secret) {
        try {
            Mac mac = Mac.getInstance(HMAC_SHA256);
            SecretKeySpec secretKeySpec = new SecretKeySpec(
                secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA256);
            mac.init(secretKeySpec);
            byte[] signatureBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(signatureBytes);
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate signature", e);
        }
    }
    
    public static boolean safeEquals(String a, String b) {
        if (a == null || b == null) {
            return false;
        }
        
        byte[] aBytes = a.getBytes(StandardCharsets.UTF_8);
        byte[] bBytes = b.getBytes(StandardCharsets.UTF_8);
        
        return MessageDigest.isEqual(aBytes, bBytes);
    }
    
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
    
    public static String generateNonce() {
        byte[] randomBytes = new byte[16];
        new java.security.SecureRandom().nextBytes(randomBytes);
        return bytesToHex(randomBytes);
    }
}
<code_end>
<code_start project_name=java_api_gateway filename=src/main/java/com/example/gateway/filter/RateLimitFilter.java title=限流过滤器 entrypoint=false runnable=false project_final_file=false>
package com.example.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;

@Component
public class RateLimitFilter implements GlobalFilter, Ordered {
    
    private final RedisTemplate<String, String> redisTemplate;
    
    public RateLimitFilter(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String appId = exchange.getRequest().getHeaders().getFirst("X-App-Id");
        String clientIp = getClientIp(exchange);
        
        String rateLimitKey = "rate_limit:" + appId + ":" + clientIp;
        
        // 使用Lua脚本实现原子性限流
        String luaScript = 
            "local key = KEYS[1] " +
            "local limit = tonumber(ARGV[1]) " +
            "local window = tonumber(ARGV[2]) " +
            "local current = redis.call('GET', key) " +
            "if current then " +
            "    if tonumber(current) >= limit then " +
            "        return 0 " +
            "    else " +
            "        redis.call('INCR', key) " +
            "        return 1 " +
            "    end " +
            "else " +
            "    redis.call('SET', key, 1, 'EX', window) " +
            "    return 1 " +
            "end";
        
        RedisScript<Long> script = RedisScript.of(luaScript, Long.class);
        
        List<String> keys = Arrays.asList(rateLimitKey);
        Long result = redisTemplate.execute(script, keys, "100", "60"); // 100请求/分钟
        
        if (result == 0) {
            return setErrorResponse(exchange, HttpStatus.TOO_MANY_REQUESTS, "Rate limit exceeded");
        }
        
        return chain.filter(exchange);
    }
    
    @Override
    public int getOrder() {
        return -2; // 在签名验证之前执行
    }
    
    private String getClientIp(ServerWebExchange exchange) {
        String xForwardedFor = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        return exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
    }
    
    private Mono<Void> setErrorResponse(ServerWebExchange exchange, 
                                         HttpStatus status, String message) {
        exchange.getResponse().setStatusCode(status);
        String jsonResponse = String.format(
            "{\"code\":%d,\"message\":\"%s\"}", 
            status.value(), message
        );
        byte[] bytes = jsonResponse.getBytes(java.nio.charset.StandardCharsets.UTF_8);
        return exchange.getResponse().writeWith(Mono.just(
            exchange.getResponse().bufferFactory().wrap(bytes)
        ));
    }
}
<code_end>
<code_start project_name=java_api_gateway filename=src/main/java/com/example/gateway/config/RedisConfig.java title=Redis配置类 entrypoint=false runnable=false project_final_file=false>
package com.example.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}
<code_end>
<code_start project_name=java_api_gateway filename=src/main/java/com/example/gateway/config/GatewayConfig.java title=网关路由配置 entrypoint=false runnable=false project_final_file=false>
package com.example.gateway.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {
    
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("user-service", r -> r.path("/api/users/**")
                .uri("http://localhost:8081"))
            .route("order-service", r -> r.path("/api/orders/**")
                .uri("http://localhost:8082"))
            .route("product-service", r -> r.path("/api/products/**")
                .uri("http://localhost:8083"))
            .route("public-api", r -> r.path("/api/public/**")
                .filters(f -> f.stripPrefix(1))
                .uri("http://localhost:8084"))
            .build();
    }
}
<code_end>
<code_start project_name=java_api_gateway filename=src/main/resources/application.yml title=应用配置文件 entrypoint=false runnable=false project_final_file=true>
server:
  port: 8080

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "*"
            allowed-methods: "*"
            allowed-headers: "*"
  redis:
    host: localhost
    port: 6379
    database: 0
    timeout: 2000ms

logging:
  level:
    com.example.gateway: DEBUG
<code_end>

结论


该API网关案例提供完整的微服务安全防护机制:
1. 全局签名验证:基于Spring Cloud Gateway的GlobalFilter实现统一验签
2. 防重放攻击:使用Redis存储nonce防止请求重复提交
3. 限流保护:实现基于应用ID和IP地址的分布式限流
4. 路由转发:支持多服务动态路由和负载均衡
5. 安全配置:包含CORS跨域支持和响应头去重
6. 高性能设计:采用响应式编程模型和Lua脚本原子操作

易验——全新登录模式 “互联网+”时代,每天都有新科技、新产品诞生。用户有了更多的选择,对产品的要求变得更高。而追求更快捷更方便的体验,一直都是时代的主流。 以支付为例,从一开始的银联支付,到用户逐渐选择了操作更为便捷简单的微信支付、支付宝支付。显而易见,产品竞争核心已转变为用户体验。好的用户体验能让应用脱颖而出,进而口口相传转化为品牌口碑,这也是各大品牌都强调“用户体验至上”的原因。 传统APP手机注册登录验证的痛点... 阅读详情

相关推荐

SpringBoot整合Zuul微服务网关

SpringBoot整合Zuul微服务网关微服务网关的优点Zuul微服务网关SpringBoot整合Zuul测试Zuul微服务网关Zuul管理端点Zuul的路由设置 微服务网关的优点 降低客户端复杂性 降低认证复杂性 避免跨域请求 易于监控 确保微服务系统可重构 减少客户端与微服务的交互次数 Zuul微服务网关 Zuul是Netflix开源的微服务网关,可以和Eureka、Ribbon、Hystrix等组件配合使用。 Zuul的核心是一系列过滤器,可以完成以下功能: 1、身份认证与安全:识别每个资源

nicedaych的博客 1317

java实现沙箱测试环境支付宝支付(demo)和整合微信支付和支付宝支付到springmvc+spring+mybatis环境全过程(支付宝和微信支付)

一、支付宝测试环境代码测试 1.下载电脑网站的官方demo: 下载地址:https://docs.open.alipay.com/270/106291/ 2.下载解压导入eclipse readme.txt请好好看一下。 只有一个Java配置类,其余都是JSP。 3.配置AlipayConfig (1).注册蚂蚁金服开发者账号(免费,不像苹果会收取费用) 注册地址:http...

好好学java 7537

华为EC6108V9E刷机实战:从U盘准备到开机优化的完整避坑指南

本文提供华为悦盒EC6108V9E刷机与优化的完整实战指南。针对搭载RK3228芯片和安卓4.4.4系统的设备,从型号确认、固件选择、U盘准备,到详细刷机步骤、常见故障排除,以及刷机后的深度性能优化,手把手帮助用户规避风险,让旧盒子焕发新生,成为流畅的全能媒体中心。

weixin_29324401的博客 1191

系列学习互联网安全架构第 8 篇 —— API 接口增加签名(验签)

通过学习之前的博客: API 接口安全设计:https://blog.youkuaiyun.com/BiandanLoveyou/article/details/117487626 信息加密技术(对称加密、非对称加密):https://blog.youkuaiyun.com/BiandanLoveyou/article/details/117524716 现在我们来讲解 API 接口增加签名的内容。不像网上讲解的那种那么难理解,不用那么复杂。 如何保证 API 接口的安全性,一般来说,解决办法有以下几种: 1、...

流放深圳 1225

智能气象站:ESP32驱动的心知天气数据可视化实践

本文详细介绍了基于ESP32微控制器和心知天气API构建智能气象站的完整实践。通过Arduino开发环境,实现天气数据的获取、JSON解析与OLED屏可视化展示,涵盖硬件连接、API配置、低功耗优化及部署问题排查,为物联网开发者提供实用指南。

jump7的博客 210

PTA 7-8 暴力小学(二年级篇)-求出4个数字 (10分)java

PTA 7-8 暴力小学(二年级篇)-求出4个数字 (10分)java 小博是新城小学二年级学生,这孩子人很老实,数学、语文成绩都是中等。不过一旦坐在电脑前,写起代码来,则有些霸气侧漏,尤其擅长各种暴力方法。本题就是数学莫老师的题目之一,请你和小博一起暴力求解吧。 输入格式: 第一行给出这个和N(图中例子的和N为2000)。 输出格式: 第一行输出所有解的数目K,随后K行按照DCBA 4位数字从...

qryzhfj的博客 2187

Dubbo想要个网关怎么办?试试整合Spring Cloud Gateway

在以Dubbo框架体系来构建的微服务架构下想要增加API网关,如果不想自研开发的情况下在目前的开源社区中几乎没有找到支持dubbo协议的主流网关,但是Spring Cloud体系下却有两个非常热门的开源API网关可以选择;本文主要介绍如何通过Nacos整合Spring Cloud Gateway与Dubbo服务。

zlt2000的博客 1万+

基于MATLAB的基本数字调制解调系统的设计

基于MATLAB的基本数字调制解调系统的设计一、摘要现代通信系统要求通信距离远、通信容量大、传输质量好,作为其关键技术之一的调制解调技术一直是人们研究的一个重要方向。本文以MATLAB为软件平台,充分利用其提供的通信工具箱和信号处理工具箱中的模块,对数字调制解调系统进行Simulink设计仿真,并且进行误差分析。调制与解调是通信系统中十分重要的一个环节,针对不同的信道环境选择不同的调制与解调方式可...

m0_55395440的博客 5549

springboot整合gateway网关

API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:(1)客户端会多次请求不同的微服务,增加了客户端的复杂性。(2)存在跨域请求,在一定场景下处理相对复杂。(3)认证复杂,每个服务都需要独立认证。(4)难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。

豆豆的博客 5970

Mamba-YOLO实战:环境配置+模型训练+精度调优全流程指南(附Colab代码)

在目标检测领域,一场由状态空间模型引发的技术革新正在悄然改写行业格局。近期国际顶级学术期刊连续发布的YOLOv5_mamba、FER-YOLO等突破性成果,不仅刷新了检测精度记录,更开创了结构化状态空间模型(SSM)与经典检测框架的创新融合范式。当前该领域涌现的十大标杆性研究,已在以下维度取得突破性进展:√ 遮挡目标识别准确率提升38%√ 小样本场景泛化能力增强2.7倍√ 视频流检测延迟降低至17ms√ 多目标交叉干扰抑制效果达SOTA。

2501_90555291的博客 1581

基于simulink的能量管理策略的场景实例:混合动力电动汽车(HEV)的能量回收与分配策略

本文以混合动力电动汽车(HEV)为研究对象,通过Simulink建模详细演示了能量管理策略的设计与实现。内容涵盖HEV系统架构、核心组件工作原理,并分步骤指导搭建车辆动力学模型、动力系统组件模型和基于规则的能量管理策略。重点分析了制动能量回收与功率分配机制,通过仿真验证了逻辑门限策略在提升燃油经济性和维持电池SOC平衡方面的有效性。文章最后展望了更高级控制策略的应用前景,为HEV能量优化提供了完整的仿真研究框架。

xiaoheshang_123的博客 213

Spring Boot API安全实战:基于RSA签名验签防篡改与防重放攻击

在API接口安全领域,HTTPS确保了传输层的加密,但无法防止请求数据在客户端或服务器端被篡改,以及请求被恶意重放。数字签名技术通过非对称加密算法(如RSA)为数据完整性和身份认证提供了解决方案。其核心原理是发送方使用私钥对请求数据的摘要进行加密生成签名,接收方使用公钥解密并比对摘要,以此验证请求的合法性与完整性。这项技术的工程价值在于,它能有效防御中间人篡改参数(如订单金额)和重放攻击,是构建高安全等级开放平台、支付系统、电商交易等场景的基石。本文以Spring Boot为技术栈,深入剖析RSA签名验签的

cneo2012的博客 413

不加行也能填满:用 ABAP BDC 驯服 Table Control 滚动录制缺陷,批量维护供应商银行账户

摘要 本文探讨了在SAP ECC/S4HANA系统中处理供应商主数据时,Table Control控件带来的批量导入挑战。文章指出,银行账户维护界面中Table Control的滚动动作难以被录制工具捕捉,导致批量导入数据时仅能填充前5行。解决方案是通过BDC_OKCODE显式触发翻页(如=P+),并配合行号拼接(如(01)~(05))将数据写入不可见行。文中提供了完整的ABAP程序示例,展示了如何读取外部文件、动态生成BDCDATA、处理表格翻页逻辑,最终通过CALL TRANSACTION执行XK01事

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 271

nodejs整合银联网关支付

银联支付的测试开发做的很完善,可以下载各个语言的测试包,进行开发测试,但是并没有 nodejs 的,难点就是证书签名还有验签这两个步骤。其实银联加密方式和支付宝微信不同的地方在于,使用了非对称加密,意思是为了在网络中传输安全,双方约定各自产生一个公钥还有私钥,私钥自己保存,公钥公开给对方(你要发送信息的人都知道)。当需要传输秘密的信息时候,用自己的私钥加密,发给对方,对方收到信息后,为了判定这个是否

blackbinbin的博客 3999

人工智能混合编程实践:C++调用封装好的DLL进行异常检测推理

C++、Python、ONNX、人工智能、异常检测、Anomalib 2.1.0

FriendshipTang的博客 170

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Spring Boot整合Spring Cloud Gateway搭建网关服务

spring cloud netflix zuul、spring cloud gateway是最常见的微服务网关,通过网关,我们可以在请求到达后端指定服务之前/后端服务处理完业务响应数据之后对请求/响应进行处理。比如常见的参数校验、接口鉴权等等,在后端服务的拦截器和过滤器能做的事都可以在网关实现。网关的主要功能是请求的转发以及负载均衡,和nginx的功能类似,只是底层实现不同。

2501_92713943的博客 2791

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

springboot+Activiti7整合实践 (九) 网关

activiti工作流中,网关是非常常用的组件,用于处理流程中分支的情况,网关就像一个处理流程分发的端口,起到处理流程走向重要功能。排他网关、并行网关、相容网关等

刘一线的博客 1193

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

springboot整合zuul,实现API网关配置

一个应用中可能包含PC、APP等等终端产品,如果使用单一服务入口的话,发生故障后,整个架构服务将不能提供服务。因此我们可以对不同的服务,配置不同的网关策略,正如nginx一样,可以负载均衡。 服务网关的要素: 稳定性,高可用;安全性;性能,并发性;扩展性; 一般api网关的实现方法有这么几种: 1、Nginx 2、zuul 3、Kong,专门提供微服务API管理平台(底层也是基于Ngi...

月光秦城 1万+

网关gateway和springsecurity的整合

我们一般在微服务的开发中,除了网关之外还有一个认证服务。他们两个都需要整合springsecurity。为啥要这么干:因为springsecurity就是能干好多事,认证(就是账号密码)和授权(就是你能干啥)。然后网关和认证服务就是分别干这两个事的 ,所以相当于拆分了一下springsecurity的功能分别完成一件事。网关统一授权:业务服务中每个微服务都需要区分不同的人干不同的事,所以就是放到网关这里进行统一授权。

wang01_01的博客 6709

网关和API网关

文章目录 腾讯云大学 网关 & API 网关概述 了解没有网关限制下,服务访问会出现的问题 了解网关的分类 了解腾讯云API网关的优势 了解腾讯云API网关的作用和应用场景

besmarterbestronger的博客 1209

zuul网关整合swagger

zuul整合swagger网关 maven依赖 <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.7.0.RELEASE</version> </depende

qq_28461661的博客 679

MobTech|如何使用秒验

秒验是MobTech公司提供的一款实现一键验证功能的产品,从根源上降低企业验证成本,有效提高拉新转化率,降低因验证带来的流失率,3秒完成手机号验证(一键登录)。秒验主要整合了三大运营商独有的数据网关验证能力,本质上提升了短信验证码的用户体验,能够用于用户注册、登陆、支付、安全校验等场景,可实现用户无感知校验,具有操作安全、便捷、低时延等特点。

apkkkk的博客 611

一次性说清楚秒验(本机号码一键登录)

点击上方蓝字关注我们技术总监面试,提问:Redis热点key解决方案一、关于秒验(一键登录)基本原理秒验(一键登录)产品整合了三大运营商特有的数据网关认证能力,升级短信验证码体验,应用于...

独行侠梦的博客 9282

易验业务介绍文档

2019年6月6日,工信部正式向三大运营商和中国广电发布5G商用牌照,中国5G踏入商用元年。之后,手机的数据传输速度会越来越快,与此同时,人们也是越来越离不开手机,购物时通过手机进行选择,吃饭时通过手机进行点餐,放松时通过手机进行娱乐等等。出现了手机可以解决生活中种种问题的情况。对于开发者来说,如何提升用户的体验,成为了一大问题。对于使用APP的用户来说,快速注册,以及登录也成为一大问题。针对各种...

Lukey123456的博客 513
上一篇: 验签Interceptor与Aspect
下一篇: 前置机与网关的区别
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值