
使用NestJS和Prisma構建REST API:身份驗證
SpringBoot中集成Redis相對比較簡單,步驟如下:
1.1 引入Redis依賴
<!-- springboot redis依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1.2 在application.yml中配置Redis
spring:
redis:
database: 3 # Redis數據庫索引(默認為0)
host: 127.0.0.1 # Redis服務器地址
port: 6379 # Redis服務器連接端口
password: 123456 # Redis服務器連接密碼(默認為空)
timeout: 2000 # 連接超時時間(毫秒)
jedis:
pool:
max-active: 200 # 連接池最大連接數(使用負值表示沒有限制)
max-idle: 20 # 連接池中的最大空閑連接
min-idle: 0 # 連接池中的最小空閑連接
max-wait: -1 # 連接池最大阻塞等待時間(使用負值表示沒有限制)
1.3 配置RedisTemplate
/**
* @Description: redis配置類
* @Author oyc
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/**
* RedisTemplate相關配置
* 使redis支持插入對象
*
* @param factory
* @return 方法緩存 Methods the cache
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置連接工廠
template.setConnectionFactory(factory);
// 設置key的序列化器
template.setKeySerializer(new StringRedisSerializer());
// 設置value的序列化器
// 使用Jackson 2,將對象序列化為JSON
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
return template;
}
}
以上,已經完成Redis的集成,后續使用可以直接注入RedisTemplate,如下所示:
@Autowired
private RedisTemplate<String, Object> redisTemplate;
2.1 添加自定義AccessLimit注解
使用注解方式實現接口的api限流操作,方便而優雅。
/**
* @Description:
* @Author oyc
*/
@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
/**
* 指定second時間內API請求次數
*/
int maxCount() default 5;
/**
* 請求次數的指定時間范圍 秒數(redis數據過期時間)
*/
int second() default 60;
}
2.2 編寫攔截器
api限流的思路
/**
* @Description: 訪問攔截器
* @Author oyc
*/
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try { // Handler 是否為 HandlerMethod 實例
if (handler instanceof HandlerMethod) {
// 強轉
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 獲取方法
Method method = handlerMethod.getMethod();
// 是否有AccessLimit注解
if (method.isAnnotationPresent(AccessLimit.class)) {
return true;
}
// 獲取注解內容信息
AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
if (accessLimit == null) {
return true;
}
int seconds = accessLimit.second();
int maxCount = accessLimit.maxCount();
// 存儲key
String key = request.getRemoteAddr() + ":" + request.getContextPath() + ":" + request.getRequestURI();
// 已經訪問的次數
Integer count = (Integer) redisTemplate.opsForValue().get(key);
System.out.println("已經訪問的次數:" + count);
if (null == count || -1 == count) {
redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS);
return true;
}
if (count < maxCount) {
redisTemplate.opsForValue().increment(key);
return true;
}
if (count >= maxCount) {
logger.warn("請求過于頻繁請稍后再試");
return false;
}
}
return true;
} catch (Exception e) {
logger.warn("請求過于頻繁請稍后再試");
e.printStackTrace();
return true;
}
}
}
2.3 注冊攔截器并配置攔截路徑和不攔截路徑
/**
* @Description: 訪問攔截器配置
* @Author oyc
* @Date 2020/10/22 11:34 下午
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private AccessLimitInterceptor accessLimitInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessLimitInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/login.html", "/user/login");
}
}
2.4 使用AccessLimit
/**
* @Description:
* @Author oyc
*/
@RestController
@RequestMapping("access")
public class AccessLimitController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 限流測試
*/
@GetMapping
@AccessLimit(maxCount = 3, second = 60)
public String limit(HttpServletRequest request) {
logger.error("Access Limit Test");
return "限流測試";
}
}
2.5 測試
已經訪問的次數:null
2020-10-23 00:19:47.423 ERROR 1496 --- [nio-8080-exec-7] c.o.s.AccessLimitController : Access Limit Test
已經訪問的次數:1
2020-10-23 00:19:48.543 ERROR 1496 --- [nio-8080-exec-6] c.o.s.AccessLimitController : Access Limit Test
已經訪問的次數:2
2020-10-23 00:19:49.069 ERROR 1496 --- [nio-8080-exec-8] c.o.s.AccessLimitController : Access Limit Test
已經訪問的次數:3
2020-10-23 00:19:53.612 WARN 1496 --- [nio-8080-exec-9] c.o.s.limit.AccessLimitInterceptor : 請求過于頻繁請稍后再試
API限流在騰訊地圖開放平臺的應用主要體現在以下幾個方面:
源碼傳送門:
https://github.com/oycyqr/springboot-learning-demo/tree/master/springboot-validated
文章轉自微信公眾號@傳一卓躍