使用Zuul搭建网关
zuul的Filter配置
- 继承ZuulFilter类,重写方法
import javax.servlet.http.HttpServletRequest;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
public class AccessUserNameFilter extends ZuulFilter {
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(String.format("%s AccessUserNameFilter request to %s", request.getMethod(), request.getRequestURL().toString()));
String username = request.getParameter("username");// 获取请求的参数
if(null != username && username.equals("chhliu")) {// 如果请求的参数不为空,且值为chhliu时,则通过
ctx.setSendZuulResponse(true);// 对该请求进行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);// 设值,让下一个Filter看到上一个Filter的状态
return null;
}else{
ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由
ctx.setResponseStatusCode(401);// 返回错误码
ctx.setResponseBody("{\"result\":\"username is not correct!\"}");// 返回错误内容
ctx.set("isSuccess", false);
return null;
}
}
@Override
public boolean shouldFilter() {
return true;// 是否执行该过滤器,此处为true,说明需要过滤
}
@Override
public int filterOrder() {
return 0;// 优先级为0,数字越大,优先级越低
}
@Override
public String filterType() {
return "pre";// 前置过滤器
}
}
通过继承ZuulFilter然后覆写上面的4个方法,就可以实现一个简单的过滤器,下面就相关注意点进行说明:
- filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
- pre:可以在请求被路由之前调用
- route:在路由请求时候被调用
- post:在route和error过滤器之后被调用
- error:处理请求时发生错误时被调用
- Zuul的主要请求生命周期包括“pre”,“route”和“post”等阶段。对于每个请求,都会运行具有这些类型的所有过滤器。
- filterOrder:通过int值来定义过滤器的执行顺序
- shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效
- run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码
仿照如上例子可建立多个过滤器,根据filterType的不同在不同的生命周期被调用,具体可参考:spring cloud-zuul的Filter详解
token续租处理
处理方式与上方自定义过滤器类一致,filterType设置为“post”,原理是post在route和error过滤器之后被调用,说明已经合法通。此时再根据判断Token是否即将过期来决定是否进行Token续租。下方判断规则为小于 60*20=1200时进行续租
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.finshell.hyacinthcloud.base.enums.ErrorCodeEnum;
import com.finshell.hyacinthcloud.base.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* The class Renew filter.
*
* @author hyacinthcloud@finshell.com
*/
@Component
@Slf4j
public class RenewFilter extends ZuulFilter {
@Resource
private JwtTokenStore jwtTokenStore;
private static final int EXPIRES_IN = 60 * 20;
/**
* Filter type string.
*
* @return the string
*/
@Override
public String filterType() {
return "post";
}
/**
* Filter order int.
*
* @return the int
*/
@Override
public int filterOrder() {
return 10;
}
/**
* Should filter boolean.
*
* @return the boolean
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* Run object.
*
* @return the object
*/
@Override
public Object run() {
log.info("RenewFilter - token续租...");
RequestContext requestContext = RequestContext.getCurrentContext();
try {
doSomething(requestContext);
} catch (Exception e) {
log.error("RenewFilter - token续租. [FAIL] EXCEPTION={}", e.getMessage(), e);
throw new BusinessException(ErrorCodeEnum.UAC10011041);
}
return null;
}
private void doSomething(RequestContext requestContext) {
HttpServletRequest request = requestContext.getRequest();
String token = StringUtils.substringAfter(request.getHeader(HttpHeaders.AUTHORIZATION), "bearer ");
if (StringUtils.isEmpty(token)) {
return;
}
OAuth2AccessToken oAuth2AccessToken = jwtTokenStore.readAccessToken(token);
int expiresIn = oAuth2AccessToken.getExpiresIn();
if (expiresIn < EXPIRES_IN) {
HttpServletResponse servletResponse = requestContext.getResponse();
servletResponse.addHeader("Renew-Header", "true");
}
}
}
可供参考资料:
- Spring OAuth2 token自动续签方案(与上述并非同一个处理方案请注意)