在上一篇文章,我实现了Spring Security3.2搭建的第一个网站应用firstWeb. 进阶-使用Spring Security3.2搭建LDAP认证受权和Remember-me。 而在更早的时候,我有使用Apache+tomcat搭建cluster,Apache httpd与tomcat集群。接下来,我将利用以前的做品,继续向更远的地方前进。java
为firstWeb应用建立cluster。node
建立并部署一个新的应用secondWeb。web
使用Remember-me机制实现firstWeb与secondWeb的SSO。算法
此图是为最终形态而画的,今天的实验并不涉及CAS,请忽略CAS。apache
由上图可见,Apache做为一个逆向代理,提供两个虚拟主机,两个虚拟主机均在.test.com域中。其中一个虚拟主机为cluster, Apache须要为其提供负载平衡服务。另外,Apache开启了SSL。tomcat
全部Apache的配置能够在此下载:http://pan.baidu.com/s/1i37fUFbcookie
因为以前的文章已经有配置好OpenLDAP,OpenSSL,因此本文将在此基础上搭建网站。想从头开始的请往前翻。session
FirstWeb是咱们在上一篇文章所产生的应用,若是须要将FirstWeb部署为cluster的话,须要四个步骤:架构
在FristWeb的web.xml中添加配置,使其支持session同步。dom
<distributable/>
配置tomcat1和tomcat2的server.xml,使它们成为cluster。详细见Apache httpd与tomcat集群 。
配置Apache逆向代理,作负载平衡。
<VirtualHost *:443> ServerAdmin joey ServerName first.test.com ErrorLog "logs/firstWebDetail" CustomLog "logs/accessFisrtWeb" common SSLEngine on SSLCipherSuite RC4-SHA:AES128-SHA:HIGH:MEDIUM:!aNULL:!MD5 SSLCertificateFile "C:/Program Files (x86)/Apache Software Foundation/Apache2.2/conf/server.crt" SSLCertificateKeyFile "C:/Program Files (x86)/Apache Software Foundation/Apache2.2/conf/server.key" LogLevel debug ProxyRequests Off ProxyPreserveHost On ProxyPass /firstWeb balancer://mycluster/firstWeb ProxyPassReverse /firstWeb balancer://mycluster/firstWeb <Proxy balancer://mycluster> BalancerMember ajp://127.0.0.1:8009 loadfactor=1 route=node1 BalancerMember ajp://127.0.0.1:8010 loadfactor=1 route=node2 ProxySet stickysession=JSESSIONID ProxySet lbmethod=byrequests </Proxy> </VirtualHost>
配置DNS,创建ip和域名映射。(无条件的同窗能够修改本地的hosts文件)。
FirstWeb的源码在此:http://pan.baidu.com/s/1nthpuDN
快速开发secondWeb,并将其部署到tomcat3上。
SecondWeb很是简单,它只包含两个页面,一个登录页面和一个Index页面。Index页面是受保护的,未认证的用户访问奖杯转到登录页面。secondWeb的源码能够从这里下载http://pan.baidu.com/s/1o64TQ9k。 SecondWeb一样使用Spring Security 3.2,并使用和FirstWeb相同的LDAP。
配置逆向代理:
<VirtualHost *:443> ServerAdmin joey ServerName second.test.com ErrorLog "logs/secondWebDetail" CustomLog "logs/accessSecondWeb" common SSLEngine on SSLCipherSuite RC4-SHA:AES128-SHA:HIGH:MEDIUM:!aNULL:!MD5 SSLCertificateFile "C:/Program Files (x86)/Apache Software Foundation/Apache2.2/conf/server.crt" SSLCertificateKeyFile "C:/Program Files (x86)/Apache Software Foundation/Apache2.2/conf/server.key" LogLevel debug ProxyRequests Off ProxyPreserveHost On ProxyPass /secondWeb ajp://127.0.0.1:8011/secondWeb ProxyPassReverse /secondWeb ajp://127.0.0.1:8011/secondWeb </VirtualHost>
如今启动tomcat1,2,3, 而后启动apache,咱们能够访问如下两个网站了:
https://first.test.com/firstWeb
https://second.test.com/secondWeb
Remember-me的原理是在cookie中存有一个token,这个token包含了用户名,加密后的密码,过时时间,以及一个混入的key。remember me cookie value的生成默认算法为:
value = Base64(username:MD5(password):expireDateValue:key)
MD5是默认的,能够选择不一样的算法为密码加密。 key能够声明,若是没有声明key,Spring Security将使用一个Random GUUID. GUUID在不一样的server,不一样的context下都是不一样的。每次重启server也会不一样。咱们固然能够声明一个固定的key值。
当用户携带这个cookie访问网站的时候,网站会使用此token与LDAP中的用户信息比对,比对成功之后,则自动登陆此用户。
因而我想,若是个人两个网站共用remember me的cookie的话,一旦一个网站登陆了,另外一个网站就能够实现自动登陆了。
须要克服的一个难点,Spring Security中的RememberMeAuthenticationFilter所使用的cookie是有限制的,例如
secondWeb的remember-me cookie只对http://second.test.com/secondWeb有用,我须要改写cookie的做用域和cookie的过时时间,以达到对整个.test.com域的做用。
我须要在上面的firstWeb和secondWeb基础上,修改代码。步骤以下:
配置相同的LDAP认证。(已经知足,跳过)
修改代码,为每一个remember me指定相同的混入key -".test.com"。 以下:
@Override public void configure(HttpSecurity http) throws Exception { http ... ... .and() .rememberMe().tokenValiditySeconds(15*24*60*60) .key(".test.com"); // 使用一个固定的key. }
建立一个新的SevletFilter,进行RememberMe cookie的改写。
//RememberMeCookieFilter.java public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)request; Cookie newCookie = null; Cookie[] cookies = req.getCookies(); if ((cookies != null) && (cookies.length > 0)) { for (Cookie cookie : cookies) { if ("remember-me".equals(cookie.getName())) { newCookie = new Cookie(cookie.getName(), cookie.getValue()); newCookie.setHttpOnly(cookie.isHttpOnly()); newCookie.setSecure(cookie.getSecure()); // Control following // 设置cookie过时时间 newCookie.setMaxAge(cookie.getMaxAge()); // 设置cookie的有效路径 newCookie.setPath("/"); // 设置cookie的有效域 newCookie.setDomain(".test.com"); } } } chain.doFilter(request, response); if (newCookie != null) { HttpServletResponse rep = (HttpServletResponse)response; rep.addCookie(newCookie); } }
将RememberMeCookieFilter注册到SpringSecurityFilterChain中去。在每一个httpSecurity中注册,以下例子:
public void configure(HttpSecurity http) throws Exception { // 将RememberMeCookieFilter注册到SpringSecurityFilterChain中 // 并放置在HeaderWriterFilter以前。 http .addFilterBefore(new RememberMeCookieFilter(), HeaderWriterFilter.class) ... ... }
(没有包含在实验中)为logout添加一个handler,将cookie删除。
而后从新打包部署FirstWeb和SecondWeb。
经过上面的filter,remember me cookie的做用域变成了
domain: .test.com path: /
两个网站 https://first.test.com/firstWeb 与 https://second.test.com/secondWeb 共用remember me cookie.
如今两个网站能够经过使用Remember Me的cookie进行SSO了。获得启发的人,能够对上面的代码进行改进,使得更健壮。
这部分的代码能够从如下地址下载:http://pan.baidu.com/s/1jDDpW