วิธีแยกที่อยู่ IP ใน Spring MVC Controller รับสาย


93

ฉันกำลังทำงานในโครงการ Spring MVC controller ซึ่งฉันกำลังทำการเรียก GET URL จากเบราว์เซอร์ -

ด้านล่างนี้คือ url ที่ฉันโทรออกจากเบราว์เซอร์ -

http://127.0.0.1:8080/testweb/processing?workflow=test&conf=20140324&dc=all

และด้านล่างนี้คือรหัสที่โทรมาหลังจากกดปุ่มที่เบราว์เซอร์ -

@RequestMapping(value = "processing", method = RequestMethod.GET)
public @ResponseBody ProcessResponse processData(@RequestParam("workflow") final String workflow,
    @RequestParam("conf") final String value, @RequestParam("dc") final String dc) {

        System.out.println(workflow);
        System.out.println(value);
        System.out.println(dc);

        // some other code
    }

คำชี้แจงปัญหา:-

มีวิธีใดบ้างที่ฉันสามารถแยกที่อยู่ IP จากส่วนหัวบางส่วนได้หรือไม่? ความหมายฉันต้องการทราบว่าที่อยู่ IP ใดมีการโทรมาหมายถึงใครก็ตามที่โทรมาเหนือ URL ฉันจำเป็นต้องรู้ที่อยู่ IP ของพวกเขา เป็นไปได้ไหมที่จะทำ?


ลองใช้ Spring AOP และจัดการ HttpServletRequest stackoverflow.com/questions/19271807/…
Dilip G

คำตอบ:


149

วิธีแก้คือ

@RequestMapping(value = "processing", method = RequestMethod.GET)
public @ResponseBody ProcessResponse processData(@RequestParam("workflow") final String workflow,
    @RequestParam("conf") final String value, @RequestParam("dc") final String dc, HttpServletRequest request) {

        System.out.println(workflow);
        System.out.println(value);
        System.out.println(dc);
        System.out.println(request.getRemoteAddr());
        // some other code
    }

เพิ่มHttpServletRequest requestในนิยามวิธีการของคุณจากนั้นใช้ Servlet API

Spring Documentation ที่นี่กล่าวใน

15.3.2.3 อาร์กิวเมนต์เมธอดตัวจัดการที่รองรับและประเภทการส่งคืน

Handler methods that are annotated with @RequestMapping can have very flexible signatures.
Most of them can be used in arbitrary order (see below for more details).

Request or response objects (Servlet API). Choose any specific request or response type,
for example ServletRequest or HttpServletRequest

1
ขอบคุณ Koitoer สำหรับความช่วยเหลือ คำถามสั้น ๆ อย่างหนึ่งสมมติว่าการโทรมาจาก Load Balancer แทนที่จะเป็นเครื่องเฉพาะแล้วสิ่งนี้จะใช้ได้หรือไม่? ฉันเดาว่าไม่ ..
ห์น

ไม่เคยชิน แต่มีการกำหนดค่าบางอย่างในตัว
โหลดบาลานซ์

ตรวจสอบว่าตัวโหลดบาลานซ์อาจส่งค่าเหล่านั้นในส่วนหัวได้ดังนั้นให้พิจารณาใช้เมธอด getHeader ของ HttpServletRequest
Koitoer

Koitoer ทำงานได้อย่างสมบูรณ์ !! แต่มีวิธีอื่นที่ไม่ใช่ HttpServletRequest
Harshal Patil

1
เลิกใช้แล้ว
Gewure

112

ฉันมาสาย แต่นี่อาจช่วยใครบางคนที่กำลังมองหาคำตอบ โดยทั่วไปใช้servletRequest.getRemoteAddr()งานได้

ในหลายกรณีผู้ใช้แอปพลิเคชันของคุณอาจเข้าถึงเว็บเซิร์ฟเวอร์ของคุณผ่านพร็อกซีเซิร์ฟเวอร์หรือแอปพลิเคชันของคุณอาจอยู่เบื้องหลังตัวโหลดบาลานเซอร์

ดังนั้นคุณควรเข้าถึงส่วนหัวX-Forwarded-For http ในกรณีเช่นนี้เพื่อรับที่อยู่ IP ของผู้ใช้

เช่น String ipAddress = request.getHeader("X-FORWARDED-FOR");

หวังว่านี่จะช่วยได้


11
โปรดทราบว่าX-Forwarded-Forโดยทั่วไปรายการ ips ที่คั่นด้วยจุลภาคโดยแต่ละพร็อกซีในเครือจะเพิ่มที่อยู่ระยะไกลที่เห็นในรายการ ดังนั้นการใช้งานที่ดีโดยทั่วไปจะมีรายชื่อพร็อกซีที่เชื่อถือได้และ "ข้าม" ips เหล่านั้นเมื่ออ่านส่วนหัวนี้จากขวาไปซ้าย
รามัน

วิธีรับที่อยู่ IP เป็น 111.111.111.111/X
Muneeb Mirza

26

ฉันใช้วิธีดังกล่าวเพื่อทำสิ่งนี้

public class HttpReqRespUtils {

    private static final String[] IP_HEADER_CANDIDATES = {
        "X-Forwarded-For",
        "Proxy-Client-IP",
        "WL-Proxy-Client-IP",
        "HTTP_X_FORWARDED_FOR",
        "HTTP_X_FORWARDED",
        "HTTP_X_CLUSTER_CLIENT_IP",
        "HTTP_CLIENT_IP",
        "HTTP_FORWARDED_FOR",
        "HTTP_FORWARDED",
        "HTTP_VIA",
        "REMOTE_ADDR"
    };

    public static String getClientIpAddressIfServletRequestExist() {

        if (RequestContextHolder.getRequestAttributes() == null) {
            return "0.0.0.0";
        }

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        for (String header: IP_HEADER_CANDIDATES) {
            String ipList = request.getHeader(header);
            if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
                String ip = ipList.split(",")[0];
                return ip;
            }
        }

        return request.getRemoteAddr();
    }
}

1
ฉันจะใช้มันที่ไหน?
Maifee Ul Asad

12

คุณสามารถรับที่อยู่ IP แบบคงที่ได้จากRequestContextHolderด้านล่าง:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
        .getRequest();

String ip = request.getRemoteAddr();

4

ใส่วิธีนี้ใน BaseController ของคุณ:

@SuppressWarnings("ConstantConditions")
protected String fetchClientIpAddr() {
    HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest();
    String ip = Optional.ofNullable(request.getHeader("X-FORWARDED-FOR")).orElse(request.getRemoteAddr());
    if (ip.equals("0:0:0:0:0:0:0:1")) ip = "127.0.0.1";
    Assert.isTrue(ip.chars().filter($ -> $ == '.').count() == 3, "Illegal IP: " + ip);
    return ip;
}

4

ดูด้านล่าง รหัสนี้ใช้ได้กับ spring-boot และ spring-boot + apache CXF / SOAP

    // in your class RequestUtil
    private static final String[] IP_HEADER_NAMES = { 
                                                        "X-Forwarded-For",
                                                        "Proxy-Client-IP",
                                                        "WL-Proxy-Client-IP",
                                                        "HTTP_X_FORWARDED_FOR",
                                                        "HTTP_X_FORWARDED",
                                                        "HTTP_X_CLUSTER_CLIENT_IP",
                                                        "HTTP_CLIENT_IP",
                                                        "HTTP_FORWARDED_FOR",
                                                        "HTTP_FORWARDED",
                                                        "HTTP_VIA",
                                                        "REMOTE_ADDR"
                                                    };

    public static String getRemoteIP(RequestAttributes requestAttributes)
    {
        if (requestAttributes == null)
        {
            return "0.0.0.0";
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        String ip = Arrays.asList(IP_HEADER_NAMES)
            .stream()
            .map(request::getHeader)
            .filter(h -> h != null && h.length() != 0 && !"unknown".equalsIgnoreCase(h))
            .map(h -> h.split(",")[0])
            .reduce("", (h1, h2) -> h1 + ":" + h2);
        return ip + request.getRemoteAddr();
    }

    //... in service class:
    String remoteAddress = RequestUtil.getRemoteIP(RequestContextHolder.currentRequestAttributes());

:)


3

ด้านล่างนี้คือ Spring way พร้อมด้วยautowiredrequest bean ใน@Controllerคลาส:

@Autowired 
private HttpServletRequest request;

System.out.println(request.getRemoteHost());

1
ซึ่งอาจทำให้เกิดปัญหากับการใช้คอนโทรลเลอร์พร้อมกันในหลายเธรด เพียง singletons ควรได้รับการฉีดเข้าไปในกรณีใช้@Controller @Autowiredมิฉะนั้นคุณต้องใช้@Scope(BeanDefinition.SCOPE_PROTOTYPE)บนคลาสคอนโทรลเลอร์เพื่อให้แน่ใจว่าอินสแตนซ์ใหม่ของคอนโทรลเลอร์ถูกสร้างขึ้นกับทุกคำขอ สิ่งนี้มีประสิทธิภาพน้อยกว่า แต่เป็นวิธีแก้ปัญหาหากคุณต้องฉีดบางอย่างเป็นคุณสมบัติให้กับคลาสคอนโทรลเลอร์
Andy

1

ในกรณีของฉันฉันใช้ Nginx หน้าแอปพลิเคชันของฉันด้วยการกำหนดค่าต่อไปนี้:

location / {
     proxy_pass        http://localhost:8080/;
     proxy_set_header  X-Real-IP $remote_addr;
     proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header  Host $http_host;
     add_header Content-Security-Policy 'upgrade-insecure-requests';
}

ดังนั้นในแอปพลิเคชันของฉันฉันจึงได้รับ ip ผู้ใช้จริงดังนี้:

String clientIP = request.getHeader("X-Real-IP");

0
private static final String[] IP_HEADER_CANDIDATES = {
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR"
    };

    public static String getIPFromRequest(HttpServletRequest request) {
        String ip = null;
        if (request == null) {
            if (RequestContextHolder.getRequestAttributes() == null) {
                return null;
            }
            request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }

        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!StringUtils.isEmpty(ip))
            return ip;

        for (String header : IP_HEADER_CANDIDATES) {
            String ipList = request.getHeader(header);
            if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
                return ipList.split(",")[0];
            }
        }

        return request.getRemoteAddr();
    }

ฉันรวมรหัสด้านบนกับรหัสนี้ใช้ได้กับกรณีส่วนใหญ่ ส่งผ่านสิ่งที่HttpServletRequest requestคุณได้รับจาก api ไปยังวิธีการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.