diff --git a/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java b/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java index df65a7f214fa1f3a63dbdf1985812f94467d0880..2dc684b954ba784022d7d77381089f8ad12710d1 100755 --- a/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java +++ b/pig-auth/src/main/java/com/pig4cloud/pig/auth/config/AuthorizationServerConfig.java @@ -25,6 +25,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; @@ -49,7 +50,7 @@ import java.util.Map; @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { private final DataSource dataSource; - private final org.springframework.security.core.userdetails.UserDetailsService UserDetailsService; + private final UserDetailsService userDetailsService; private final AuthenticationManager authenticationManager; private final RedisConnectionFactory redisConnectionFactory; @@ -74,7 +75,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) .tokenStore(tokenStore()) .tokenEnhancer(tokenEnhancer()) - .userDetailsService(UserDetailsService) + .userDetailsService(userDetailsService) .authenticationManager(authenticationManager) .reuseRefreshTokens(false) .exceptionTranslator(new PigWebResponseExceptionTranslator()); diff --git a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/CommonConstants.java b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/CommonConstants.java index c8c2f0191b9ea19b42d727b9722b10c8cf67af57..0cf370b3639cec9e9690247a6d47c99bbf8af70a 100644 --- a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/CommonConstants.java +++ b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/constant/CommonConstants.java @@ -69,4 +69,8 @@ public interface CommonConstants { */ Integer FAIL = 1; + /** + * 验证码前缀 + */ + String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY_"; } diff --git a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/util/WebUtils.java b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/util/WebUtils.java index 3845c078df2644e84b8186a9936e62176575db11..560b344c4e3f2458e131892c503eaf67b010033b 100755 --- a/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/util/WebUtils.java +++ b/pig-common/pig-common-core/src/main/java/com/pig4cloud/pig/common/core/util/WebUtils.java @@ -16,10 +16,15 @@ package com.pig4cloud.pig.common.core.util; +import cn.hutool.core.codec.Base64; import cn.hutool.json.JSONUtil; +import com.pig4cloud.pig.common.core.exception.CheckedException; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.RequestContextHolder; @@ -31,6 +36,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; /** @@ -40,8 +46,8 @@ import java.io.PrintWriter; */ @Slf4j public class WebUtils extends org.springframework.web.util.WebUtils { - - public static final String UNKNOWN = "unknown"; + private static final String BASIC_ = "Basic "; + private static final String UNKNOWN = "unknown"; /** * 判断是否ajax请求 @@ -188,5 +194,36 @@ public class WebUtils extends org.springframework.web.util.WebUtils { } return StringUtils.isBlank(ip) ? null : ip.split(",")[0]; } + + /** + * 从request 获取CLIENT_ID + * + * @return + */ + @SneakyThrows + public static String[] getClientId(ServerHttpRequest request) { + String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); + + if (header == null || !header.startsWith(BASIC_)) { + throw new CheckedException("请求头中client信息为空"); + } + byte[] base64Token = header.substring(6).getBytes("UTF-8"); + byte[] decoded; + try { + decoded = Base64.decode(base64Token); + } catch (IllegalArgumentException e) { + throw new CheckedException( + "Failed to decode basic authentication token"); + } + + String token = new String(decoded, StandardCharsets.UTF_8); + + int delim = token.indexOf(":"); + + if (delim == -1) { + throw new CheckedException("Invalid basic authentication token"); + } + return new String[]{token.substring(0, delim), token.substring(delim + 1)}; + } } diff --git a/pig-config/src/main/resources/config/pig-gateway-dev.yml b/pig-config/src/main/resources/config/pig-gateway-dev.yml index 85f7533bec6a98f48305f94723115da418bb14ba..e2ef1d04b75ce08e7d478130ccb09132e5f46484 100755 --- a/pig-config/src/main/resources/config/pig-gateway-dev.yml +++ b/pig-config/src/main/resources/config/pig-gateway-dev.yml @@ -11,7 +11,7 @@ spring: - Path=/auth/** filters: # 验证码处理 - - ImageCodeGatewayFilter + - ValidateCodeGatewayFilter # 前端密码解密 - PasswordDecoderFilter #UPMS 模块 @@ -42,3 +42,8 @@ security: encode: # 前端密码密钥,必须16位 key: 'thanks,pig4cloud' + +# 不校验验证码终端 +ignore: + clients: + - test diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ImageCodeGatewayFilter.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ImageCodeGatewayFilter.java deleted file mode 100755 index bf02c87dc285ca7fa7c25d15da7034407a04f969..0000000000000000000000000000000000000000 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ImageCodeGatewayFilter.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com). - *

- * Licensed under the GNU Lesser General Public License 3.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.gnu.org/licenses/lgpl.html - *

- * 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.codec.Base64; -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.StrUtil; -import com.pig4cloud.pig.common.core.exception.CheckedException; -import com.pig4cloud.pig.common.core.exception.ValidateCodeException; -import com.pig4cloud.pig.common.core.config.FilterIgnorePropertiesConfig; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -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.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -/** - * @author lengleng - * @date 2019/2/1 - * 验证码处理 - */ -@Slf4j -@Component -public class ImageCodeGatewayFilter extends AbstractGatewayFilterFactory { - public static final String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY"; - public static final String OAUTH_TOKEN_URL = "/oauth/token"; - private static final String BASIC_ = "Basic "; - @Autowired - private RedisTemplate redisTemplate; - @Autowired - private FilterIgnorePropertiesConfig filterIgnorePropertiesConfig; - - /** - * 从header 请求中的clientId/clientsecect - * - * @param header header中的参数 - * @throws CheckedException if the Basic header is not present or is not valid - * Base64 - */ - public static String[] extractAndDecodeHeader(String header) - throws IOException, CheckedException { - - byte[] base64Token = header.substring(6).getBytes("UTF-8"); - byte[] decoded; - try { - decoded = Base64.decode(base64Token); - } catch (IllegalArgumentException e) { - throw new CheckedException( - "Failed to decode basic authentication token"); - } - - String token = new String(decoded, CharsetUtil.UTF_8); - - int delim = token.indexOf(":"); - - if (delim == -1) { - throw new CheckedException("Invalid basic authentication token"); - } - return new String[]{token.substring(0, delim), token.substring(delim + 1)}; - } - - /** - * *从header 请求中的clientId/clientsecect - * - * @param request - * @return - * @throws IOException - */ - public static String[] extractAndDecodeHeader(ServerHttpRequest request) - throws IOException, CheckedException { - String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); - - if (header == null || !header.startsWith(BASIC_)) { - throw new CheckedException("请求头中client信息为空"); - } - - return extractAndDecodeHeader(header); - } - - @Override - public GatewayFilter apply(Object config) { - return (exchange, chain) -> { - ServerHttpRequest request = exchange.getRequest(); - - // 不是登录请求,直接向下执行 - if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), OAUTH_TOKEN_URL)) { - return chain.filter(exchange); - } - - // 终端设置不校验, 直接向下执行(1. 从请求参数中获取 2.从header取) - String clientId = request.getQueryParams().getFirst("client_id"); - if (StrUtil.isNotBlank(clientId)) { - if (filterIgnorePropertiesConfig.getClients().contains(clientId)) { - return chain.filter(exchange); - } - } - try { - String[] clientInfos = extractAndDecodeHeader(request); - if (filterIgnorePropertiesConfig.getClients().contains(clientInfos[0])) { - return chain.filter(exchange); - } - } catch (Exception e) { - ServerHttpResponse response = exchange.getResponse(); - response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED); - return response.setComplete(); - } - - //校验验证码合法性 - try { - checkCode(request); - } catch (ValidateCodeException e) { - ServerHttpResponse response = exchange.getResponse(); - response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED); - return response.setComplete(); - } - - return chain.filter(exchange); - }; - } - - /** - * 检查code - * - * @param request - * @throws ValidateCodeException 校验异常 - */ - private void checkCode(ServerHttpRequest request) throws ValidateCodeException { - String code = request.getQueryParams().getFirst("code"); - - if (StrUtil.isBlank(code)) { - throw new ValidateCodeException(); - } - - String randomStr = request.getQueryParams().getFirst("randomStr"); - if (StrUtil.isBlank(randomStr)) { - throw new ValidateCodeException(); - } - - String key = DEFAULT_CODE_KEY + randomStr; - if (!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-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ValidateCodeGatewayFilter.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ValidateCodeGatewayFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..ea336eacb95413f49f1201bc4c9c82a8f54fb47d --- /dev/null +++ b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/ValidateCodeGatewayFilter.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License 3.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.gnu.org/licenses/lgpl.html + *

+ * 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.StrUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pig4cloud.pig.common.core.config.FilterIgnorePropertiesConfig; +import com.pig4cloud.pig.common.core.constant.CommonConstants; +import com.pig4cloud.pig.common.core.constant.SecurityConstants; +import com.pig4cloud.pig.common.core.exception.ValidateCodeException; +import com.pig4cloud.pig.common.core.util.R; +import com.pig4cloud.pig.common.core.util.WebUtils; +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.http.HttpStatus; +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 lengleng + * @date 2018/7/4 + * 验证码处理 + */ +@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)) { + 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[] clientInfos = WebUtils.getClientId(request); + if (filterIgnorePropertiesConfig.getClients().contains(clientInfos[0])) { + return chain.filter(exchange); + } + + //校验验证码 + checkCode(request); + } catch (Exception e) { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED); + try { + return response.writeWith(Mono.just(response.bufferFactory() + .wrap(objectMapper.writeValueAsBytes( + R.builder().msg(e.getMessage()) + .code(CommonConstants.FAIL).build())))); + } catch (JsonProcessingException e1) { + log.error("对象输出异常", e1); + } + } + + return chain.filter(exchange); + }; + } + + /** + * 检查code + * + * @param request + */ + @SneakyThrows + private void checkCode(ServerHttpRequest request) { + String code = request.getQueryParams().getFirst("code"); + + if (StrUtil.isBlank(code)) { + throw new ValidateCodeException("验证码不能为空"); + } + + String randomStr = request.getQueryParams().getFirst("randomStr"); + if (StrUtil.isBlank(randomStr)) { + randomStr = request.getQueryParams().getFirst("mobile"); + } + + String key = CommonConstants.DEFAULT_CODE_KEY + randomStr; + if (!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-gateway/src/main/java/com/pig4cloud/pig/gateway/handler/ImageCodeHandler.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/handler/ImageCodeHandler.java index 0620b581a8bec1dee1dc0fe46a3aa8a344ac2236..f3b1cc46b4b18af6789c13f5abd8e5898a085d0f 100755 --- 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 @@ -17,7 +17,7 @@ package com.pig4cloud.pig.gateway.handler; import com.google.code.kaptcha.Producer; -import com.pig4cloud.pig.gateway.filter.ImageCodeGatewayFilter; +import com.pig4cloud.pig.common.core.constant.CommonConstants; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ByteArrayResource; @@ -57,7 +57,7 @@ public class ImageCodeHandler implements HandlerFunction { //保存验证码信息 String randomStr = serverRequest.queryParam("randomStr").get(); - redisTemplate.opsForValue().set(ImageCodeGatewayFilter.DEFAULT_CODE_KEY + randomStr, text, 60, TimeUnit.SECONDS); + redisTemplate.opsForValue().set(CommonConstants.DEFAULT_CODE_KEY + randomStr, text, 60, TimeUnit.SECONDS); // 转换流信息写出 FastByteArrayOutputStream os = new FastByteArrayOutputStream();