Appearance
首先是内部处理时也许携带请求头,尤其是Token,毕竟其他服务可不知道你来自外部还是内部,除了Token外还可传递一些其他信息:
java
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
HttpServletRequest request = SecurityUtils.getRequest();
if (ObjUtil.isNotEmpty(request)) {
// 传递用户信息请求头,防止丢失
String userId = request.getHeader(TokenConstants.DETAILS_USER_ID);
if (StrUtil.isNotEmpty(userId)) {
requestTemplate.header(TokenConstants.DETAILS_USER_ID, userId);
}
String userName = request.getHeader(TokenConstants.DETAILS_USERNAME);
if (StrUtil.isNotEmpty(userName)) {
requestTemplate.header(TokenConstants.DETAILS_USERNAME, userName);
}
String authentication = request.getHeader(TokenConstants.AUTHENTICATION);
if (StrUtil.isNotEmpty(authentication)) {
requestTemplate.header(TokenConstants.AUTHENTICATION, authentication);
} else {
// 如果没有token,设置请求来源为内部请求,即匿名访问
requestTemplate.header(TokenConstants.FROM_SOURCE, TokenConstants.INNER);
}
// 配置客户端IP
requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr(request));
}
}
}
创建配置类使其生效:
java
@Configuration
public class FeignAutoConfiguration {
@Bean
public RequestInterceptor requestInterceptor() {
return new FeignRequestInterceptor();
}
}
一些全局变量:
java
public class TokenConstants {
/** 令牌自定义标识 */
public static final String AUTHENTICATION = "token";
/** 请求来源 */
public static final String FROM_SOURCE = "from-source";
/** 内部请求 */
public static final String INNER = "inner";
}
解决:内部调用不可匿名访问
首先,内部调用且未携带Token时都需要携带请求头,如上述代码高亮部分。
然后,Token过滤器需要新增逻辑,如果是内部请求并且补携带Token则直接放行。
java
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 0.如果请求是内部请求并且,则直接放行
String source = request.getHeader(TokenConstants.FROM_SOURCE);
if (StrUtil.isNotBlank(source) && source.equals(TokenConstants.INNER)) {
// 设置一个空用户,绕过SpringSecurity权限
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(new LoginUser(new SysUser("root"), new ArrayList<>()), null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// 放行
filterChain.doFilter(request, response);
return;
}
// 此处省略,和之前相同
}
}
gateway设置拦截器
设置来源为内部,那么如果黑客将请求都设置为内部请求,岂不是带来安全隐患?因此在网管层设置过滤器,所有请求的请求来源均从头部删除。
java
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).doFirst(() -> {
exchange.getResponse().beforeCommit(() -> Mono.fromRunnable(() -> {
HttpHeaders headers = exchange.getResponse().getHeaders();
headers.remove(TokenConstants.FROM_SOURCE);
}));
});
}
@Override
public int getOrder() {
return -200;
}
}