特别说明: 注意这里验证码开关,指的是服务端不会校验请求的是否携带验证码及其正确性。 不是前端不显示验证码
nacos 配置
pigx-gateway-dev.yml
不校验验证码终端
ignore:
clients:
- 不校验验证码的终端client-id
webflux 生成验证码的 handler
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hzjt.gczjt.common.core.constant.CommonConstants;
import com.hzjt.gczjt.common.core.util.R;
import com.hzjt.gczjt.common.core.util.SpringContextHolder;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
/**
@author gczjt
@date 2020/5/19 验证码生成逻辑处理类
*/
@Slf4j
@Component
@AllArgsConstructor
public class ImageCodeCheckHandler implements HandlerFunction{ private final ObjectMapper objectMapper;
@Override
@SneakyThrows
public Monohandle(ServerRequest request) { CaptchaVO vo = new CaptchaVO(); vo.setPointJson(request.queryParam("pointJson").get()); vo.setToken(request.queryParam("token").get()); vo.setCaptchaType(CommonConstants.IMAGE_CODE_TYPE); CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class); ResponseModel responseModel = captchaService.check(vo); return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(objectMapper.writeValueAsString(R.ok(responseModel))));
}
}
webflux 请求处理入口
public class RouterFunctionConfiguration {
private final ImageCodeCheckHandler imageCodeCheckHandler;
private final ImageCodeCreateHandler imageCodeCreateHandler;
private final SwaggerResourceHandler swaggerResourceHandler;
private final SwaggerSecurityHandler swaggerSecurityHandler;
private final SwaggerUiHandler swaggerUiHandler;
@Bean
public RouterFunction routerFunction() {
return RouterFunctions
.route(RequestPredicates.path("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
imageCodeCreateHandler)
校验逻辑,通过 oauth2 终端的 client-id 来确定是否校验验证码
package com.hzjt.gczjt.gateway.filter;
import cn.hutool.core.util.StrUtil;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hzjt.gczjt.common.core.constant.CacheConstants;
import com.hzjt.gczjt.common.core.constant.CommonConstants;
import com.hzjt.gczjt.common.core.constant.SecurityConstants;
import com.hzjt.gczjt.common.core.constant.enums.LoginTypeEnum;
import com.hzjt.gczjt.common.core.exception.ValidateCodeException;
import com.hzjt.gczjt.common.core.util.R;
import com.hzjt.gczjt.common.core.util.SpringContextHolder;
import com.hzjt.gczjt.common.core.util.WebUtils;
import com.hzjt.gczjt.gateway.config.FilterIgnorePropertiesConfig;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
/**
@author gczjt
@date 2020/5/19 登录逻辑验证码处理
*/
@Slf4j
@Component
@AllArgsConstructor
public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {private final ObjectMapper objectMapper;
private final RedisTemplate redisTemplate;
private final FilterIgnorePropertiesConfig filterIgnorePropertiesConfig;
@Override
public GatewayFilter apply(Object config) {return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); // 不是登录请求,直接向下执行 if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL, SecurityConstants.SMS_TOKEN_URL, SecurityConstants.SOCIAL_TOKEN_URL)) { return chain.filter(exchange); } // 刷新token,直接向下执行 String grantType = request.getQueryParams().getFirst("grant_type"); if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) { return chain.filter(exchange); } // 终端设置不校验, 直接向下执行 try { String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); String clientId = WebUtils.extractClientId(header).orElse(null); if (StrUtil.isNotBlank(clientId) && filterIgnorePropertiesConfig.getClients().contains(clientId)) { return chain.filter(exchange); } // 如果是社交登录,判断是否包含SMS if (StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.SOCIAL_TOKEN_URL)) { String mobile = request.getQueryParams().getFirst("mobile"); if (StrUtil.containsAny(mobile, LoginTypeEnum.SMS.getType())) { throw new ValidateCodeException("验证码不合法"); } else { return chain.filter(exchange); } } // 校验验证码 checkCode(request); } catch (Exception e) { ServerHttpResponse response = exchange.getResponse(); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED); try { return response.writeWith(Mono.just( response.bufferFactory().wrap(objectMapper.writeValueAsBytes(R.failed(e.getMessage()))))); } catch (JsonProcessingException e1) { log.error("对象输出异常", e1); } } return chain.filter(exchange); };
}