如何在Heroku上使用JAX-RS查找传入RESTful请求的IP?

30

我正在编写一个基于Heroku托管的Java RESTful服务,参考示例 -> https://api.heroku.com/myapps/template-java-jaxrs/clone

我的样例服务是:

package com.example.services;

import com.example.models.Time;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/time")
@Produces(MediaType.APPLICATION_JSON)
public class TimeService {

    @GET
    public Time get() {
        return new Time();
    }

}

我的主要是:

public class Main {

    public static final String BASE_URI = getBaseURI();

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception{
        final Map<String, String> initParams = new HashMap<String, String>();
        initParams.put("com.sun.jersey.config.property.packages","services.contracts"); 

        System.out.println("Starting grizzly...");
        SelectorThread threadSelector = GrizzlyWebContainerFactory.create(BASE_URI, initParams);
        System.out.println(String.format("Jersey started with WADL available at %sapplication.wadl.",BASE_URI, BASE_URI));
    }

    private static String getBaseURI() 
    {
        return "http://localhost:"+(System.getenv("PORT")!=null?System.getenv("PORT"):"9998")+"/";      
    }

}

我的问题是如何在我的服务中找出请求来自的IP地址和端口组合?我已经阅读了关于使用@Context注入javax.ws.rs.core.HttpHeaders、javax.ws.rs.core.Request等内容的信息。然而,没有传入的IP或端口信息。

我知道如果你实现com.sun.grizzly.tcp.Adapter,你可以做类似这样的事情:

public static void main(String[] args) {
    SelectorThread st = new SelectorThread();
    st.setPort(8282);
    st.setAdapter(new EmbeddedServer());
    try {
        st.initEndpoint();
        st.startEndpoint();
    } catch (Exception e) {
        System.out.println("Exception in SelectorThread: " + e);
    } finally {
        if (st.isRunning()) {
            st.stopEndpoint();
        }
    }
}

public void service(Request request, Response response)
        throws Exception {
    String requestURI = request.requestURI().toString();

    System.out.println("New incoming request with URI: " + requestURI);
    System.out.println("Request Method is: " + request.method());

    if (request.method().toString().equalsIgnoreCase("GET")) {
        response.setStatus(HttpURLConnection.HTTP_OK);
        byte[] bytes = "Here is my response text".getBytes();

        ByteChunk chunk = new ByteChunk();
        response.setContentLength(bytes.length);
        response.setContentType("text/plain");
        chunk.append(bytes, 0, bytes.length);
        OutputBuffer buffer = response.getOutputBuffer();
        buffer.doWrite(chunk, response);
        response.finish();
    }
}

public void afterService(Request request, Response response)
        throws Exception {
    request.recycle();
    response.recycle();
}

并获得访问权限

    request.remoteAddr()

但是我真的希望像我第一次实现时那样,以更加结构化的方式分离我的RESTful API。

非常感谢任何帮助!谢谢!

3个回答

59

您可以注入{{link1:HttpServletRequest}}:

@GET
@Produces(MediaType.TEXT_PLAIN)
public Response getIp(@Context HttpServletRequest req) {
    String remoteHost = req.getRemoteHost();
    String remoteAddr = req.getRemoteAddr();
    int remotePort = req.getRemotePort();
    String msg = remoteHost + " (" + remoteAddr + ":" + remotePort + ")";
    return Response.ok(msg).build();
}

谢谢提供信息,但是当我运行它时,得到的IP与发出请求的客户端的公共IP不同。我在Heroku上运行服务,担心可能是一些Heroku中间人的IP(执行路由/负载平衡的IP)。我该如何解决这个问题? - Luke
2
我进行了主机名查找,req.getRemoteHost() 确实是一个私有 IP ip-10-42-223-182.ec2.internal,看起来是一个 EC2 IP。 - Luke
11
我想到了,你需要从Http Header中提取X-Forwarded-For头。 - Luke
5
想提醒那些在使用JAX-RS进行maven配置时遇到与原帖问题相同的人们,你需要在pom.xml文件中包含javax-servlet用于servlet-api(http://mvnrepository.com/artifact/javax.servlet/servlet-api/2.5),然后导入`javax.servlet.http.HttpServletRequest`。 Context类已经包含在JAX-RS中:import javax.ws.rs.core.Context - Sonny
嗨@Sonny,感谢您的评论。您知道在JAX RS方式中获取远程IP的方法吗(不使用servlet-api)? - Nilesh

12

正如Luke所说,使用Heroku时,远程主机是AWS应用程序层,因此是EC2 IP地址。

"X-Forwarded-For"头信息就是答案:

String ip = "0.0.0.0";
try {
    ip = req.getHeader("X-Forwarded-For").split(",")[0];
} catch (Exception ignored){}

8

基于@user647772和@Ethan的融合,感谢他们 ;)

注入HttpServletRequest:

@GET
@Produces(MediaType.TEXT_PLAIN)
public Response getFromp(@Context HttpServletRequest req) {
    String from = _getUserFrom(req);
    return Response.ok(from).build();
}

private String _getUserFrom(HttpServletRequest req) {
    String xForwardedFor = req.getHeader("X-Forwarded-For");
    xForwardedFor = xForwardedFor != null && xForwardedFor.contains(",") ? xForwardedFor.split(",")[0]:xForwardedFor;
    String remoteHost = req.getRemoteHost();
    String remoteAddr = req.getRemoteAddr();
    int remotePort = req.getRemotePort();
    StringBuffer sb = new StringBuffer();
    if (remoteHost != null 
    && !"".equals(remoteHost)
    && !remoteHost.equals(remoteAddr)) {
        sb.append(remoteHost).append(" ");
    }
    if (xForwardedFor != null 
    && !"".equals(xForwardedFor)) {
        sb.append(xForwardedFor).append("(fwd)=>");
    }
    if (remoteAddr != null || !"".equals(remoteAddr)) {
        sb.append(remoteAddr).append(":").append(remotePort);
    }
    return sb.toString();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接