使用 Zuul 作为认证网关
Using Zuul as an authentication gateway
背景
我想实现本 article 中提出的设计。
可以用下图来概括:
- 客户端首先使用 IDP (OpenID Connect/OAuth2) 进行身份验证
- IDP returns 一个访问令牌(没有用户信息的不透明令牌)
- 客户端通过 API 网关进行调用,使用授权中的访问令牌 header
- API 网关使用访问令牌向 IDP 发出请求
- IDP 验证访问令牌是否有效以及 returns 用户信息 JSON 格式
- API 网关将用户信息存储在 JWT 中,并使用私钥对其进行签名。然后将 JWT 传递给下游服务,后者使用 public 密钥
验证 JWT
- 如果一个服务必须调用另一个服务来完成请求,它会传递 JWT 作为请求的身份验证和授权
到目前为止我有什么
我使用以下方法完成了大部分工作:
- Spring 云作为全球框架
- Spring 开机启动个人服务
- Netflix Zuul 作为 API 网关
我还编写了一个 Zuul PRE 过滤器,用于检查访问令牌、联系 IDP 并创建 JWT。然后将 JWT 添加到 header 用于转发到下游服务的请求。
问题
现在我的问题是针对 Zuul 及其过滤器的。如果身份验证因任何原因在 API 网关中失败,我如何才能停止路由并直接响应 401 而无需继续过滤器链和转发呼叫?
目前,如果身份验证失败,过滤器不会将 JWT 添加到 header,401 将来自下游服务。我希望我的网关可以阻止这种不必要的调用。
我试图看看如何使用 com.netflix.zuul.context.RequestContext
来做到这一点,但文档很差,我找不到方法。
您可以尝试在当前上下文中设置 setSendZuulResponse(false)
。这不应该路由请求。您也可以从上下文中调用 removeRouteHost()
,这会达到同样的效果。您可以使用setResponseStatusCode
设置 401 状态代码。
我知道我来晚了。
您可以使用 zuul 的预过滤器。您必须遵循的步骤如下。
//1. create filter with type pre
//2. Set the order of filter to greater than 5 because we need to run our filter after preDecoration filter of zuul.
@Component
public class CustomPreZuulFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Object run() {
final RequestContext requestContext = RequestContext.getCurrentContext();
logger.info("in zuul filter " + requestContext.getRequest().getRequestURI());
byte[] encoded;
try {
encoded = Base64.encode("fooClientIdPassword:secret".getBytes("UTF-8"));
requestContext.addZuulRequestHeader("Authorization", "Basic " + new String(encoded));
final HttpServletRequest req = requestContext.getRequest();
if (requestContext.getRequest().getHeader("Authorization") == null
&& !req.getContextPath().contains("login")) {
requestContext.unset();
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
} else {
//next logic
}
}
} catch (final UnsupportedEncodingException e) {
logger.error("Error occured in pre filter", e);
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 6;
}
@Override
public String filterType() {
return "pre";
}
}
requestContext.unset() 将重置当前线程活动请求的 RequestContext,您可以提供响应状态代码。
在您的 运行 方法中添加以下内容,它将解决此问题
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
背景
我想实现本 article 中提出的设计。
可以用下图来概括:
- 客户端首先使用 IDP (OpenID Connect/OAuth2) 进行身份验证
- IDP returns 一个访问令牌(没有用户信息的不透明令牌)
- 客户端通过 API 网关进行调用,使用授权中的访问令牌 header
- API 网关使用访问令牌向 IDP 发出请求
- IDP 验证访问令牌是否有效以及 returns 用户信息 JSON 格式
- API 网关将用户信息存储在 JWT 中,并使用私钥对其进行签名。然后将 JWT 传递给下游服务,后者使用 public 密钥 验证 JWT
- 如果一个服务必须调用另一个服务来完成请求,它会传递 JWT 作为请求的身份验证和授权
到目前为止我有什么
我使用以下方法完成了大部分工作:
- Spring 云作为全球框架
- Spring 开机启动个人服务
- Netflix Zuul 作为 API 网关
我还编写了一个 Zuul PRE 过滤器,用于检查访问令牌、联系 IDP 并创建 JWT。然后将 JWT 添加到 header 用于转发到下游服务的请求。
问题
现在我的问题是针对 Zuul 及其过滤器的。如果身份验证因任何原因在 API 网关中失败,我如何才能停止路由并直接响应 401 而无需继续过滤器链和转发呼叫?
目前,如果身份验证失败,过滤器不会将 JWT 添加到 header,401 将来自下游服务。我希望我的网关可以阻止这种不必要的调用。
我试图看看如何使用 com.netflix.zuul.context.RequestContext
来做到这一点,但文档很差,我找不到方法。
您可以尝试在当前上下文中设置 setSendZuulResponse(false)
。这不应该路由请求。您也可以从上下文中调用 removeRouteHost()
,这会达到同样的效果。您可以使用setResponseStatusCode
设置 401 状态代码。
我知道我来晚了。 您可以使用 zuul 的预过滤器。您必须遵循的步骤如下。
//1. create filter with type pre
//2. Set the order of filter to greater than 5 because we need to run our filter after preDecoration filter of zuul.
@Component
public class CustomPreZuulFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Object run() {
final RequestContext requestContext = RequestContext.getCurrentContext();
logger.info("in zuul filter " + requestContext.getRequest().getRequestURI());
byte[] encoded;
try {
encoded = Base64.encode("fooClientIdPassword:secret".getBytes("UTF-8"));
requestContext.addZuulRequestHeader("Authorization", "Basic " + new String(encoded));
final HttpServletRequest req = requestContext.getRequest();
if (requestContext.getRequest().getHeader("Authorization") == null
&& !req.getContextPath().contains("login")) {
requestContext.unset();
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
} else {
//next logic
}
}
} catch (final UnsupportedEncodingException e) {
logger.error("Error occured in pre filter", e);
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return 6;
}
@Override
public String filterType() {
return "pre";
}
}
requestContext.unset() 将重置当前线程活动请求的 RequestContext,您可以提供响应状态代码。
在您的 运行 方法中添加以下内容,它将解决此问题
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);