webflux token 验证 自定义注解

本文介绍了一种使用Spring WebFlux实现的自定义Token验证机制。通过定义TokenAuth注解来控制方法级别的验证,并在TokenFilter中实现了具体的验证逻辑。文章详细展示了如何根据注解属性决定是否进行验证,以及如何从请求头中获取并验证Token。

webflux token 验证 自定义注解

定义注解:


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TokenAuth {
    boolean validate() default true;
}

验证:

@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class TokenFilter implements WebFilter {

    @Resource
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Resource
    private RRRRRR rrrrrr;

    @Resource
    private ZZZZZZZZ  zzzz;

    private boolean flag;

    private String message;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        // 获取Response、Request
        ServerHttpResponse res = exchange.getResponse();
        ServerHttpRequest req = exchange.getRequest();
        
        // 判断是不是需要开启token验证
        (读取配置文件是否开启验证,可删除掉)
        // if (!zzzz.z1()) return chain.filter(exchange);

        // 获取请求对应的HandlerMethod
        Mono<HandlerMethod> handlerMethodMono = requestMappingHandlerMapping
                .getHandler(exchange).cast(HandlerMethod.class);
        handlerMethodMono.subscribe(handlerMethod -> {
        	// 判断方法头是否有注解
            flag = handlerMethod.hasMethodAnnotation(TokenAuth.class);
            if (!flag) return;
            
            // 获取自定义注解里面的 validate 判断是否为false,如果是,则不验证
            TokenAuth methodAnnotation = handlerMethod.getMethodAnnotation(TokenAuth.class);
            if (methodAnnotation == null || methodAnnotation.validate() == false){
                flag = false;
                return;
            }
	
		// 获取  token信息,进行判断
            HttpHeaders headers = req.getHeaders();
            String token = headers.getFirst("token");
            if (token != null){
            	// 开关控制,可以做一些逻辑判断,redis,其他判断
            	flag = false;	
            }
        }).dispose();
        
        if (!flag) return chain.filter(exchange);
        // 返回
        res.setStatusCode(HttpStatus.NON_AUTHORITATIVE_INFORMATION);
        byte[] bytes = message.getBytes();
        return res.writeWith(Mono.just(res.bufferFactory().wrap(bytes)));
    }
}

测试:

    @GetMapping(value = "")
    @TokenAuth(validate = true)
    public Mono<WapResponse<>> getXXXXXX() {
  
    }
<think>好的,我现在需要解决用户的问题:使用Java Reactor生成一个带有30分钟有效Token的链接,用于浏览器访问下载文件接口。首先,我得理解用户的需求。用户想要在Web应用中提供一个文件下载的链接,这个链接需要包含一个Token,该Token在30分钟后过期,确保链接的有效期受限,提高安全性。接下来,我需要考虑如何用Reactor来实现这个功能。Reactor是响应式编程库,强调异步和非阻塞处理。因此,整个流程需要符合响应式编程的范式。首先,生成Token的部分,可能需要使用到JWT(JSONWeb Token)或者自定义Token生成机制,并设置过期时间。然后,需要将Token与对应的文件信息存储起来,可能使用缓存如Redis,并设置30分钟的过期时间。最后,提供一个下载接口,验证Token的有效性,并返回文件流。首先,生成Token。JWT是一个常用的方案,可以方便地设置过期时间。或者,如果系统不想引入JWT,可以自己生成一个唯一的字符串作为Token,并记录在缓存中。这里两种方式都可以,但JWT可能更简洁,因为过期时间可以直接在Token中体现,不需要依赖缓存的过期机制。不过,使用缓存的话,即使Token被篡改,只要缓存中没有对应的记录,也会失效,可能更安全一些。需要权衡。假设用户选择JWT方案,那么需要引入相关的依赖,比如jjwt。然后,生成Token时设置30分钟的过期时间。同时,可能需要在Token中存储一些必要的信息,比如文件ID或路径,这样在验证时可以直接解析出来,而不需要查询数据库或缓存。不过,如果文件信息敏感,可能需要加密或者存储在服务端,这时候就需要将Token作为键,去缓存中查找对应的文件信息。 另一种方案是生成随机UUID作为Token,存入缓存(如Redis),并设置30分钟的TTL。当用户访问下载链接时,从请求参数中获取Token,查询缓存,若存在且未过期,则允许下载。这种方式更依赖缓存,但实现起来可能更简单,尤其是当文件信息较大或需要频繁变更时,缓存可以灵活处理。 接下来,整合Reactor。由于Reactor是响应式的,所有操作需要是非阻塞的。例如,生成Token、存储到缓存、验证Token等步骤都需要使用异步的方式。例如,使用ReactiveRedisTemplate来操作Redis,返回Mono或Flux对象。然后,设计下载接口。使用SpringWebFlux注解方式定义路由,处理GET请求,提取Token参数,验证有效性,然后返回文件流。文件流的响应也需要是响应式的,比如使用DataBuffer来分块传输。还需要考虑异常处理,比如Token无效或过期时,返回相应的HTTP错误状态。Reactor中的错误处理可以通过onErrorResume等方法来实现,保持响应式链的完整性。现在,具体步骤可能如下:1.生成Token(JWT或UUID)。 2.存储Token及相关信息到缓存,设置30分钟过期。3.创建下载链接,包含Token作为参数。4.实现下载接口,验证Token,返回文件。 代码示例方面,需要展示如何用Reactor实现这些步骤。比如,使用Mono.defer生成Token,然后使用flatMap将Token存储到Redis,最后生成链接。下载接口中,从请求中获取Token,查询Redis,如果存在则返回文件,否则返回错误。需要注意,Reactor的操作是惰性的,需要确保每个步骤都正确订阅。例如,存储Token到Redis时,需要确保写操作完成,可能需要使用then()来返回结果。 另外,关于时间处理,生成Token的时间点需要记录,或者在JWT中设置expiration时间。对于Redis,设置TTL为30分钟即可。可能的相关问题包括如何选择Token生成方式、Reactor与缓存的集成、响应式文件下载的实现,以及如何测试这类接口。需要确保每个环节都符合响应式编程的要求,避免阻塞操作。 总结下来,关键点在于Token生成与存储的响应式处理,以及下载接口的非阻塞验证和文件流返回。需要确保整个流程的异步性和正确性,同时处理好错误情况。</think>### 分步实现方案 #### 1. Token生成与存储 使用JWT生成带有过期时间的Token,并存储文件元数据到Redis: ```java // 添加依赖:io.jsonwebtoken:jjwt-api, io.jsonwebtoken:jjwt-impl, io.jsonwebtoken:jjwt-jackson public Mono<String> generateDownloadToken(String fileId) { String secretKey = "your-256-bit-secret"; Instant now = Instant.now(); Instant expiry = now.plus(30, ChronoUnit.MINUTES); String token = Jwts.builder() .setSubject(fileId) .setIssuedAt(Date.from(now)) .setExpiration(Date.from(expiry)) .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) .compact(); return reactiveRedisTemplate.opsForValue() .set(token, fileId, Duration.ofMinutes(30)) .thenReturn(token); } ``` #### 2. 创建下载链接 ```java public Mono<String> createDownloadLink(String fileId) { return generateDownloadToken(fileId) .map(token -> "https://yourdomain.com/download?token=" + token); } ``` #### 3. 文件下载接口 使用Spring WebFlux实现响应式文件下载路由: ```java @GetMapping("/download") public Mono<ResponseEntity<Resource>> downloadFile( @RequestParam String token, ServerWebExchange exchange ) { return reactiveRedisTemplate.opsForValue().get(token) .flatMap(fileId -> { if (fileId == null) { return Mono.error(new InvalidTokenException()); } Resource fileResource = new FileSystemResource("/path/to/files/" + fileId); return Mono.just(ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileResource.getFilename() + "\"") .body(fileResource)); }) .onErrorResume(InvalidTokenException.class, e -> Mono.just(ResponseEntity.status(HttpStatus.FORBIDDEN).build())); } ``` #### 4. 异常处理 ```java public class InvalidTokenException extends RuntimeException { // 自定义异常处理逻辑 } ``` ### 流程图解析 ```mermaid graph TD A[用户请求下载链接] --> B{验证文件权限} B -->|通过| C[生成JWT Token] C --> D[存储Token到Redis] D --> E[返回含Token的URL] E --> F[用户访问URL] F --> G{验证Token有效性} G -->|有效| H[返回文件流] G -->|无效| I[返回403错误] ``` ### 技术验证点 1. **响应式存储操作**:使用`ReactiveRedisTemplate`确保Redis操作不阻塞线程[^3] 2. **时间敏感校验**:JWT自带过期时间校验+Redis双重验证机制 3. **背压控制**:文件下载使用`Resource`对象自动处理背压
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值