Apache反向代理服务器设置为Spring Security的网站

我有一个使用Spring Security进行login的Spring MVC应用程序。 我使用Apache Webserver作为代理和Tomcat。 以下是我的/etc/apache2/sites-enabled/example.com.conf文件:

ServerAdmin [email protected] ServerName example.com ServerAlias www.example.com DocumentRoot /var/www/example.com/public_html ProxyPreserveHost On ProxyRequests off ProxyPass /myapp/j_spring_security_check http://XX.YY.ZZ.WW:8080/myapp/j_spring_security_check ProxyPassReverse /myapp/j_spring_security_check http://XX.YY.ZZ.WW:8080/myapp/j_spring_security_check ProxyPass /myapp http://XX.YY.ZZ.WW:8080/myapp ProxyPassReverse /myapp http://XX.YY.ZZ.WW:8080/myapp 

我的问题是现在我必须访问我的网站:

www.example.com/myapp

在那里我想要访问它

www.example.com

我试着玩,但login没有正常工作。 我应该如何为此设置ProxyPass和ProxyPassReverse?

我一直在同样的问题挣扎了几天,我可能已经破解了它。 我是Spring Security的新手,所以不要把这当成福音! 其他人可能会反对…我使用Apache 2.4(在OS X上)和Spring Security 4.1.1。

一切都运行完好,本地运行,但每当它被部署运行在反向代理后面,我每次login时都会收到404错误。经过多次头部划伤和谷歌search,以下是我发现的:

(因为我没有足够的声望点发布更多的2个链接,所以我不得不在URL之后使用'http://'的空格!)

假设Apache和Tomcat运行在同一台主机上(本地主机),Apacheconfiguration为将来自www.example.com的请求代理到部署在上下文path“/ webapp”下的web应用程序

  ProxyPass / http://localhost:8080/webapp/ ProxyPassReverse / http://localhost:8080/webapp/ 
  1. 外部客户端请求受保护的URL:http:// http://www.example.com/secret

     GET /secret HTTP/1.1 
  2. Apache将其代理到http:// localhost:8080 / webapp / secret

  3. Spring的安全filter之一介入并响应redirect到/login

     HTTP/1.1 302 Found Location: http://www.example.com/login 
  4. 浏览器提取URL

     GET /login HTTP/1.1 
  5. Apache将其代理到http:// localhost:8080 / webapp / login

  6. Spring使用其默认login页面进行响应

     HTTP/1.1 200 OK 
  7. 需要注意的是,Spring生成的login表单为表单action元素添加了上下文path(即action =“/ webapp / login”)。 然后,当你点击提交button时,一个POST被执行到URL / webapp / login

     POST /webapp/login HTTP/1.1 

我们现在有一个问题。 当Apache将其代理到后端服务器时,得到的URL将是http:// localhost / webapp / webapp / login。 您可以在catalina.out日志中看到这一点,显示没有可处理请求的处理程序,因为上下文path现在在URL中显示两次。

这里的问题是,ProxyPass和ProxyReversePass指令(mod_proxy模块)只修改HTTP位置标头,URL保持不变。 所需要的是从URL中取消上下文path,然后到达将重新添加的代理。 Apache的RewriteRule似乎有诀窍:

 RewriteRule /webapp/(.*)$ http://localhost:8080/webapp/$1 [P] 

虽然这解决了404错误,我可以看到Apache正在代理正确的URL,但是我每次login都会重新显示login页面。接下来的configuration似乎解决了这个问题:

 ProxyPassReverseCookieDomain localhost www.example.com ProxyPassReverseCookiePath /webapp/ / 

我相信这可能是因为代理导致cookie中的域和path设置不正确,但是我必须详细阅读这些内容!

我希望这可以帮助别人,在这个领域比我更专业的人可以评论这是否是一个公平的解决scheme。

你可以定义虚拟主机。 这样的事情应该这样做,我想:

 <VirtualHost *:80> ServerAdmin [email protected] ProxyRequests Off ProxyPreserveHost On ProxyPass / http://localhost:8080/myapp connectiontimeout=5 timeout=30 ProxyPassReverse / http://localhost:8080/myapp ServerName youname.it </VirtualHost> 

我在这里用Apache 2.4来运行这个设置

如果你定义你自己的Filter&HttpServletRequestWrapper,在Spring Securityfilter之前插入Filter,这是可能的。 通过覆盖“getContextPath”返回空string,可以设置一个nginx / apache反向代理。 (这个基本概念来自: http : //www.lacerta.be/d7/content/keeping-real-user-ip-java-web-apps-behind-nginx-proxy ,但是不包括上下文path)

在您的依赖pipe理软件中,添加Servlet API:

  <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> 

(这当然假设你将运行在Tomcat / J2EE容器中)。

然后,在你的项目或共享库中,定义两个类:

  import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class RealIPFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest) { chain.doFilter(new RealIPWrapper((HttpServletRequest)request), response); } else { chain.doFilter(request, response); } } @Override public void destroy() {} @Override public void init(FilterConfig config) throws ServletException {} } 

然后包装:

  public class RealIPWrapper extends HttpServletRequestWrapper { public RealIPWrapper(HttpServletRequest request) { super(request); } @Override public String getContextPath() { return ""; } @Override public String getRemoteAddr() { String realIP = super.getHeader("X-Real-IP"); return realIP != null ? realIP : super.getRemoteAddr(); } @Override public String getRemoteHost() { try { return InetAddress.getByName(this.getRemoteAddr()).getHostName(); } catch (UnknownHostException|NullPointerException e) { return getRemoteAddr(); } } } 

然后在spring filter(web.xml) 之前添加正确的filter

 <filter> <filter-name>RealIPFilter</filter-name> <filter-class>RealIPFilter</filter-class> </filter> <filter-mapping> <filter-name>RealIPFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

那么在NGINX服务器块中:

  location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-NginX-Proxy true; proxy_cookie_path /<context-path>/ /; proxy_pass http://localhost:8080/<context>/; proxy_redirect off; }