Skip to content

Commit

Permalink
✨ feat: 为生成验证码添加响应式调度程序 (boot/platform/src/main/java/com/platform/boo…
Browse files Browse the repository at this point in the history
…t/security/core/captcha/CaptchaController.java)
  • Loading branch information
vnobo committed May 30, 2024
1 parent ce449e3 commit 433f399
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.io.IOException;
import java.io.OutputStream;
Expand All @@ -28,9 +29,10 @@ public class CaptchaController {
@GetMapping("code")
public Mono<ResponseEntity<DataBuffer>> getCaptcha(ServerWebExchange exchange) {
DataBuffer dataBuffer = exchange.getResponse().bufferFactory().allocateBuffer(2048);
return captchaTokenRepository.loadToken(exchange).flatMap(captchaToken -> {
try (OutputStream ignored = dataBuffer.asOutputStream()) {
//captchaToken.getCaptcha().write(outputStream);
return this.captchaTokenRepository.generateToken(exchange)
.publishOn(Schedulers.boundedElastic()).flatMap(captchaToken -> {
try (OutputStream outputStream = dataBuffer.asOutputStream()) {
outputStream.write(captchaToken.getCaptcha().getBytes());
return Mono.just(ResponseEntity.ok().contentType(MediaType.IMAGE_PNG).body(dataBuffer))
.delayUntil((a) -> this.captchaTokenRepository.saveToken(exchange, captchaToken));
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ public class CaptchaRepository {
protected String headerName = DEFAULT_CAPTCHA_HEADER_NAME;
protected String sessionAttributeName = DEFAULT_CAPTCHA_TOKEN_ATTR_NAME;

/**
* This method is used to generate a CaptchaToken and store it in the exchange
* for later use.
*
* @param exchange the exchange
* @return a Mono that encapsulates the CaptchaToken
*/
public Mono<CaptchaToken> generateToken(ServerWebExchange exchange) {
return Mono.fromCallable(this::createCaptchaToken)
.doOnNext(captchaToken -> exchange.getAttributes().put(CaptchaToken.class.getName(), captchaToken))
Expand Down Expand Up @@ -59,11 +52,9 @@ public Mono<CaptchaToken> loadToken(ServerWebExchange exchange) {
}

protected CaptchaToken createCaptchaToken() {
// 定义图形验证码的长、宽、验证码字符数、干扰元素个数
// LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 10);
// captcha.setGenerator(new RandomGenerator("0123456789", 4));
return CaptchaToken.of(this.headerName, this.parameterName, null);

//LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 10);
//captcha.setGenerator(new RandomGenerator("0123456789", 4));
return CaptchaToken.of(this.headerName, this.parameterName, "54321");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.platform.boot.security.core.captcha.CaptchaRepository;
import com.platform.boot.security.core.captcha.CaptchaToken;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
Expand All @@ -14,7 +15,6 @@
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
Expand All @@ -28,29 +28,24 @@
* @author <a href="https://github.com/vnobo">Alex bob</a>
*/
@Log4j2
@Component
@RequiredArgsConstructor
public class CaptchaFilter implements WebFilter, Ordered {

public static final ServerWebExchangeMatcher DEFAULT_CAPTCHA_MATCHER =
private static final ServerAccessDeniedHandler ACCESS_DENIED_HANDLER =
new CaptchaServerAccessDeniedHandler(HttpStatus.FORBIDDEN);
private static final ServerWebExchangeMatcher REQUIRE_CAPTCHA_PROTECTION_MATCHER =
new PathPatternParserServerWebExchangeMatcher("/oauth2/token");
private static final ServerAccessDeniedHandler ACCESS_DENIED_HANDLER = new CaptchaServerAccessDeniedHandler(
HttpStatus.FORBIDDEN);
private static final ServerWebExchangeMatcher REQUIRE_CAPTCHA_PROTECTION_MATCHER = DEFAULT_CAPTCHA_MATCHER;

private final CaptchaRepository captchaTokenRepository;

public CaptchaFilter(CaptchaRepository captchaTokenRepository) {
this.captchaTokenRepository = captchaTokenRepository;
}

@Override
public @NonNull Mono<Void> filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) {
return REQUIRE_CAPTCHA_PROTECTION_MATCHER.matches(exchange)
.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
.switchIfEmpty(Mono.defer(() -> continueFilterChain(exchange, chain).then(Mono.empty())))
.flatMap(matchResult -> exchange.getSession()
.filter(webSession -> webSession.getAttributes().containsKey(DEFAULT_CAPTCHA_TOKEN_ATTR_NAME))
.flatMap(webSession -> validateToken(exchange)))
.then(Mono.defer(() -> continueFilterChain(exchange, chain)))
.onErrorResume(CaptchaException.class, ex -> ACCESS_DENIED_HANDLER.handle(exchange, ex));
}

Expand All @@ -61,22 +56,22 @@ private Mono<Void> continueFilterChain(ServerWebExchange exchange, WebFilterChai

private Mono<Void> validateToken(ServerWebExchange exchange) {
return this.captchaTokenRepository.loadToken(exchange)
.switchIfEmpty(Mono.defer(() -> Mono.error(new CaptchaException("An expected Captcha token cannot be found"))))
.filterWhen((expected) -> containsValidCaptchaToken(exchange, expected))
.switchIfEmpty(Mono.defer(() -> Mono.error(new CaptchaException("Invalid Captcha Token"))))
.switchIfEmpty(Mono.defer(() ->
Mono.error(new CaptchaException("An expected Captcha token cannot be found"))))
.filterWhen((captchaToken) -> containsValidCaptchaToken(exchange, captchaToken))
.switchIfEmpty(Mono.defer(() ->
Mono.error(new CaptchaException("Invalid Captcha Token"))))
.then(this.captchaTokenRepository.clearToken(exchange));
}

private Mono<Boolean> containsValidCaptchaToken(ServerWebExchange exchange, CaptchaToken expected) {
return this.resolveCaptchaTokenValue(exchange, expected).map((actual) -> true);
private Mono<Boolean> containsValidCaptchaToken(ServerWebExchange exchange, CaptchaToken captchaToken) {
return this.resolveCaptchaTokenValue(exchange, captchaToken)
.map((actual) -> captchaToken.getCaptcha().equals(actual));
}

private Mono<String> resolveCaptchaTokenValue(ServerWebExchange exchange, CaptchaToken captchaToken) {
Assert.notNull(exchange, "exchange cannot be null");
Assert.notNull(captchaToken, "captchaToken cannot be null");
return exchange.getFormData().flatMap((data) -> Mono.justOrEmpty(data.getFirst(captchaToken.getParameterName())))
.switchIfEmpty(Mono.defer(() ->
Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst(captchaToken.getHeaderName()))));
String captchaCode = exchange.getRequest().getHeaders().getFirst(captchaToken.getHeaderName());
return Mono.justOrEmpty(captchaCode);
}

@Override
Expand Down

0 comments on commit 433f399

Please sign in to comment.