Grails 2.5:如何将IP地址传播到服务层?

时间:2015-11-30 18:12:24

标签: grails

控制器层可以使用request.getRemoteAddr()和/或request.getHeader("Client-IP")等获取IP。

但是,在服务层的内容中,我们可能希望记录用户检测到的或可疑的欺诈活动以及用户的IP地址。但是,IP不可用于服务层,请求也不可用。

显然,从每个控制器方法到每个服务方法的每次调用也可以传入IP或请求,但是由于我们有数以千计的这些调用和许多链,它实际上并不实用。

有人能想到更好的方法吗?

由于我们不负责服务的实例化(这些只是神奇地注入),我们甚至无法在为当前HTTP调用创建每个服务时传递IP。

更新1

如建议的那样,尝试了MDC路线。不幸的是,这似乎不起作用。

过滤器中的

import org.apache.log4j.MDC
class IpFilters  {

def filters = {

    all() {
        before = {
            MDC.put "IP", "1.1.1.1"
            println "MDC.put:" + MDC.get("IP")
        }
        afterView = { Exception e ->
            println "MDC.remove:" + MDC.get("IP")
            MDC.remove 'IP'
        }
}

在服务中:

import org.apache.log4j.MDC
:
def someMethod() {
    String ip = MDC.get("IP")
    println("someMethod: IP = $ip")
}

结果总是:

MDC.put:1.1.1.1
MDC.remove:1.1.1.1
someMethod: IP = null

因此服务无法访问过滤器中的线程上的MDC变量,这是一个真正的耻辱。问题可能是“someMethod”实际上是由springSecuirty调用的。

2 个答案:

答案 0 :(得分:1)

嗯,强烈建议我们应该让业务逻辑了解控制器逻辑。但是记住你的情况,你必须这样做,绝对可用。在您的服务方法中,写下此内容以记录当前请求的IP地址

import org.springframework.web.context.request.RequestContextHolder

// ... your code and class

def request = RequestContextHolder.currentRequestAttributes().getRequest()
println request.getRemoteAddr()

请确保,当从Grails请求上下文(如Job)调用相同的服务方法时,您处理从该行抛出的任何异常。

答案 1 :(得分:0)

我的两便士

基本上已经在上面使用了,当通过标准grails实践指示请求时它完全正常。

在这种情况下,用户触发websockets连接,然后使用Holders.applicationContext将其注入websockets侦听器。

问题出现在你的网络请求之外。

修复很痛苦,但在这种情况下对其他任何人都可以派上用场:

private static String userIp

 String getIp() {
        String i
        new Thread({
            //to bypass :
            // Are you referring to request attributes outside of an actual web request, or processing a
            // request outside of the originally receiving thread? If you are actually operating within a web request
            // and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet:
            // In this case, use RequestContextListener or RequestContextFilter to expose the current request.
            def webRequest = RequestContextHolder.getRequestAttributes()
            if(!webRequest) {
                def servletContext  = ServletContextHolder.getServletContext()
                def applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)
                webRequest =  grails.util.GrailsWebMockUtil.bindMockWebRequest(applicationContext)
            }
        //def request = RequestContextHolder.currentRequestAttributes().request
            def request = WebUtils.retrieveGrailsWebRequest().currentRequest
            i=request.getRemoteAddr()
            if (!i ||i == '127.0.0.1') {
                i=request.getHeader("X-Forwarded-For")
            }
            if (!i ||i == '127.0.0.1') {
                i=request.getHeader("Client-IP")
            }
            if (!i) { i="127.0.0.1"}
            this.userIp=i
        } as Runnable ).start()

        return i
    }

现在调用它时需要一些休眠时间,因为它作为一个runnable运行:

 def aa = getIp()
        sleep(300)
        println "$aa is aa"
        println "---- ip  ${userIp}"

还提供了在grails 3中调用请求def request = WebUtils.retrieveGrailsWebRequest().currentRequest的替代方法,注释掉的行.request在ide中无法识别(即使它有效)

new Thread({仍然是需要的,因为即使它在获得ip后返回ip它也试图保存到数据库并且周围出现了一些其他奇怪的问题

java.lang.RuntimeException: org.springframework.mock.web.MockHttpServletRequest.getServletContext()Ljavax/servlet/ServletContext;
    at org.apache.tomcat.websocket.pojo.PojoMessageHandlerBase.handlePojoMethodException(PojoMessageHandlerBase.java:119)
    at org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholeBase.onMessage(PojoMessageHandlerWholeBase.java:82)

因此,在此方案中获取请求属性的修复程序高于

对于模拟库,您需要在build.gradle中使用它:

 compile 'org.springframework:spring-test:2.5'

所以传奇继续 - 上面实际上似乎并没有在我的情况下工作,因为基本上这个请求是由用户发起的,但是当发送到websockets时 - 尝试检索请求(ip / session)的会话不是真正的真实用户。 / p>

这最终必须以一种非常不同的方式完成,所以非常陡峭地离开主题但是当这种尝试ip的方法不起作用时,剩下的唯一方法是通过SessionListeners:

src/main/groovy/{packageName}

中的

 class SessionListener implements HttpSessionListener {
        private static List activeUsers = Collections.synchronizedList(new ArrayList())

        static Map sessions = [:].asSynchronized()

        void sessionCreated (HttpSessionEvent se) {
            sessions.put(se.session.id, se.session)
        }

        void sessionDestroyed (HttpSessionEvent se) {
            sessions.remove(se.session.id)
        }
    }
grails-app/init/Application.groovy

中的

Closure doWithSpring() {
        { ->
            websocketConfig WebSocketConfig
        }
    }
// this already exists
static void main(String[] args) {
    GrailsApp.run(Application, args)
}

在同一个init文件夹中: class WebSocketConfig {

@Bean
public ServletContextInitializer myInitializer() {
    return new ServletContextInitializer() {
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {

            servletContext.addListener(SessionListener)
        }
    }
}

}

现在获取userIP,当套接字最初连接时,它将用户的会话发送到套接字。套接字在websockets usersession详细信息中注册该用户的会话。

尝试获取用户ip时(我已将用户ip注册到控制器/页面上的session.ip,然后点击页面打开套接字)

 def aa = SessionListener.sessions.find{it.key==sessionId}?.value
        println "aa $aa.ip"