diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/endpoint/ImageCodeEndpoint.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/endpoint/ImageCodeEndpoint.java new file mode 100644 index 00000000..dce5fbfd --- /dev/null +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/endpoint/ImageCodeEndpoint.java @@ -0,0 +1,48 @@ +package com.pig4cloud.pig.auth.endpoint; + +import com.pig4cloud.pig.common.core.constant.CacheConstants; +import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import io.springboot.captcha.ArithmeticCaptcha; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.TimeUnit; + +/** + * 验证码相关的接口 + * + * @author lengleng + * @date 2022/6/27 + */ +@RestController +@RequestMapping("/code") +@RequiredArgsConstructor +public class ImageCodeEndpoint { + + private static final Integer DEFAULT_IMAGE_WIDTH = 100; + + private static final Integer DEFAULT_IMAGE_HEIGHT = 40; + + private final RedisTemplate redisTemplate; + + /** + * 创建图形验证码 + */ + @SneakyThrows + @GetMapping("/image") + public void image(String randomStr, HttpServletResponse response) { + ArithmeticCaptcha captcha = new ArithmeticCaptcha(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT); + + String result = captcha.text(); + redisTemplate.opsForValue() + .set(CacheConstants.DEFAULT_CODE_KEY + randomStr, result, SecurityConstants.CODE_TIME, TimeUnit.SECONDS); + // 转换流信息写出 + captcha.out(response.getOutputStream()); + } + +} diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/PasswordDecoderFilter.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/PasswordDecoderFilter.java new file mode 100755 index 00000000..76227b63 --- /dev/null +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/PasswordDecoderFilter.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pig.auth.support.filter; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.Mode; +import cn.hutool.crypto.Padding; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.AES; +import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import com.pig4cloud.pig.common.core.servlet.RepeatBodyRequestWrapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.util.Map; + +/** + * @author lengleng + * @date 2019 /2/1 密码解密工具类 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class PasswordDecoderFilter extends OncePerRequestFilter { + + private final AuthSecurityConfigProperties authSecurityConfigProperties; + + private static final String PASSWORD = "password"; + + private static final String KEY_ALGORITHM = "AES"; + + + static { + // 关闭hutool 强制关闭Bouncy Castle库的依赖 + SecureUtil.disableBouncyCastle(); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { + // 不是登录请求,直接向下执行 + if (!StrUtil.containsAnyIgnoreCase(request.getRequestURI(), SecurityConstants.OAUTH_TOKEN_URL)) { + chain.doFilter(request, response); + return; + } + + // 将请求流转换为可多次读取的请求流 + RepeatBodyRequestWrapper requestWrapper = new RepeatBodyRequestWrapper(request); + Map parameterMap = requestWrapper.getParameterMap(); + + // 构建前端对应解密AES 因子 + AES aes = new AES(Mode.CFB, Padding.NoPadding, + new SecretKeySpec(authSecurityConfigProperties.getEncodeKey().getBytes(), KEY_ALGORITHM), + new IvParameterSpec(authSecurityConfigProperties.getEncodeKey().getBytes())); + + + parameterMap.forEach((k, v) -> { + String[] values = parameterMap.get(k); + if (!PASSWORD.equals(k) || ArrayUtil.isEmpty(values)) { + return; + } + + // 解密密码 + String decryptPassword = aes.decryptStr(values[0]); + parameterMap.put(k, new String[]{decryptPassword}); + }); + chain.doFilter(requestWrapper, response); + } + + +} diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/ValidateCodeFilter.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/ValidateCodeFilter.java new file mode 100644 index 00000000..a0e6d28f --- /dev/null +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/support/filter/ValidateCodeFilter.java @@ -0,0 +1,126 @@ +package com.pig4cloud.pig.auth.support.filter; + +/** + * 登录前处理器 + * + * @author lengleng + * @date 2024/4/3 + */ + + +import cn.hutool.core.util.StrUtil; +import com.pig4cloud.pig.common.core.constant.CacheConstants; +import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import com.pig4cloud.pig.common.core.exception.ValidateCodeException; +import com.pig4cloud.pig.common.core.util.SpringContextHolder; +import com.pig4cloud.pig.common.core.util.WebUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Optional; + +/** + * @author lbw + * @date 2024-01-06 + *

+ * 登录前置处理器: 前端密码传输密文解密,验证码处理 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class ValidateCodeFilter extends OncePerRequestFilter { + + private final AuthSecurityConfigProperties authSecurityConfigProperties; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + + String requestUrl = request.getServletPath(); + + // 不是登录URL 请求直接跳过 + if (!SecurityConstants.OAUTH_TOKEN_URL.equals(requestUrl)) { + filterChain.doFilter(request, response); + return; + } + + // 如果登录URL 但是刷新token的请求,直接向下执行 + String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); + if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) { + filterChain.doFilter(request, response); + return; + } + + // 客户端配置跳过验证码 + boolean isIgnoreClient = authSecurityConfigProperties.getIgnoreClients().contains(WebUtils.getClientId()); + if (isIgnoreClient) { + filterChain.doFilter(request, response); + return; + } + + // 校验验证码 1. 客户端开启验证码 2. 短信模式 + try { + checkCode(); + filterChain.doFilter(request, response); + } catch (ValidateCodeException validateCodeException) { + throw new OAuth2AuthenticationException(validateCodeException.getMessage()); + } + } + + /** + * 校验验证码 + */ + private void checkCode() throws ValidateCodeException { + Optional request = WebUtils.getRequest(); + String code = request.get().getParameter("code"); + + if (StrUtil.isBlank(code)) { + throw new ValidateCodeException("验证码不能为空"); + } + + String randomStr = request.get().getParameter("randomStr"); + + // https://gitee.com/log4j/pig/issues/IWA0D + String mobile = request.get().getParameter("mobile"); + if (StrUtil.isNotBlank(mobile)) { + randomStr = mobile; + } + + String key = CacheConstants.DEFAULT_CODE_KEY + randomStr; + RedisTemplate redisTemplate = SpringContextHolder.getBean(RedisTemplate.class); + if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) { + throw new ValidateCodeException("验证码不合法"); + } + + Object codeObj = redisTemplate.opsForValue().get(key); + + if (codeObj == null) { + throw new ValidateCodeException("验证码不合法"); + } + + String saveCode = codeObj.toString(); + if (StrUtil.isBlank(saveCode)) { + redisTemplate.delete(key); + throw new ValidateCodeException("验证码不合法"); + } + + if (!StrUtil.equals(saveCode, code)) { + redisTemplate.delete(key); + throw new ValidateCodeException("验证码不合法"); + } + + redisTemplate.delete(key); + } + +} + diff --git a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/servlet/RepeatBodyRequestWrapper.java b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/servlet/RepeatBodyRequestWrapper.java new file mode 100644 index 00000000..e173f1f5 --- /dev/null +++ b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/servlet/RepeatBodyRequestWrapper.java @@ -0,0 +1,103 @@ +/* + * Copyright 2023-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pig4cloud.pig.common.core.servlet; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StreamUtils; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Map; + +/** + * Request包装类:允许 body 重复读取 + * + * @author Hccake + */ +@Slf4j +public class RepeatBodyRequestWrapper extends HttpServletRequestWrapper { + + private final byte[] bodyByteArray; + + private final Map parameterMap; + + public RepeatBodyRequestWrapper(HttpServletRequest request) { + super(request); + this.bodyByteArray = getByteBody(request); + this.parameterMap = super.getParameterMap(); + } + + @Override + public BufferedReader getReader() { + return ObjectUtils.isEmpty(this.bodyByteArray) ? null + : new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.bodyByteArray); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + // doNoting + } + + @Override + public int read() { + return byteArrayInputStream.read(); + } + }; + } + + private static byte[] getByteBody(HttpServletRequest request) { + byte[] body = new byte[0]; + try { + body = StreamUtils.copyToByteArray(request.getInputStream()); + } catch (IOException e) { + log.error("解析流中数据异常", e); + } + return body; + } + + /** + * 重写 getParameterMap() 方法 解决 undertow 中流被读取后,会进行标记,从而导致无法正确获取 body 中的表单数据的问题 + * + * @return Map parameterMap + */ + @Override + public Map getParameterMap() { + return this.parameterMap; + } + +} diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/GatewayConfigProperties.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/GatewayConfigProperties.java deleted file mode 100644 index 4ca748de..00000000 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/GatewayConfigProperties.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.pig4cloud.pig.gateway.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cloud.context.config.annotation.RefreshScope; - -import java.util.List; - -/** - * @author lengleng - * @date 2020/10/4 - *

- * 网关配置文件 - */ -@Data -@RefreshScope -@ConfigurationProperties("gateway") -public class GatewayConfigProperties { - - /** - * 网关解密登录前端密码 秘钥 {@link com.pig4cloud.pig.gateway.filter.PasswordDecoderFilter} - */ - private String encodeKey; - - /** - * 网关不需要校验验证码的客户端 {@link com.pig4cloud.pig.gateway.filter.ValidateCodeGatewayFilter} - */ - private List ignoreClients; - -} diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/GatewayConfiguration.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/GatewayConfiguration.java index 42c3b7bf..8b8295fa 100644 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/GatewayConfiguration.java +++ b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/GatewayConfiguration.java @@ -1,16 +1,10 @@ package com.pig4cloud.pig.gateway.config; import com.fasterxml.jackson.databind.ObjectMapper; -import com.pig4cloud.pig.gateway.filter.PasswordDecoderFilter; import com.pig4cloud.pig.gateway.filter.PigRequestGlobalFilter; -import com.pig4cloud.pig.gateway.filter.ValidateCodeGatewayFilter; import com.pig4cloud.pig.gateway.handler.GlobalExceptionHandler; -import com.pig4cloud.pig.gateway.handler.ImageCodeHandler; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.core.RedisTemplate; /** * 网关配置 @@ -18,21 +12,8 @@ import org.springframework.data.redis.core.RedisTemplate; * @author L.cm */ @Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(GatewayConfigProperties.class) public class GatewayConfiguration { - /** - * 创建密码解码器过滤器 - * @param modifyRequestBodyGatewayFilterFactory 修改请求体网关过滤器工厂 - * @param configProperties 配置属性 - * @return 密码解码器过滤器 - */ - @Bean - public PasswordDecoderFilter passwordDecoderFilter( - ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory, - GatewayConfigProperties configProperties) { - return new PasswordDecoderFilter(modifyRequestBodyGatewayFilterFactory, configProperties); - } /** * 创建PigRequest全局过滤器 @@ -43,17 +24,6 @@ public class GatewayConfiguration { return new PigRequestGlobalFilter(); } - /** - * 创建验证码网关过滤器 - * @param configProperties 配置属性 - * @param redisTemplate Redis模板 - * @return 验证码网关过滤器 - */ - @Bean - public ValidateCodeGatewayFilter validateCodeGatewayFilter(GatewayConfigProperties configProperties, - RedisTemplate redisTemplate) { - return new ValidateCodeGatewayFilter(configProperties, redisTemplate); - } /** * 创建全局异常处理程序 @@ -65,14 +35,5 @@ public class GatewayConfiguration { return new GlobalExceptionHandler(objectMapper); } - /** - * 创建图片验证码处理器 - * @param redisTemplate Redis模板 - * @return 图片验证码处理器 - */ - @Bean - public ImageCodeHandler imageCodeHandler(RedisTemplate redisTemplate) { - return new ImageCodeHandler(redisTemplate); - } } diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/RouterFunctionConfiguration.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/RouterFunctionConfiguration.java deleted file mode 100755 index df44a94d..00000000 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/config/RouterFunctionConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pig4cloud.pig.gateway.config; - -import com.pig4cloud.pig.gateway.handler.ImageCodeHandler; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.MediaType; -import org.springframework.web.reactive.function.server.RequestPredicates; -import org.springframework.web.reactive.function.server.RouterFunction; -import org.springframework.web.reactive.function.server.RouterFunctions; -import org.springframework.web.reactive.function.server.ServerResponse; - -/** - * 路由配置信息 - * - * @author lengleng - * @date 2020-06-11 - */ -@Slf4j -@Configuration(proxyBeanMethods = false) -@RequiredArgsConstructor -public class RouterFunctionConfiguration { - - private final ImageCodeHandler imageCodeHandler; - - @Bean - public RouterFunction routerFunction() { - return RouterFunctions.route( - RequestPredicates.path("/code/image").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), - imageCodeHandler); - } - -} diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/PasswordDecoderFilter.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/PasswordDecoderFilter.java deleted file mode 100755 index d6ce2e9c..00000000 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/PasswordDecoderFilter.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pig4cloud.pig.gateway.filter; - -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.Mode; -import cn.hutool.crypto.Padding; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.symmetric.AES; -import cn.hutool.http.HttpUtil; -import com.pig4cloud.pig.common.core.constant.SecurityConstants; -import com.pig4cloud.pig.gateway.config.GatewayConfigProperties; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cloud.gateway.filter.GatewayFilter; -import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; -import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; -import org.springframework.http.server.reactive.ServerHttpRequest; -import reactor.core.publisher.Mono; - -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.nio.charset.Charset; -import java.util.Map; - -/** - * @author lengleng - * @date 2019 /2/1 密码解密工具类 - */ -@Slf4j -@RequiredArgsConstructor -public class PasswordDecoderFilter extends AbstractGatewayFilterFactory { - - private final ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter; - - private static final String PASSWORD = "password"; - - private static final String KEY_ALGORITHM = "AES"; - - private final GatewayConfigProperties gatewayConfig; - - static { - // 关闭hutool 强制关闭Bouncy Castle库的依赖 - SecureUtil.disableBouncyCastle(); - } - - @Override - public GatewayFilter apply(Object config) { - return (exchange, chain) -> { - ServerHttpRequest request = exchange.getRequest(); - // 不是登录请求,直接向下执行 - if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) { - return chain.filter(exchange); - } - - return modifyRequestBodyFilter - .apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(String.class, String.class, - (webExchange, body) -> Mono.just(modifyRequestPassword(body)))) - .filter(exchange, chain); - }; - } - - /** - * 修改请求报文的密码密文为名为 - * @param requestBody 请求报文 - * @return 修改后的报文 - */ - private String modifyRequestPassword(String requestBody) { - // 构建前端对应解密AES 因子 - AES aes = new AES(Mode.CFB, Padding.NoPadding, - new SecretKeySpec(gatewayConfig.getEncodeKey().getBytes(), KEY_ALGORITHM), - new IvParameterSpec(gatewayConfig.getEncodeKey().getBytes())); - - // 获取请求密码并解密 - Map inParamsMap = HttpUtil.decodeParamMap(requestBody, CharsetUtil.CHARSET_UTF_8); - if (inParamsMap.containsKey(PASSWORD)) { - String password = aes.decryptStr(inParamsMap.get(PASSWORD)); - // 返回修改后报文字符 - inParamsMap.put(PASSWORD, password); - } - - return HttpUtil.toParams(inParamsMap, Charset.defaultCharset(), true); - } - -} diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ValidateCodeGatewayFilter.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ValidateCodeGatewayFilter.java deleted file mode 100644 index 148135b8..00000000 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ValidateCodeGatewayFilter.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pig4cloud.pig.gateway.filter; - -import cn.hutool.core.text.CharSequenceUtil; -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.HttpUtil; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.pig4cloud.pig.common.core.constant.CacheConstants; -import com.pig4cloud.pig.common.core.constant.SecurityConstants; -import com.pig4cloud.pig.common.core.exception.ValidateCodeException; -import com.pig4cloud.pig.common.core.util.WebUtils; -import com.pig4cloud.pig.gateway.config.GatewayConfigProperties; -import lombok.RequiredArgsConstructor; -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.cloud.gateway.support.ServerWebExchangeUtils; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.http.server.reactive.ServerHttpRequest; - -import java.nio.CharBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.Objects; - -/** - * The type Validate code gateway filter. - * - * @author lengleng - * @date 2018 /7/4 验证码处理 - */ -@Slf4j -@RequiredArgsConstructor -public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory { - - private final GatewayConfigProperties configProperties; - - private final RedisTemplate redisTemplate; - - /** - * 应用网关过滤器 - * @param config 配置对象 - * @return 网关过滤器 - */ - @Override - public GatewayFilter apply(Object config) { - - return (exchange, chain) -> { - ServerHttpRequest request = exchange.getRequest(); - // 不是登录请求,直接向下执行 - if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) { - return chain.filter(exchange); - } - - // 客户端配置跳过,直接向下执行 - boolean isIgnoreClient = configProperties.getIgnoreClients().contains(WebUtils.getClientId(request)); - if (isIgnoreClient) { - return chain.filter(exchange); - } - - // 构建缓存body,可重复读获取form data - return ServerWebExchangeUtils.cacheRequestBody(exchange, (serverHttpRequest) -> { - // get cacheRequestBody - DataBuffer cachedRequestBody = exchange.getAttribute("cachedRequestBody"); - CharBuffer charBuffer = StandardCharsets.UTF_8 - .decode(Objects.requireNonNull(cachedRequestBody).asByteBuffer()); - Map requestBodyMap = HttpUtil.decodeParamMap(charBuffer.toString(), - CharsetUtil.CHARSET_UTF_8); - // 刷新请求跳过,直接向下执行 - if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, requestBodyMap.get("grant_type"))) { - return chain.filter(exchange.mutate().request(serverHttpRequest).build()); - } - - // 根据 randomStr 参数判断验证码是否正常 - String code = requestBodyMap.get("code"); - String randomStr = requestBodyMap.getOrDefault("randomStr", - requestBodyMap.get(SecurityConstants.SMS_PARAMETER_NAME)); - checkCode(code, randomStr); - - return chain.filter(exchange.mutate().request(serverHttpRequest).build()); - }); - }; - } - - /** - * 检查验证码,错误扔出 ValidateCodeException GlobalExceptionHandler统一处理 - * @param code 验证码 - * @param randomStr 请求参数 - * @throws ValidateCodeException 验证码异常 - */ - @SneakyThrows - private void checkCode(String code, String randomStr) { - if (CharSequenceUtil.isBlank(code)) { - throw new ValidateCodeException("验证码不能为空"); - } - - String key = CacheConstants.DEFAULT_CODE_KEY + randomStr; - - Object codeObj = redisTemplate.opsForValue().get(key); - - if (ObjectUtil.isEmpty(codeObj) || !code.equals(codeObj)) { - throw new ValidateCodeException("验证码不合法"); - } - - redisTemplate.delete(key); - } - -} diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/handler/ImageCodeHandler.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/handler/ImageCodeHandler.java deleted file mode 100755 index d401497a..00000000 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/handler/ImageCodeHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pig4cloud.pig.gateway.handler; - -import com.pig4cloud.captcha.ArithmeticCaptcha; -import com.pig4cloud.pig.common.core.constant.CacheConstants; -import com.pig4cloud.pig.common.core.constant.SecurityConstants; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.StringRedisSerializer; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.util.FastByteArrayOutputStream; -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; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -/** - * @author lengleng - * @date 2018/7/5 验证码生成逻辑处理类 - */ -@Slf4j -@RequiredArgsConstructor -public class ImageCodeHandler implements HandlerFunction { - - private static final Integer DEFAULT_IMAGE_WIDTH = 100; - - private static final Integer DEFAULT_IMAGE_HEIGHT = 40; - - private final RedisTemplate redisTemplate; - - @Override - public Mono handle(ServerRequest serverRequest) { - ArithmeticCaptcha captcha = new ArithmeticCaptcha(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT); - - String result = captcha.text(); - - // 保存验证码信息 - Optional randomStr = serverRequest.queryParam("randomStr"); - redisTemplate.setKeySerializer(new StringRedisSerializer()); - randomStr.ifPresent(s -> redisTemplate.opsForValue() - .set(CacheConstants.DEFAULT_CODE_KEY + s, result, SecurityConstants.CODE_TIME, TimeUnit.SECONDS)); - - // 转换流信息写出 - FastByteArrayOutputStream os = new FastByteArrayOutputStream(); - captcha.out(os); - - return ServerResponse.status(HttpStatus.OK) - .contentType(MediaType.IMAGE_JPEG) - .body(BodyInserters.fromResource(new ByteArrayResource(os.toByteArray()))); - } - -}