diff --git a/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaController.java b/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaController.java index f1e81d00..4682f7d9 100644 --- a/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaController.java +++ b/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaController.java @@ -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; @@ -28,9 +29,10 @@ public class CaptchaController { @GetMapping("code") public Mono> 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) { diff --git a/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaRepository.java b/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaRepository.java index 93d6d44c..69fe54aa 100644 --- a/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaRepository.java +++ b/boot/platform/src/main/java/com/platform/boot/security/core/captcha/CaptchaRepository.java @@ -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 generateToken(ServerWebExchange exchange) { return Mono.fromCallable(this::createCaptchaToken) .doOnNext(captchaToken -> exchange.getAttributes().put(CaptchaToken.class.getName(), captchaToken)) @@ -59,11 +52,9 @@ public Mono 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"); } } \ No newline at end of file diff --git a/boot/platform/src/main/java/com/platform/boot/security/filter/CaptchaFilter.java b/boot/platform/src/main/java/com/platform/boot/security/filter/CaptchaFilter.java index b24f9d00..f8125a16 100644 --- a/boot/platform/src/main/java/com/platform/boot/security/filter/CaptchaFilter.java +++ b/boot/platform/src/main/java/com/platform/boot/security/filter/CaptchaFilter.java @@ -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; @@ -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; @@ -28,29 +28,24 @@ * @author Alex bob */ @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 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)); } @@ -61,22 +56,22 @@ private Mono continueFilterChain(ServerWebExchange exchange, WebFilterChai private Mono 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 containsValidCaptchaToken(ServerWebExchange exchange, CaptchaToken expected) { - return this.resolveCaptchaTokenValue(exchange, expected).map((actual) -> true); + private Mono containsValidCaptchaToken(ServerWebExchange exchange, CaptchaToken captchaToken) { + return this.resolveCaptchaTokenValue(exchange, captchaToken) + .map((actual) -> captchaToken.getCaptcha().equals(actual)); } private Mono 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