分享

我的shiro之旅: 十七 跨域session共享的一种解决方法

 KILLKISS 2015-07-29

在文章七里面有介绍session共享,不过只是在一个域名及其他子域间共享。有时候,我们需要在多个一级域名共享登录的session。当然,用shiro-cas是一个很不错的解决方法,应该也是大部分人使用的方法。不过因为种种原因,并没有选择shiro-cas的方式,就使用了其他方式代替,思路也来自shiro-cas。比如有www.a.com,www.两个域名需要共享session,并且www.域名下的session是主,登录在www.域名下,www.a.com域名下的session需要取www.域名下的session。大概画一个图:



在www.a.com中加一个filter,用于重定向到www.,取得b域名下的sessionId。代码贴出来:

  1. import javax.servlet.ServletRequest;  
  2. import javax.servlet.ServletResponse;  
  3. import javax.servlet.http.HttpServletRequest;  
  4.   
  5. import org.apache.commons.lang.StringUtils;  
  6. import org.apache.shiro.web.servlet.AdviceFilter;  
  7. import org.apache.shiro.web.util.WebUtils;  
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10.   
  11. import com.concom.security.infrastructure.helper.HttpHelper;  
  12. import com.concom.security.infrastructure.helper.ShiroSecurityHelper;  
  13.   
  14.   
  15. /**用于跨域共享session,那个应用需要与登录应用共享session,就开放此filter,并且应该是拦截所有请求,至少是拦截所有需要保护的资源 
  16.  * @author Dylan 
  17.  * @mail pickear@gmail.com 
  18.  * @time 2014年4月14日 
  19.  */  
  20. public class CasFilter extends AdviceFilter {  
  21.       
  22.     private final static Logger log = LoggerFactory.getLogger(CasFilter.class);  
  23.       
  24.     private String casServerURL;  //重定向的目标地址,该地址用于获取sessionId,如www./token  
  25.     private String domain;   //filter应用的域名,如www.a.com  
  26.   
  27.     @Override  
  28.     protected boolean preHandle(ServletRequest request, ServletResponse response)throws Exception {  
  29.           
  30.         boolean hasSyn = (null == ShiroSecurityHelper.getSession().getAttribute("hasSyn") ? false : (Boolean) ShiroSecurityHelper.getSession().getAttribute("hasSyn"));  
  31.         if(ShiroSecurityHelper.hasAuthenticated() || hasSyn){//当用户已经登录或者从session中取得的hasSyn为true,说明已经同步session,不需要再重定向  
  32.             return true;  
  33.         }  
  34.         String jsid = WebUtils.getCleanParam(request, "jsid");  
  35.         HttpServletRequest httpRequest = WebUtils.toHttp(request);  
  36.         String url = httpRequest.getRequestURL().toString();  
  37.         url = StringUtils.remove(url, httpRequest.getContextPath());  
  38.         if(StringUtils.isNotBlank(jsid)){//如果jsid不为空,说明是通过www.重定向回来的,将从b域名拿到的sessionId写回到自己域名下。  
  39.                        //以下两句作用是将jsid,rememberMe写到domain域名下的cookie中,读者可以自己实现。  
  40.             HttpHelper.setCookie(WebUtils.toHttp(httpRequest),WebUtils.toHttp(response), "jsid", jsid,domain,"/");  
  41.             HttpHelper.setCookie(WebUtils.toHttp(httpRequest),WebUtils.toHttp(response), "rememberMe", WebUtils.getCleanParam(request, "rememberMe"),domain,"/");  
  42.             WebUtils.issueRedirect(request, response, url);  
  43.             log.info("redirect : " + url);  
  44.             return false;  
  45.         }  
  46.         String uri = casServerURL + "?service=" + url;   //重写向到www./token下  
  47.         WebUtils.issueRedirect(request, response, uri);  
  48.         log.info("redirect : " + uri);  
  49.         return false;  
  50.     }  
  51.   
  52.     @Override  
  53.     protected void postHandle(ServletRequest request, ServletResponse response)throws Exception {  
  54.         super.postHandle(request, response);  
  55.     }  
  56.   
  57.     public void setCasServerURL(String casServerURL) {  
  58.         this.casServerURL = casServerURL;  
  59.     }  
  60.   
  61.     public void setDomain(String domain) {  
  62.         this.domain = domain;  
  63.     }  
  64.       
  65. }  



www./token请求的代码应该是如下的:

  1. /**用于跨域共享session,该方法应该在登录的应用开放。如登录页面在xxx.aa.com域名下,要和***.b共享session 
  2.      * 那么,在***.b应该有个filter,用来重写向到xxx.aa.com域名下的token请求,并带上回调地址,在token方法中 
  3.      * 会拿到xxx.aa.com下的sesison,以参数的形式再重定向回***.b。在bb域名的filter中拿到该jsid,应该保存 
  4.      * 到自己域名下的cookie里,以确保bb的cookie里jsid值和xxx.aa.com相同。filter的实现为casFilter。 
  5.      * @param request 
  6.      * @param response 
  7.      * @return 
  8.      */  
  9.         @RequestMapping(value="/token",method = RequestMethod.GET)  
  10.     public void token(HttpServletRequest request, HttpServletResponse response) {  
  11.         String service = WebUtils.getCleanParam(request, "service");  
  12.         String rememberMe = HttpHelper.getCookieValue(request, "rememberMe");  
  13.         Session session = ShiroSecurityHelper.getSession();  
  14.         session.setAttribute("hasSyn", true);   //将hasSyn为true设置到session中,CasFilter会从Session中取该值  
  15.         try {  
  16.             WebUtils.issueRedirect(request, response, service+"?jsid="+session.getId()+"&rememberMe="+rememberMe);  
  17.         } catch (IOException e) {  
  18.             e.printStackTrace();  
  19.         }  
  20.     }  



然后,将CasFilter交由spring 管理

  1.    <bean id="casFilter" class="com.xxx.security.interfaces.filter.CasFilter">  
  2. <property name="casServerURL" value="http://www./token"/>  
  3. <property name="domain" value="www.a.com"/>  
  4. /bean>  


在shiro的配置文件中,配置CasFilter拦截那些请求,大概如下:

  1.      <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
  2. <property name="securityManager" ref="securityManager" />  
  3. <property name="loginUrl" value="/security/login.html" />  
  4. <property name="successUrl" value="/home.html" />  
  5. <property name="unauthorizedUrl" value="/security/unauthorized.html" />  
  6. <property name="filters">  
  7.     <map>  
  8.         <entry key="cas" value-ref="casFilter" />  
  9.     </map>  
  10. </property>  
  11. <property name="filterChainDefinitions">  
  12.     <value>  
  13.         /** = cas,anon  
  14.     </value>  
  15. </property>  
  16. lt;/bean>  

        整个过程,目的只有一个,就是让www.a.com,www.两个域名的cookie共享。当然,cookie跨域共享方式很多,网上也有很多资料,包括通过iframe,js等方式。本文的这种方法,只是借仿shiro-cas的重定向方式,不过也有所不同。本文分享到这里,希望读者有更好的方式分享。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多