微服务网关跨域权限校验
集中的去解决一下跨域
这段代码是用来配置跨源资源共享(CORS)过滤器的。它创建了一个 CorsConfiguration
实例,允许所有方法和头部,并支持凭证(如 Cookies)。setAllowedOriginPatterns
方法设置允许的源,这里用 "*"
表示允许所有来源。最后,使用 UrlBasedCorsConfigurationSource
将该配置应用于所有路径(/**
),并返回一个 CorsWebFilter
实例,以便在请求时进行 CORS 处理。
package com.yupi.yuojbackendgateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
import java.util.Arrays;
// 处理跨域
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.setAllowCredentials(true);
// todo 实际改为线上真实域名、本地域名
config.setAllowedOriginPatterns(Arrays.asList("*"));
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
在网关层面
自定义一个跨域的拦截器
去给请求的响应头加上一个运行跨域的注解
package com.yupi.yuojbackendgateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
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 org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
@Component
public class GlobalAuthFilter implements GlobalFilter, Ordered {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String path = serverHttpRequest.getURI().getPath();
// 判断路径中是否包含 inner,只允许内部调用
if (antPathMatcher.match("/**/inner/**", path)) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
DataBufferFactory dataBufferFactory = response.bufferFactory();
DataBuffer dataBuffer = dataBufferFactory.wrap("无权限".getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
// todo 统一权限校验,通过 JWT 获取登录用户信息
return chain.filter(exchange);
}
/**
* 优先级提到最高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
拓展
1.JWT校验
2.可以在网关实现接口限流降级
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
// 在类中添加JWT密钥
private static final String SECRET_KEY = "your_secret_key"; // 请替换为你的密钥
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String path = serverHttpRequest.getURI().getPath();
// 判断路径中是否包含 inner,只允许内部调用
if (antPathMatcher.match("/**/inner/**", path)) {
return respondWithForbidden(exchange, "无权限");
}
// JWT校验逻辑
String authorizationHeader = serverHttpRequest.getHeaders().getFirst("Authorization");
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
return respondWithForbidden(exchange, "缺少或格式不正确的Authorization头");
}
String token = authorizationHeader.substring(7); // 移除 "Bearer " 前缀
try {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
// 根据需要,您可以将用户信息提取并存储在请求的属性中
// exchange.getAttributes().put("user", claims);
} catch (SignatureException e) {
return respondWithForbidden(exchange, "无效的JWT");
} catch (Exception e) {
return respondWithForbidden(exchange, "JWT校验失败");
}
return chain.filter(exchange);
}
private Mono<Void> respondWithForbidden(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
DataBufferFactory dataBufferFactory = response.bufferFactory();
DataBuffer dataBuffer = dataBufferFactory.wrap(message.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
JWT密钥:
private static final String SECRET_KEY = "your_secret_key"; // 请替换为你的密钥
用于签名和验证JWT的密钥。请确保此密钥保密,并使用安全的方式存储。
检查Authorization头:
String authorizationHeader = serverHttpRequest.getHeaders().getFirst("Authorization");
获取请求头中的Authorization
。
Bearer Token处理:
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
return respondWithForbidden(exchange, "缺少或格式不正确的Authorization头");
}
检查头是否存在且格式正确。
JWT解析和验证:
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
响应处理:
private Mono<Void> respondWithForbidden(ServerWebExchange exchange, String message) {
...
}
抽取出的响应处理方法,方便在需要时发送403响应。
请确保JWT的签名和解析使用相同的密钥。
根据实际需要处理用户信息和权限。
思考:
企业级开发真的有必要用微服务吗
真的有必要用 Spring Cloud 实现微服务吗
大多数情况下 一般使用API RPC HTTP 实现跨部门 服务的实现
各部门直接共享一套接口的定义
数据格式 和 调用代码全部自动生成 保持统一 同时解耦