苗木网站素材,佛山网站建设业务员,三明网站设计,学校网站建设钉钉Gitee仓库
https://gitee.com/Lin_DH/system
介绍
常用的 API 安全措施包括#xff1a;防火墙、验证码、鉴权、IP限制、数据加密、限流、监控、网关等#xff0c;以确保接口的安全性。
常见措施
1#xff09;防火墙 防火墙是网络安全中最基本的安全设备之一#xff0c…Gitee仓库
https://gitee.com/Lin_DH/system
介绍
常用的 API 安全措施包括防火墙、验证码、鉴权、IP限制、数据加密、限流、监控、网关等以确保接口的安全性。
常见措施
1防火墙 防火墙是网络安全中最基本的安全设备之一主要用于防止未经授权的网络访问和攻击。 防火墙主要用于过滤和控制网络流量以保护网络安全。 防火墙可以防止的攻击行为包括
无效数据包防火墙可以识别和过滤无效的数据包如错误的 IP 地址、伪造的数据包、无法识别的协议等。DOS 和 DDOS 攻击防火墙可以使用不同的技术来检测和阻止 DOS 和 DDOS 攻击如阻止大量 TCP / UDP 连接、IP 地址过滤、流量限制等。病毒和蠕虫攻击防火墙可以使用特定的病毒和蠕虫检测技术如签名检测、行为检测、模式识别等来防止这些恶意软件的传播。网络钓鱼和欺骗攻击防火墙可以检测、防止网络钓鱼、欺骗攻击如防止虚假登录页面、欺骗的网站等。恶意流量攻击防火墙可以检测和防止恶意流量攻击如过滤带有恶意载荷的数据包和防止被黑客利用的端口。网络侦察攻击防火墙可以使用一些技术来防止网络侦察攻击如防止扫描、端口扫描、漏洞利用等。
2验证码 在特定接口上要求用户在访问前先进行验证码验证以确保发送该请求的为真实用户。
3鉴权 要求用户在访问 API 时进行身份认证并根据用户的权限进行授权只允许有权限的用户访问特定的接口。 4IP限制 仅限特定 IP 范围对 API 的访问例如允许内网或者加入 IP 白名单的能够访问特定 API 。 5数据加密 对敏感数据进行加密传输使用 HTTPS 协议保证数据传输的安全性。 以往很多接口都是使用 HTTP 协议Hyper Text Transport Protocol超文本传输协议用于传输客户端和服务器端的数据。 HTTP 协议使用虽然简单方便但也存在着问题
使用明文通讯传输内容容易被窃听不验证通讯方的身份容易遭到伪装无法证明报文的完整性报文容易被篡改 为了解决 HTTP 协议的一系列问题出现了 HTTPS 协议。HTTPS 协议是在 HTTP 协议上添加了加密机制。 SSLSecure Socket Layer安全套接层 TLSTransport Layer Security传输层安全 HTTPS HTTP 加密 认证 完整性保护 为了安全性考虑接口的协议需要使用 HTTPS 协议。
6限流 设置访问频率限制例如每分钟、每小时、每天只允许请求访问一定次数超出限制则返回错误信息或者封禁 IP。 7监控 监控 API 的访问日志统计用户对接口的调用情况对流量激增、某个IP频繁请求同一接口则自动发送邮件等通知及时采取相应的安全措施。 8网关 在 API 和客户端之间引入 API 网关对请求进行过滤、鉴权、限流等操作保护后端 API 的安全。
IP限制方式拦截器
代码实现
第一步定义 IP 限制拦截器 IPInterceptor.java import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;/*** IP拦截器* author DUHAOLIN* date 2024/11/13*/
Component
public class IPInterceptor implements HandlerInterceptor {//IP白名单private static final ListString ALLOWED_IPS Arrays.asList(127.0.0.1, 0:0:0:0:0:0:0:1);Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String ipAddress request.getRemoteAddr();//不允许访问的IP返回Access denied错误信息,并且设置响应的状态码为403(Forbidden)if (!ALLOWED_IPS.contains(ipAddress)) {response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.getWriter().write(Access denied);return false;}return true;}}效果图 限制次数方式Redis 拦截器
Windows安装Redis
Redis 下载链接https://pan.baidu.com/s/1BMt4cIxjKTtyL3T0_iSC2w 密码rkne 打开 CMD 命令窗口在 Redis 安装目录执行如下命令redis-server.exe redis.windows.conf
依赖 pom.xml !-- Redis --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency!-- 分布式锁工具 --dependencygroupIdorg.redisson/groupIdartifactIdredisson-spring-boot-starter/artifactIdversion${redission.version}/version/dependency配置文件 application.yml spring:redis:host: localhostport: 6379timeout: 10代码实现
第一步添加解析 Redis Key 前缀的接口 KeyPrefix.java package com.lm.system.redis;/*** author DUHAOLIN* date 2024/11/13*/
public interface KeyPrefix {int expireSeconds();String getPrefix();
}第二步添加解析 Redis 基础前缀的抽象类 BasePrefix.java package com.lm.system.redis;/*** author DUHAOLIN* date 2024/11/13*/
public abstract class BasePrefix implements KeyPrefix {public BasePrefix() {}public BasePrefix(String prefix) {this(0, prefix);}private int expireSeconds;private String prefix;public BasePrefix(int expireSeconds, String prefix) {this.expireSeconds expireSeconds;this.prefix prefix;}Overridepublic int expireSeconds() {return 0; //默认永不过期}Overridepublic String getPrefix() {String simpleName this.getClass().getSimpleName();return simpleName : prefix;}}
第三步添加解析用户Key的实现类 AccessKey.java package com.lm.system.common;import com.lm.system.redis.BasePrefix;/*** author DUHAOLIN* date 2024/11/13*/
public class AccessKey extends BasePrefix {public AccessKey() {}public AccessKey(String prefix) {super(0, prefix);}public AccessKey(int expireSeconds, String prefix) {super(expireSeconds, prefix);}public static AccessKey withExpire(int expireSeconds) {return new AccessKey(expireSeconds, prefix);}Overridepublic int expireSeconds() {return super.expireSeconds();}Overridepublic String getPrefix() {return super.getPrefix();}}
第四步在需要限流的接口上添加 AccessLimit 注解。 注
其他 User 实体类等可以查看 Gitee 仓库https://gitee.com/Lin_DH/system。Redis 不能和 cache 缓存一起使用ServiceImpl中 users 方法使用需要了需要注释掉 Cacheable 注解。 UserController.java GetMapping(users)ApiOperation(获取所有用户信息)AccessLimit(seconds 10, maxCount 3)public String users() {ListUser users userService.queryAllUser();return ResultBody.build(HttpStatus.OK).setData(users).setCount(users.size()).getReturn();}第五步启动类添加 EnableCaching 注解 SystemApplication.java package com.lm.system;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;EnableCaching
SpringBootApplication
MapperScan(com.lm.system.mapper)
public class SystemApplication extends SpringBootServletInitializer {public static void main(String[] args) {SpringApplication.run(SystemApplication.class, args);}Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder application) {return application.sources(SystemApplication.class);}}第六步添加 Redis 操作接口 RedisService.java package com.lm.system.redis;import org.redisson.api.RReadWriteLock;import java.util.List;
import java.util.Map;
import java.util.Set;/*** author DUHAOLIN* date 2024/11/13*/
public interface RedisService {/*** 保存属性*/void set(String key, Object value, long time);/*** 保存属性*/void set(String key, Object value);/*** 获取属性*/Object get(String key);/*** 删除属性*/Boolean del(String key);/*** 批量删除属性*/Long del(ListString keys);/*** 设置过期时间*/Boolean expire(String key, long time);/*** 获取过期时间*/Long getExpire(String key);/*** 判断是否有该属性*/Boolean hasKey(String key);/*** 按delta递增*/Long incr(String key, long delta);/*** 按delta递减*/Long decr(String key, long delta);/*** 获取Hash结构中的属性*/Object hGet(String key, String hashKey);/*** 向Hash结构中放入一个属性*/Boolean hSet(String key, String hashKey, Object value, long time);/*** 向Hash结构中放入一个属性*/void hSet(String key, String hashKey, Object value);/*** 直接获取整个Hash结构*/MapObject, Object hGetAll(String key);/*** 直接设置整个Hash结构*/Boolean hSetAll(String key, MapString, Object map, long time);/*** 直接设置整个Hash结构*/void hSetAll(String key, MapString, ? map);/*** 删除Hash结构中的属性*/void hDel(String key, Object... hashKey);/*** 判断Hash结构中是否有该属性*/Boolean hHasKey(String key, String hashKey);/*** Hash结构中属性递增*/Long hIncr(String key, String hashKey, Long delta);/*** Hash结构中属性递减*/Long hDecr(String key, String hashKey, Long delta);/*** 获取Set结构*/SetObject sMembers(String key);/*** 向Set结构中添加属性*/Long sAdd(String key, Object... values);/*** 向Set结构中添加属性*/Long sAdd(String key, long time, Object... values);/*** 是否为Set中的属性*/Boolean sIsMember(String key, Object value);/*** 获取Set结构的长度*/Long sSize(String key);/*** 删除Set结构中的属性*/Long sRemove(String key, Object... values);/*** 获取List结构中的属性*/ListObject lRange(String key, long start, long end);/*** 获取List结构的长度*/Long lSize(String key);/*** 根据索引获取List中的属性*/Object lIndex(String key, long index);/*** 向List结构中添加属性*/Long lPush(String key, Object value);/*** 向List结构中添加属性*/Long lPush(String key, Object value, long time);/*** 向List结构中批量添加属性*/Long lPushAll(String key, Object... values);/*** 向List结构中批量添加属性*/Long lPushAll(String key, Long time, Object... values);/*** 从List结构中移除属性*/Long lRemove(String key, long count, Object value);/*** 尝试获取分布式锁* param key* param timeOut* param expireTime* return* throws InterruptedException*/boolean tryLock(String key, long timeOut, long expireTime) throws InterruptedException;/*** 解锁* param key* return*/void unLock(String key);/*** 获取分布式读写锁对象* param lockKey* return*/RReadWriteLock getReadWriteLock(String lockKey);
}第七步添加 Redis 操作实现类 RedisServiceImpl.java package com.lm.system.redis;import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** author DUHAOLIN* date 2024/11/13*/
Service
public class RedisServiceImpl implements RedisService {Resourceprivate RedisTemplateString, Object redisTemplate;Resourceprivate RedissonClient redissonClient;Overridepublic void set(String key, Object value, long time) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);}Overridepublic void set(String key, Object value) {redisTemplate.opsForValue().set(key, value);}Overridepublic Object get(String key) {return redisTemplate.opsForValue().get(key);}Overridepublic Boolean del(String key) {return redisTemplate.delete(key);}Overridepublic Long del(ListString keys) {return redisTemplate.delete(keys);}Overridepublic Boolean expire(String key, long time) {return redisTemplate.expire(key, time, TimeUnit.SECONDS);}Overridepublic Long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}Overridepublic Boolean hasKey(String key) {return redisTemplate.hasKey(key);}Overridepublic Long incr(String key, long delta) {return redisTemplate.opsForValue().increment(key, delta);}Overridepublic Long decr(String key, long delta) {return redisTemplate.opsForValue().increment(key, -delta);}Overridepublic Object hGet(String key, String hashKey) {return redisTemplate.opsForHash().get(key, hashKey);}Overridepublic Boolean hSet(String key, String hashKey, Object value, long time) {redisTemplate.opsForHash().put(key, hashKey, value);return expire(key, time);}Overridepublic void hSet(String key, String hashKey, Object value) {redisTemplate.opsForHash().put(key, hashKey, value);}Overridepublic MapObject, Object hGetAll(String key) {return redisTemplate.opsForHash().entries(key);}Overridepublic Boolean hSetAll(String key, MapString, Object map, long time) {redisTemplate.opsForHash().putAll(key, map);return expire(key, time);}Overridepublic void hSetAll(String key, MapString, ? map) {redisTemplate.opsForHash().putAll(key, map);}Overridepublic void hDel(String key, Object... hashKey) {redisTemplate.opsForHash().delete(key, hashKey);}Overridepublic Boolean hHasKey(String key, String hashKey) {return redisTemplate.opsForHash().hasKey(key, hashKey);}Overridepublic Long hIncr(String key, String hashKey, Long delta) {return redisTemplate.opsForHash().increment(key, hashKey, delta);}Overridepublic Long hDecr(String key, String hashKey, Long delta) {return redisTemplate.opsForHash().increment(key, hashKey, -delta);}Overridepublic SetObject sMembers(String key) {return redisTemplate.opsForSet().members(key);}Overridepublic Long sAdd(String key, Object... values) {return redisTemplate.opsForSet().add(key, values);}Overridepublic Long sAdd(String key, long time, Object... values) {Long count redisTemplate.opsForSet().add(key, values);expire(key, time);return count;}Overridepublic Boolean sIsMember(String key, Object value) {return redisTemplate.opsForSet().isMember(key, value);}Overridepublic Long sSize(String key) {return redisTemplate.opsForSet().size(key);}Overridepublic Long sRemove(String key, Object... values) {return redisTemplate.opsForSet().remove(key, values);}Overridepublic ListObject lRange(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);}Overridepublic Long lSize(String key) {return redisTemplate.opsForList().size(key);}Overridepublic Object lIndex(String key, long index) {return redisTemplate.opsForList().index(key, index);}Overridepublic Long lPush(String key, Object value) {return redisTemplate.opsForList().rightPush(key, value);}Overridepublic Long lPush(String key, Object value, long time) {Long index redisTemplate.opsForList().rightPush(key, value);expire(key, time);return index;}Overridepublic Long lPushAll(String key, Object... values) {return redisTemplate.opsForList().rightPushAll(key, values);}Overridepublic Long lPushAll(String key, Long time, Object... values) {Long count redisTemplate.opsForList().rightPushAll(key, values);expire(key, time);return count;}Overridepublic Long lRemove(String key, long count, Object value) {return redisTemplate.opsForList().remove(key, count, value);}Overridepublic boolean tryLock(String key, long timeOut, long expireTime) throws InterruptedException {RLock lock redissonClient.getLock(key);return lock.tryLock(timeOut, expireTime, TimeUnit.SECONDS);}/*** 解锁* param key* return*/Overridepublic void unLock(String key){RLock lock redissonClient.getLock(key);lock.unlock();}Overridepublic RReadWriteLock getReadWriteLock(String lockKey) {return redissonClient.getReadWriteLock(lockKey);}
}第八步添加访问频率限制拦截器 AntiBrushInterceptor.java package com.lm.system.interceptor;import com.lm.system.annotation.AccessLimit;
import com.lm.system.common.AccessKey;
import com.lm.system.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;/*** 访问频率限制拦截器* author DUHAOLIN* date 2024/11/13*/
Slf4j
public class AntiBrushInterceptor implements HandlerInterceptor {private final RedisService redisService;public AntiBrushInterceptor(RedisService redisService) {this.redisService redisService;}private static final DateTimeFormatter FORMATTER DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss);Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断请求是否属于方法请求if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod (HandlerMethod) handler;//获取方法中的注解判断是否有该注解AccessLimit accessLimit handlerMethod.getMethodAnnotation(AccessLimit.class);if (accessLimit null) {return true;}int seconds accessLimit.seconds();int maxCount accessLimit.maxCount();boolean needLogin accessLimit.needLogin();String key request.getRequestURI();if (needLogin) {//判断是否登录key _userId001; //已登录,获取userId}//从redis中获取用户的访问次数AccessKey accessKey AccessKey.withExpire(seconds);String realKey accessKey.getPrefix() key;Integer count (Integer) redisService.get(realKey);//访问次数处理if (count null) {//首次访问redisService.set(realKey, 1, 60);}else if (count maxCount) {//加1redisService.incr(realKey, 1);}else {//超出访问次数log.info(进入服务降级,时间{}, LocalDateTime.now().format(FORMATTER));response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());response.getWriter().write(Too Many Requests. Number of visits: maxCount);return false;}}return true;}
}
效果图
前三次访问正常第四次开始返回错误信息。
项目结构图 参考链接
如何防范API经常被人频繁调用【https://baijiahao.baidu.com/s?id1791472081681790682wfrspiderforpc】 API接口防刷的9种方案【https://baijiahao.baidu.com/s?id1802852256970678261wfrspiderforpc】 Spring Boot 项目的 API 接口防刷【https://www.iocoder.cn/Fight/Spring-Boot-project-API-anti-brush-interface/?self】