mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-12 05:39:00 +08:00
🔖 v1.2.3
This commit is contained in:
commit
f185add546
198 files changed
+11968
No files matched your search
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway;
|
||||
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.cloud.client.SpringCloudApplication;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018年06月21日
|
||||
* 网关应用
|
||||
*/
|
||||
@SpringCloudApplication
|
||||
public class PigxGatewayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PigxGatewayApplication.class, args);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/7/22
|
||||
* 网关不简要终端配置
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@RefreshScope
|
||||
@ConditionalOnExpression("!'${ignore}'.isEmpty()")
|
||||
@ConfigurationProperties(prefix = "ignore")
|
||||
public class FilterIgnorePropertiesConfig {
|
||||
private List<String> clients = new ArrayList<>();
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.config;
|
||||
|
||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||
import com.google.code.kaptcha.util.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2017-12-21 21:12:18
|
||||
*/
|
||||
@Configuration
|
||||
public class KaptchaConfiguration {
|
||||
|
||||
private static final String KAPTCHA_BORDER = "kaptcha.border";
|
||||
private static final String KAPTCHA_TEXTPRODUCER_FONT_COLOR = "kaptcha.textproducer.font.color";
|
||||
private static final String KAPTCHA_TEXTPRODUCER_CHAR_SPACE = "kaptcha.textproducer.char.space";
|
||||
private static final String KAPTCHA_IMAGE_WIDTH = "kaptcha.image.width";
|
||||
private static final String KAPTCHA_IMAGE_HEIGHT = "kaptcha.image.height";
|
||||
private static final String KAPTCHA_TEXTPRODUCER_CHAR_LENGTH = "kaptcha.textproducer.char.length";
|
||||
private static final Object KAPTCHA_IMAGE_FONT_SIZE = "kaptcha.textproducer.font.size";
|
||||
|
||||
/**
|
||||
* 默认生成图形验证码宽度
|
||||
*/
|
||||
private static final String DEFAULT_IMAGE_WIDTH = "100";
|
||||
|
||||
/**
|
||||
* 默认生成图像验证码高度
|
||||
*/
|
||||
private static final String DEFAULT_IMAGE_HEIGHT = "40";
|
||||
|
||||
/**
|
||||
* 默认生成图形验证码长度
|
||||
*/
|
||||
private static final String DEFAULT_IMAGE_LENGTH = "4";
|
||||
/**
|
||||
* 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue.
|
||||
*/
|
||||
private static final String DEFAULT_COLOR_FONT = "black";
|
||||
|
||||
/**
|
||||
* 图片边框
|
||||
*/
|
||||
private static final String DEFAULT_IMAGE_BORDER = "no";
|
||||
/**
|
||||
* 默认图片间隔
|
||||
*/
|
||||
private static final String DEFAULT_CHAR_SPACE = "5";
|
||||
/**
|
||||
* 验证码文字大小
|
||||
*/
|
||||
private static final String DEFAULT_IMAGE_FONT_SIZE = "30";
|
||||
|
||||
@Bean
|
||||
public DefaultKaptcha producer() {
|
||||
Properties properties = new Properties();
|
||||
properties.put(KAPTCHA_BORDER, DEFAULT_IMAGE_BORDER);
|
||||
properties.put(KAPTCHA_TEXTPRODUCER_FONT_COLOR, DEFAULT_COLOR_FONT);
|
||||
properties.put(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, DEFAULT_CHAR_SPACE);
|
||||
properties.put(KAPTCHA_IMAGE_WIDTH, DEFAULT_IMAGE_WIDTH);
|
||||
properties.put(KAPTCHA_IMAGE_HEIGHT, DEFAULT_IMAGE_HEIGHT);
|
||||
properties.put(KAPTCHA_IMAGE_FONT_SIZE, DEFAULT_IMAGE_FONT_SIZE);
|
||||
properties.put(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, DEFAULT_IMAGE_LENGTH);
|
||||
Config config = new Config(properties);
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.config;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/7/1
|
||||
* 路由限流配置
|
||||
*/
|
||||
@Configuration
|
||||
public class RateLimiterConfiguration {
|
||||
@Bean(value = "remoteAddrKeyResolver")
|
||||
public KeyResolver remoteAddrKeyResolver() {
|
||||
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.config;
|
||||
|
||||
import com.pig4cloud.pigx.gateway.handle.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/7/5
|
||||
* 路由配置信息
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
public class RouterFunctionConfiguration {
|
||||
private final HystrixFallbackHandler hystrixFallbackHandler;
|
||||
private final SwaggerResourceHandler swaggerResourceHandler;
|
||||
private final SwaggerSecurityHandler swaggerSecurityHandler;
|
||||
private final SwaggerUiHandler swaggerUiHandler;
|
||||
private final ImageCodeHandler imageCodeHandler;
|
||||
|
||||
@Bean
|
||||
public RouterFunction routerFunction() {
|
||||
return RouterFunctions.route(
|
||||
RequestPredicates.GET("/fallback")
|
||||
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), hystrixFallbackHandler)
|
||||
.andRoute(RequestPredicates.GET("/code")
|
||||
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), imageCodeHandler)
|
||||
.andRoute(RequestPredicates.GET("/swagger-resources")
|
||||
.and(RequestPredicates.accept(MediaType.ALL)), swaggerResourceHandler)
|
||||
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/ui")
|
||||
.and(RequestPredicates.accept(MediaType.ALL)), swaggerUiHandler)
|
||||
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/security")
|
||||
.and(RequestPredicates.accept(MediaType.ALL)), swaggerSecurityHandler);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
import springfox.documentation.swagger.web.SwaggerResource;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Sywd
|
||||
* 聚合接口文档注册,和zuul实现类似
|
||||
*/
|
||||
@Component
|
||||
@Primary
|
||||
@AllArgsConstructor
|
||||
public class SwaggerProvider implements SwaggerResourcesProvider {
|
||||
public static final String API_URI = "/v2/api-docs";
|
||||
private final RouteLocator routeLocator;
|
||||
private final GatewayProperties gatewayProperties;
|
||||
|
||||
|
||||
@Override
|
||||
public List<SwaggerResource> get() {
|
||||
List<SwaggerResource> resources = new ArrayList<>();
|
||||
List<String> routes = new ArrayList<>();
|
||||
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
|
||||
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
|
||||
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
|
||||
.filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
|
||||
.filter(predicateDefinition -> !"pigx-auth".equalsIgnoreCase(routeDefinition.getId()))
|
||||
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
|
||||
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
|
||||
.replace("/**", API_URI)))));
|
||||
return resources;
|
||||
}
|
||||
|
||||
private SwaggerResource swaggerResource(String name, String location) {
|
||||
SwaggerResource swaggerResource = new SwaggerResource();
|
||||
swaggerResource.setName(name);
|
||||
swaggerResource.setLocation(location);
|
||||
swaggerResource.setSwaggerVersion("2.0");
|
||||
return swaggerResource;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.exception;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018年07月22日
|
||||
* 异常包装类
|
||||
*/
|
||||
public class CheckedException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CheckedException() {
|
||||
}
|
||||
|
||||
public CheckedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CheckedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CheckedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.exception;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
*/
|
||||
public class ValidateCodeException extends Exception {
|
||||
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.filter;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pigx.gateway.config.FilterIgnorePropertiesConfig;
|
||||
import com.pig4cloud.pigx.gateway.exception.CheckedException;
|
||||
import com.pig4cloud.pigx.gateway.exception.ValidateCodeException;
|
||||
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.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 2018/7/4
|
||||
* 验证码处理
|
||||
*/
|
||||
@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";
|
||||
@Autowired
|
||||
private RedisTemplate redisTemplate;
|
||||
@Autowired
|
||||
private FilterIgnorePropertiesConfig filterIgnorePropertiesConfig;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
private static final String BASIC_ = "Basic ";
|
||||
|
||||
/**
|
||||
* 从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("Authorization");
|
||||
|
||||
if (header == null || !header.startsWith(BASIC_)) {
|
||||
throw new CheckedException("请求头中client信息为空");
|
||||
}
|
||||
|
||||
return extractAndDecodeHeader(header);
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.filter;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/7/15
|
||||
* 密码解密工具类
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {
|
||||
private static final String PASSWORD = "password";
|
||||
private static final String KEY_ALGORITHM = "AES";
|
||||
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/NOPadding";
|
||||
@Value("${security.encode.key:1234567812345678}")
|
||||
private String encodeKey;
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
// 不是登录请求,直接向下执行
|
||||
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), ImageCodeGatewayFilter.OAUTH_TOKEN_URL)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
URI uri = exchange.getRequest().getURI();
|
||||
String queryParam = uri.getRawQuery();
|
||||
Map<String, String> paramMap = HttpUtil.decodeParamMap(queryParam, CharsetUtil.UTF_8);
|
||||
|
||||
String password = paramMap.get(PASSWORD);
|
||||
if (StrUtil.isNotBlank(password)) {
|
||||
try {
|
||||
password = decryptAES(password, encodeKey);
|
||||
} catch (Exception e) {
|
||||
log.error("密码解密失败:{}", password);
|
||||
return Mono.error(e);
|
||||
}
|
||||
paramMap.put(PASSWORD, password.trim());
|
||||
}
|
||||
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri)
|
||||
.replaceQuery(HttpUtil.toParams(paramMap))
|
||||
.build(true)
|
||||
.toUri();
|
||||
|
||||
ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
|
||||
return chain.filter(exchange.mutate().request(newRequest).build());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static String decryptAES(String data, String pass) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(pass.getBytes(), KEY_ALGORITHM);
|
||||
IvParameterSpec ivspec = new IvParameterSpec(pass.getBytes());
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
|
||||
byte[] result = cipher.doFinal(Base64.decode(data.getBytes(CharsetUtil.UTF_8)));
|
||||
return new String(result, CharsetUtil.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.filter;
|
||||
|
||||
import com.pig4cloud.pigx.gateway.config.SwaggerProvider;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author Sywd
|
||||
* 添加X-Forwarded-Prefix header
|
||||
*/
|
||||
@Component
|
||||
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
|
||||
private static final String HEADER_NAME = "X-Forwarded-Prefix";
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
String path = request.getURI().getPath();
|
||||
if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI));
|
||||
|
||||
|
||||
ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
|
||||
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
|
||||
return chain.filter(newExchange);
|
||||
};
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.handle;
|
||||
|
||||
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 lengleng
|
||||
* @date 2018/7/5
|
||||
* Hystrix 降级处理
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class HystrixFallbackHandler implements HandlerFunction<ServerResponse> {
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
|
||||
log.error("网关执行请求:{}失败,hystrix服务降级处理", serverRequest.uri());
|
||||
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
|
||||
.contentType(MediaType.TEXT_PLAIN).body(BodyInserters.fromObject("服务异常"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.handle;
|
||||
|
||||
import com.google.code.kaptcha.Producer;
|
||||
import com.pig4cloud.pigx.gateway.filter.ImageCodeGatewayFilter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
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 javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/7/5
|
||||
* 验证码生成逻辑处理类
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class ImageCodeHandler implements HandlerFunction<ServerResponse> {
|
||||
private final Producer producer;
|
||||
private final RedisTemplate redisTemplate;
|
||||
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
|
||||
//生成验证码
|
||||
String text = producer.createText();
|
||||
BufferedImage image = producer.createImage(text);
|
||||
|
||||
//保存验证码信息
|
||||
String randomStr = serverRequest.queryParam("randomStr").get();
|
||||
redisTemplate.opsForValue().set(ImageCodeGatewayFilter.DEFAULT_CODE_KEY + randomStr, text, 60, TimeUnit.SECONDS);
|
||||
|
||||
// 转换流信息写出
|
||||
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(image, "jpeg", os);
|
||||
} catch (IOException e) {
|
||||
log.error("ImageIO write err", e);
|
||||
return Mono.error(e);
|
||||
}
|
||||
|
||||
return ServerResponse
|
||||
.status(HttpStatus.OK)
|
||||
.contentType(MediaType.IMAGE_JPEG)
|
||||
.body(BodyInserters.fromResource(new ByteArrayResource(os.toByteArray())));
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.handle;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
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;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018-07-19
|
||||
* SwaggerResourceHandler
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class SwaggerResourceHandler implements HandlerFunction<ServerResponse> {
|
||||
private final SwaggerResourcesProvider swaggerResources;
|
||||
|
||||
/**
|
||||
* Handle the given request.
|
||||
*
|
||||
* @param request the request to handle
|
||||
* @return the response
|
||||
*/
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
return ServerResponse.status(HttpStatus.OK)
|
||||
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(swaggerResources.get()));
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.handle;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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;
|
||||
import springfox.documentation.swagger.web.SecurityConfiguration;
|
||||
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018-07-19
|
||||
* SwaggerSecurityHandler
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SwaggerSecurityHandler implements HandlerFunction<ServerResponse> {
|
||||
@Autowired(required = false)
|
||||
private SecurityConfiguration securityConfiguration;
|
||||
|
||||
/**
|
||||
* Handle the given request.
|
||||
*
|
||||
* @param request the request to handle
|
||||
* @return the response
|
||||
*/
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
return ServerResponse.status(HttpStatus.OK)
|
||||
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(
|
||||
Optional.ofNullable(securityConfiguration)
|
||||
.orElse(SecurityConfigurationBuilder.builder().build())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.gateway.handle;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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;
|
||||
import springfox.documentation.swagger.web.UiConfiguration;
|
||||
import springfox.documentation.swagger.web.UiConfigurationBuilder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018-07-19
|
||||
* SwaggerUiHandler
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SwaggerUiHandler implements HandlerFunction<ServerResponse> {
|
||||
@Autowired(required = false)
|
||||
private UiConfiguration uiConfiguration;
|
||||
|
||||
/**
|
||||
* Handle the given request.
|
||||
*
|
||||
* @param request the request to handle
|
||||
* @return the response
|
||||
*/
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
return ServerResponse.status(HttpStatus.OK)
|
||||
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(
|
||||
Optional.ofNullable(uiConfiguration)
|
||||
.orElse(UiConfigurationBuilder.builder().build())));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user