SpringBoot+Ajax+redis实现隐藏重要接口地址

'# SpringBoot+Ajax+redis实现隐藏重要接口地址

一、背景与问题

在分布式系统中,接口地址暴露可能带来严重安全风险。传统做法是将接口地址写在前端代码中,但这种方式存在以下问题:

  1. 接口地址容易被逆向工程获取
  2. 前端可以直接调用后端接口,绕过业务逻辑
  3. 接口地址变更时需要更新前端代码

需要一种机制既能保护接口地址,又能保证正常业务交互。本文提出使用SpringBoot+Ajax+Redis的组合方案,通过动态生成访问凭证的方式隐藏接口地址,实现接口访问控制。

二、基本原理

该方案的核心原理是通过动态生成访问凭证(Token),配合Redis缓存实现接口访问控制。具体流程如下:

  1. 前端通过Ajax请求获取访问凭证(Token)
  2. 后端生成唯一Token并存储到Redis(设置过期时间)
  3. 前端使用Token作为参数调用隐藏接口
  4. 后端验证Token有效性,通过则执行业务逻辑

这种机制实现了以下安全特性:

  • 接口地址始终隐藏在前端代码中
  • 通过Token机制控制访问权限
  • Redis缓存保证实时性与可控性

三、环境准备

1. 依赖配置

pom.xml 文件中添加以下依赖:

<dependencies>
    <!-- SpringBoot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Jackson 依赖 -->
    <dependency>
        <groupId>com.fasterxml.jackson</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

2. Redis配置

application.yml 中配置Redis连接信息:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 2
        max-wait: 10000ms

四、核心实现

1. Token生成与存储

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.UUID;

@Service
public class TokenService {
    private final StringRedisTemplate redisTemplate;

    public TokenService(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 生成带过期时间的Token
     * @return 生成的Token
     */
    public String generateToken() {
        String token = UUID.randomUUID().toString();
        // 设置1小时过期时间
        redisTemplate.opsForValue().set(token, "valid", 3600);
        return token;
    }

    /**
     * 验证Token有效性
     * @param token Token值
     * @return 验证结果
     */
    public boolean validateToken(String token) {
        return redisTemplate.hasKey(token) && 
               redisTemplate.getExpire(token) > 0;
    }
}

关键代码说明:

  • 使用UUID生成唯一Token
  • 通过Redis存储Token和有效期
  • 设置过期时间防止Token长期有效

2. 接口访问控制

import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class ApiController {
    private final TokenService tokenService;

    public ApiController(TokenService tokenService) {
        this.tokenService = tokenService;
    }

    /**
     * 获取访问凭证接口
     * @return 生成的Token
     */
    @GetMapping("/token")
    public String getToken() {
        return tokenService.generateToken();
    }

    /**
     * 隐藏接口,需要Token认证
     * @param token 访问凭证
     * @return 模拟业务数据
     */
    @PostMapping("/secure")
    public Map<String, Object> secureData(@RequestParam String token) {
        if (!tokenService.validateToken(token)) {
            throw new RuntimeException("Invalid or expired token");
        }
        Map<String, Object> response = new HashMap<>();
        response.put("status", "success");
        response.put("data", "Secret data");
        return response;
    }
}

关键代码说明:

  • /token 接口提供访问凭证
  • /secure 接口需要Token认证
  • 验证Token有效性后才执行业务逻辑

3. 前端Ajax调用

// 前端调用示例(使用fetch API)
async function getSecureData() {
    // 1. 获取访问凭证
    const tokenResponse = await fetch('/api/token');
    const token = await tokenResponse.text();
    
    // 2. 使用Token调用隐藏接口
    const secureResponse = await fetch('/api/secure', {
        method: 'POST',
        params: { token }
    });
    
    const data = await secureResponse.json();
    console.log(data);
}

关键代码说明:

  • 先获取Token再调用隐藏接口
  • Token作为参数传递
  • 前端代码不暴露实际接口地址

五、完整案例

1. 项目结构

src
├── main
│   ├── java
│   │   └── com.example.demo
│   │       ├── controller
│   │       │   ├── ApiController.java
│   │       │   └── TokenController.java
│   │       ├── service
│   │       │   └── TokenService.java
│   │       └── DemoApplication.java
│   └── resources
│       └── application.yml

2. 完整SpringBoot应用

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

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3. 完整前端调用示例

<!DOCTYPE html>
<html>
<head>
    <title>Secure API Example</title>
</head>
<body>
    <button onclick="getSecureData()">Get Secure Data</button>
    <script>
        async function getSecureData() {
            // 1. 获取访问凭证
            const tokenResponse = await fetch('/api/token');
            const token = await tokenResponse.text();
            
            // 2. 使用Token调用隐藏接口
            const secureResponse = await fetch('/api/secure', {
                method: 'POST',
                params: { token }
            });
            
            const data = await secureResponse.json();
            console.log(data);
        }
    </script>
</body>
</html>

六、源码解析

1. Token生成逻辑

public String generateToken() {
    String token = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set(token, "valid", 3600);
    return token;
}
  • 使用UUID生成128位的随机字符串
  • 将Token作为Key存储到Redis
  • 值设为"valid",实际业务中可存储用户信息
  • 设置1小时过期时间(3600秒)

2. Token验证逻辑

public boolean validateToken(String token) {
    return redisTemplate.hasKey(token) && 
           redisTemplate.getExpire(token) > 0;
}
  • 检查Redis中是否存在该Key
  • 检查Key的剩余有效期
  • 过期时间需要考虑Redis的TTL机制

3. 接口访问控制

@PostMapping("/secure")
public Map<String, Object> secureData(@RequestParam String token) {
    if (!tokenService.validateToken(token)) {
        throw new RuntimeException("Invalid or expired token");
    }
    ...
}
  • 接收Token参数进行验证
  • 通过验证后执行业务逻辑
  • 未通过验证则抛出异常(可自定义异常处理)

七、进阶使用

1. Token刷新机制

public String refreshToken(String oldToken) {
    if (!tokenService.validateToken(oldToken)) {
        throw new RuntimeException("Invalid token");
    }
    String newToken = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set(newToken, "valid", 3600);
    return newToken;
}
  • 支持Token刷新功能
  • 旧Token自动失效
  • 新Token重新生成并存储

2. 多用户支持

public String generateToken(String userId) {
    String token = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set(token, userId, 3600);
    return token;
}
  • 存储用户ID信息
  • 验证时可获取用户信息
  • 支持用户权限控制

3. JWT结合使用

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public String generateJwtToken(String userId) {
    return Jwts.builder()
        .setSubject(userId)
        .setExpiration(new Date(System.currentTimeMillis() + 3600000))
        .signWith(SignatureAlgorithm.HS512, "secretKey")
        .compact();
}
  • 使用JWT替代简单Token
  • 包含用户信息和过期时间
  • 支持更复杂的验证逻辑

八、性能与工程实践

1. Redis性能优化

  • 使用连接池避免频繁连接
  • 合理设置过期时间
  • 避免大Key存储
  • 使用Pipeline批量操作
public void batchSetTokens(List<String> tokens) {
    redisTemplate.getConnectionFactory().getConnection().pipeline()
        .forEach(token -> {
            redisTemplate.getConnectionFactory().getConnection()
                .set(token.getBytes(), "valid".getBytes());
        });
}

2. 异常处理机制

@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleRuntimeException(RuntimeException ex) {
    return ResponseEntity.status(401).body(ex.getMessage());
}
  • 统一处理Token验证异常
  • 返回标准错误响应
  • 记录日志便于排查

3. 安全加固措施

  • 使用HTTPS传输Token
  • 对Token进行加密处理
  • 限制Token请求频率
  • 对Redis进行安全加固(密码、防火墙)

九、常见问题与踩坑

1. Token泄露风险

问题表现:Token被截获后可以访问接口

解决办法:

  • 使用HTTPS加密传输
  • 限制Token请求频率
  • 定期刷新Token
  • 增加用户身份验证

2. Redis连接问题

问题表现:获取Token失败

解决办法:

  • 检查Redis服务是否运行
  • 确认连接配置正确
  • 使用连接池提升性能
  • 配置合理的超时时间

3. 跨域问题

问题表现:前端请求被拒绝

解决办法:

  • 配置CORS支持
  • 使用反向代理
  • 配置Spring Security
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://example.com")
                .allowedMethods("GET", "POST")
                .allowedHeaders("*")
                .maxAge(3600);
    }
}

十、最佳实践

1. 适用场景

  • 敏感接口需要保护
  • 接口地址需要隐藏
  • 前端需要动态获取凭证
  • 需要细粒度访问控制

2. 不适用场景

  • 简单接口不需要保护
  • 低流量场景
  • 需要高性能的场景(建议使用JWT)
  • 需要持久化存储的场景

3. 推荐方案

方案适用场景优点缺点
Token + Redis需要动态Token灵活控制依赖Redis
JWT需要持久化无状态信息暴露
签名校验需要安全传输加密安全配置复杂

十一、总结

SpringBoot+Ajax+Redis实现隐藏重要接口地址的方案,通过动态生成Token和Redis缓存机制,有效保护了接口安全。该方案具有以下特点:

  1. 接口地址始终隐藏在前端代码中
  2. 通过Token机制控制访问权限
  3. Redis缓存保证实时性与可控性
  4. 支持多种进阶功能(刷新、JWT、多用户)

在实际开发中,应根据具体场景选择合适方案。对于需要高度安全的接口,建议结合HTTPS、JWT和安全加固措施。同时要注意性能优化和异常处理,确保系统稳定运行。这种方案特别适合需要隐藏接口地址、控制访问权限的中大型系统。

最后修改于:2026年09月23日 23:41

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日