查域名的網(wǎng)站網(wǎng)站seo排名培訓(xùn)
Gitee倉庫
https://gitee.com/Lin_DH/system
介紹
常用的 API 安全措施包括:防火墻、驗證碼、鑒權(quán)、IP限制、數(shù)據(jù)加密、限流、監(jiān)控、網(wǎng)關(guān)等,以確保接口的安全性。
常見措施
1)防火墻
防火墻是網(wǎng)絡(luò)安全中最基本的安全設(shè)備之一,主要用于防止未經(jīng)授權(quán)的網(wǎng)絡(luò)訪問和攻擊。
防火墻主要用于過濾和控制網(wǎng)絡(luò)流量,以保護網(wǎng)絡(luò)安全。
防火墻可以防止的攻擊行為包括:
- 無效數(shù)據(jù)包:防火墻可以識別和過濾無效的數(shù)據(jù)包,如錯誤的 IP 地址、偽造的數(shù)據(jù)包、無法識別的協(xié)議等。
- DOS 和 DDOS 攻擊:防火墻可以使用不同的技術(shù)來檢測和阻止 DOS 和 DDOS 攻擊,如阻止大量 TCP / UDP 連接、IP 地址過濾、流量限制等。
- 病毒和蠕蟲攻擊:防火墻可以使用特定的病毒和蠕蟲檢測技術(shù),如簽名檢測、行為檢測、模式識別等,來防止這些惡意軟件的傳播。
- 網(wǎng)絡(luò)釣魚和欺騙攻擊:防火墻可以檢測、防止網(wǎng)絡(luò)釣魚、欺騙攻擊,如防止虛假登錄頁面、欺騙的網(wǎng)站等。
- 惡意流量攻擊:防火墻可以檢測和防止惡意流量攻擊,如過濾帶有惡意載荷的數(shù)據(jù)包和防止被黑客利用的端口。
- 網(wǎng)絡(luò)偵察攻擊:防火墻可以使用一些技術(shù)來防止網(wǎng)絡(luò)偵察攻擊,如防止掃描、端口掃描、漏洞利用等。
2)驗證碼
在特定接口上,要求用戶在訪問前先進行驗證碼驗證,以確保發(fā)送該請求的為真實用戶。
3)鑒權(quán)
要求用戶在訪問 API 時,進行身份認證,并根據(jù)用戶的權(quán)限進行授權(quán),只允許有權(quán)限的用戶訪問特定的接口。
4)IP限制
僅限特定 IP 范圍對 API 的訪問,例如允許內(nèi)網(wǎng)或者加入 IP 白名單的能夠訪問特定 API 。
5)數(shù)據(jù)加密
對敏感數(shù)據(jù)進行加密傳輸,使用 HTTPS 協(xié)議保證數(shù)據(jù)傳輸?shù)陌踩浴?br /> 以往很多接口都是使用 HTTP 協(xié)議(Hyper Text Transport Protocol,超文本傳輸協(xié)議),用于傳輸客戶端和服務(wù)器端的數(shù)據(jù)。
HTTP 協(xié)議使用雖然簡單方便,但也存在著問題:
- 使用明文通訊,傳輸內(nèi)容容易被竊聽
- 不驗證通訊方的身份,容易遭到偽裝
- 無法證明報文的完整性,報文容易被篡改
為了解決 HTTP 協(xié)議的一系列問題,出現(xiàn)了 HTTPS 協(xié)議。HTTPS 協(xié)議是在 HTTP 協(xié)議上添加了加密機制。
SSL(Secure Socket Layer,安全套接層)
TLS(Transport Layer Security,傳輸層安全)
HTTPS = HTTP + 加密 + 認證 + 完整性保護
為了安全性考慮,接口的協(xié)議需要使用 HTTPS 協(xié)議。
6)限流
設(shè)置訪問頻率限制,例如每分鐘、每小時、每天只允許請求訪問一定次數(shù),超出限制則返回錯誤信息或者封禁 IP。
7)監(jiān)控
監(jiān)控 API 的訪問日志,統(tǒng)計用戶對接口的調(diào)用情況,對流量激增、某個IP頻繁請求同一接口,則自動發(fā)送郵件等通知,及時采取相應(yīng)的安全措施。
8)網(wǎng)關(guān)
在 API 和客戶端之間引入 API 網(wǎng)關(guān),對請求進行過濾、鑒權(quán)、限流等操作,保護后端 API 的安全。
IP限制方式(攔截器)
代碼實現(xiàn)
第一步:定義 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 List<String> 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"錯誤信息,并且設(shè)置響應(yīng)的狀態(tài)碼為403(Forbidden)if (!ALLOWED_IPS.contains(ipAddress)) {response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.getWriter().write("Access denied");return false;}return true;}}
效果圖
限制次數(shù)方式(Redis + 攔截器)
Windows安裝Redis
Redis 下載鏈接:https://pan.baidu.com/s/1BMt4cIxjKTtyL3T0_iSC2w 密碼:rkne
打開 CMD 命令窗口,在 Redis 安裝目錄執(zhí)行如下命令:redis-server.exe redis.windows.conf
依賴
pom.xml
<!-- Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 分布式鎖工具 --><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>${redission.version}</version></dependency>
配置文件
application.yml
spring:redis:host: localhostport: 6379timeout: 10
代碼實現(xiàn)
第一步:添加解析 Redis Key 前綴的接口
KeyPrefix.java
package com.lm.system.redis;/*** @author DUHAOLIN* @date 2024/11/13*/
public interface KeyPrefix {int expireSeconds();String getPrefix();
}
第二步:添加解析 Redis 基礎(chǔ)前綴的抽象類
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的實現(xiàn)類
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() {List<User> 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(List<String> keys);/*** 設(shè)置過期時間*/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結(jié)構(gòu)中的屬性*/Object hGet(String key, String hashKey);/*** 向Hash結(jié)構(gòu)中放入一個屬性*/Boolean hSet(String key, String hashKey, Object value, long time);/*** 向Hash結(jié)構(gòu)中放入一個屬性*/void hSet(String key, String hashKey, Object value);/*** 直接獲取整個Hash結(jié)構(gòu)*/Map<Object, Object> hGetAll(String key);/*** 直接設(shè)置整個Hash結(jié)構(gòu)*/Boolean hSetAll(String key, Map<String, Object> map, long time);/*** 直接設(shè)置整個Hash結(jié)構(gòu)*/void hSetAll(String key, Map<String, ?> map);/*** 刪除Hash結(jié)構(gòu)中的屬性*/void hDel(String key, Object... hashKey);/*** 判斷Hash結(jié)構(gòu)中是否有該屬性*/Boolean hHasKey(String key, String hashKey);/*** Hash結(jié)構(gòu)中屬性遞增*/Long hIncr(String key, String hashKey, Long delta);/*** Hash結(jié)構(gòu)中屬性遞減*/Long hDecr(String key, String hashKey, Long delta);/*** 獲取Set結(jié)構(gòu)*/Set<Object> sMembers(String key);/*** 向Set結(jié)構(gòu)中添加屬性*/Long sAdd(String key, Object... values);/*** 向Set結(jié)構(gòu)中添加屬性*/Long sAdd(String key, long time, Object... values);/*** 是否為Set中的屬性*/Boolean sIsMember(String key, Object value);/*** 獲取Set結(jié)構(gòu)的長度*/Long sSize(String key);/*** 刪除Set結(jié)構(gòu)中的屬性*/Long sRemove(String key, Object... values);/*** 獲取List結(jié)構(gòu)中的屬性*/List<Object> lRange(String key, long start, long end);/*** 獲取List結(jié)構(gòu)的長度*/Long lSize(String key);/*** 根據(jù)索引獲取List中的屬性*/Object lIndex(String key, long index);/*** 向List結(jié)構(gòu)中添加屬性*/Long lPush(String key, Object value);/*** 向List結(jié)構(gòu)中添加屬性*/Long lPush(String key, Object value, long time);/*** 向List結(jié)構(gòu)中批量添加屬性*/Long lPushAll(String key, Object... values);/*** 向List結(jié)構(gòu)中批量添加屬性*/Long lPushAll(String key, Long time, Object... values);/*** 從List結(jié)構(gòu)中移除屬性*/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 操作實現(xiàn)類
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 RedisTemplate<String, 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(List<String> 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 Map<Object, Object> hGetAll(String key) {return redisTemplate.opsForHash().entries(key);}@Overridepublic Boolean hSetAll(String key, Map<String, Object> map, long time) {redisTemplate.opsForHash().putAll(key, map);return expire(key, time);}@Overridepublic void hSetAll(String key, Map<String, ?> 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 Set<Object> 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 List<Object> 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中獲取用戶的訪問次數(shù)AccessKey accessKey = AccessKey.withExpire(seconds);String realKey = accessKey.getPrefix() + key;Integer count = (Integer) redisService.get(realKey);//訪問次數(shù)處理if (count == null) {//首次訪問redisService.set(realKey, 1, 60);}else if (count < maxCount) {//加1redisService.incr(realKey, 1);}else {//超出訪問次數(shù)log.info("進入服務(wù)降級,時間{}", 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;}
}
效果圖
前三次訪問正常,第四次開始返回錯誤信息。
項目結(jié)構(gòu)圖
參考鏈接
如何防范API經(jīng)常被人頻繁調(diào)用【https://baijiahao.baidu.com/s?id=1791472081681790682&wfr=spider&for=pc】
API接口防刷的9種方案【https://baijiahao.baidu.com/s?id=1802852256970678261&wfr=spider&for=pc】
Spring Boot 項目的 API 接口防刷【https://www.iocoder.cn/Fight/Spring-Boot-project-API-anti-brush-interface/?self】