控制器层可以使用request.getRemoteAddr()
和/或request.getHeader("Client-IP")
等获取IP。
但是,在服务层的内容中,我们可能希望记录用户检测到的或可疑的欺诈活动以及用户的IP地址。但是,IP不可用于服务层,请求也不可用。
显然,从每个控制器方法到每个服务方法的每次调用也可以传入IP或请求,但是由于我们有数以千计的这些调用和许多链,它实际上并不实用。
有人能想到更好的方法吗?
由于我们不负责服务的实例化(这些只是神奇地注入),我们甚至无法在为当前HTTP调用创建每个服务时传递IP。
如建议的那样,尝试了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调用的。
答案 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"