LZH-2012
未曾清贫难成人,不经打击老天真。 自古英雄出炼狱,从来富贵入凡尘。 醉生梦死谁成气,拓马长枪定乾坤。 挥军千里山河在,立名扬威传后人。

CookieInterceptor

  该拦截器的目的是把cookie的name/value对设置到stack/action中。

  • 如果一个星号出现在cookiesName参数值中,它将会表明所有cookie名称将被注入到struts的action中,即使cookiesName是被逗号分隔,例如(cookie1,*,cookie2)。
  • 如果cookieName值为空,它将会表明没有cookie将会被注入到Struts的action。
  • 如果一个星号出现在cookiesValue参数中,它将会表明所有cookie名称,不管它的值,将会被注入到Struts的action中,只要cookie名称匹配这些在cookiesName参数中指定的值。
  • 如果cookiesValue值为空,它将表明所有匹配cookieName参数的cookie将会被注入到Struts的action中。

  为了具有一个被过滤cookies的Map表,action可以实现CookiesAware接口。

属性

  • cookiesName(必须):将要被注入到action中的cookies名称。如果有多个,它将会使用逗号分隔。如果需要所有cookies名称 ,可以使用*号表示。当任何匹配逗号分隔的cookies名称的cookie都是合格的。
  • cookiesValue(必须):如果cookies的名字匹配cookieName且它的值也匹配,该cookie将会被注入Struts的action中。如果有多个值,可以使用逗号分隔。如果为空,则表明任何值都匹配。如果多个值被指定(逗号分隔),它如果匹配任何一个值,则表明它匹配。
  • acceptCookieNames(可选):用来检查cookie的名称是否匹配该指定模式。

方法

  • populateCookieValueIntoStack:该方法将会决定是否该cookie值合格且被放入值栈中。
  • injectIntoCookiesAwareAction:该方法将会注入选择cookies(即Map中的)到实现CookieAware接口action中。

示例

<!--该例子将注入名称为cookie1或者cookie2且值为cookie1value或者cookie2value的cookies到Struts的action中-->
<action ... >
    <interceptor-ref name="cookie">
        <param name="cookiesName">cookie1, cookie2</param>
        <param name="cookiesValue">cookie1value, cookie2value</param>
    </interceptor-ref>
    ....
 </action>
<!--该例子将注入名称为cookie1或者cookie2且值为任何的cookies到Struts的action中-->
<action ... >
   <interceptor-ref name="cookie">
      <param name="cookiesName">cookie1, cookie2</param>
      <param name="cookiesValue">*</param>
   <interceptor-ref>
   ...
 </action>
<!--该例子将注入名称为cookie1且值为cookie1value或者名称为cookie2且值为cookie2value的cookies到Struts的action中-->
<action ... >
   <interceptor-ref name="cookie">
      <param name="cookiesName">cookie1</param>
      <param name="cookiesValue">cookie1value</param>
   </interceptor-ref>
   <interceptor-ref name="cookie">
      <param name="cookiesName"<cookie2</param>
     <param name="cookiesValue">cookie2value</param>
   </interceptor-ref>
   ....
 </action>
<!--该例子将会注入任何cookies不管它的值到Struts的action中-->
<action ... >
   <interceptor-ref name="cookie">
      <param name="cookiesName">*</param>
      <param name="cookiesValue">*</param>
   </interceptor-ref>
    ...
 </action>

源码如下:

public class CookieInterceptor extends AbstractInterceptor {

    private static final long serialVersionUID = 4153142432948747305L;

    private static final Logger LOG = LoggerFactory.getLogger(CookieInterceptor.class);

    private static final String ACCEPTED_PATTERN = "[a-zA-Z0-9\\.\\]\\[_'\\s]+";

    private Set<String> cookiesNameSet = Collections.emptySet();
    private Set<String> cookiesValueSet = Collections.emptySet();

    private ExcludedPatternsChecker excludedPatternsChecker;
    private AcceptedPatternsChecker acceptedPatternsChecker;

    @Inject
    public void setExcludedPatternsChecker(ExcludedPatternsChecker excludedPatternsChecker) {
        this.excludedPatternsChecker = excludedPatternsChecker;
    }

    @Inject
    public void setAcceptedPatternsChecker(AcceptedPatternsChecker acceptedPatternsChecker) {
        this.acceptedPatternsChecker = acceptedPatternsChecker;
        this.acceptedPatternsChecker.setAcceptedPatterns(ACCEPTED_PATTERN);
    }

	 //设置cookiesName,如果匹配它将会允许cookies被注入到action中,可以使用逗号分隔字符串
    public void setCookiesName(String cookiesName) {
        if (cookiesName != null)
            this.cookiesNameSet = TextParseUtil.commaDelimitedStringToSet(cookiesName);
    }

	 //设置cookiesValue,如果匹配将会引起该cookie被注入到action中,可以使用逗号分隔字符串
    public void setCookiesValue(String cookiesValue) {
        if (cookiesValue != null)
            this.cookiesValueSet = TextParseUtil.commaDelimitedStringToSet(cookiesValue);
    }

	 //设置允许的cookies名称字符串模式来防止远程命名执行漏洞
    public void setAcceptCookieNames(String commaDelimitedPattern) {
        acceptedPatternsChecker.setAcceptedPatterns(commaDelimitedPattern);
    }

    public String intercept(ActionInvocation invocation) throws Exception {
        if (LOG.isDebugEnabled()) {
            LOG.debug("start interception");
        }

        // contains selected cookies
        final Map<String, String> cookiesMap = new LinkedHashMap<String, String>();

        Cookie[] cookies = ServletActionContext.getRequest().getCookies();
        if (cookies != null) {
            final ValueStack stack = ActionContext.getContext().getValueStack();

            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                String value = cookie.getValue();

                if (isAcceptableName(name) && isAcceptableValue(value)) {
                    if (cookiesNameSet.contains("*")) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("contains cookie name [*] in configured cookies name set, cookie with name [" + name + "] with value [" + value + "] will be injected");
                        }
                        populateCookieValueIntoStack(name, value, cookiesMap, stack);
                    } else if (cookiesNameSet.contains(cookie.getName())) {
                        populateCookieValueIntoStack(name, value, cookiesMap, stack);
                    }
                } else {
                    LOG.warn("Cookie name [#0] with value [#1] was rejected!", name, value);
                }
            }
        }

		//即使我们没有任何cookies,注入cookieMap到action中
        injectIntoCookiesAwareAction(invocation.getAction(), cookiesMap);

        return invocation.invoke();
    }

	 //检查Cookie的值不包含漏洞代码
    protected boolean isAcceptableValue(String value) {
		//如果该值没有被排除且该值可接受,则返回true
        return !isExcluded(value) && isAccepted(value);
    }

	 //检查Cookie的名称不包含漏洞代码
    protected boolean isAcceptableName(String name) {
		//如果该名称没有被排除且该名称可接受,则返回true
        return !isExcluded(name) && isAccepted(name);
    }

	 //检查cookie的name/value可接受
    protected boolean isAccepted(String name) {
        AcceptedPatternsChecker.IsAccepted accepted = acceptedPatternsChecker.isAccepted(name);
        if (accepted.isAccepted()) {
            if (LOG.isTraceEnabled()) {
                LOG.trace("Cookie [#0] matches acceptedPattern [#1]", name, accepted.getAcceptedPattern());
            }
            return true;
        }
        if (LOG.isTraceEnabled()) {
            LOG.trace("Cookie [#0] doesn't match acceptedPattern [#1]", name, accepted.getAcceptedPattern());
        }
        return false;
    }

	 //检查cookie的name/value被排除
    protected boolean isExcluded(String name) {
        ExcludedPatternsChecker.IsExcluded excluded = excludedPatternsChecker.isExcluded(name);
        if (excluded.isExcluded()) {
            if (LOG.isTraceEnabled()) {
                LOG.trace("Cookie [#0] matches excludedPattern [#1]", name, excluded.getExcludedPattern());
            }
            return true;
        }
        if (LOG.isTraceEnabled()) {
            LOG.trace("Cookie [#0] doesn't match excludedPattern [#1]", name, excluded.getExcludedPattern());
        }
        return false;
    }

	 //填充cookie值到值栈的钩子,如果满足了规则(即如果cookie值匹配这些配置)
    protected void populateCookieValueIntoStack(String cookieName, String cookieValue, Map<String, String> cookiesMap, ValueStack stack) {
        if (cookiesValueSet.isEmpty() || cookiesValueSet.contains("*")) {
			//如果该拦截器被配置来接受任何cookie值或者没有cookiesValue被指定,只要cookie名称匹配,
			//我们将注入它到Struts的action中。
            if (LOG.isDebugEnabled()) {
                if (cookiesValueSet.isEmpty())
                    LOG.debug("no cookie value is configured, cookie with name ["+cookieName+"] with value ["+cookieValue+"] will be injected");
                else if (cookiesValueSet.contains("*"))
                    LOG.debug("interceptor is configured to accept any value, cookie with name ["+cookieName+"] with value ["+cookieValue+"] will be injected");
            }
            cookiesMap.put(cookieName, cookieValue); //放入Map中
            stack.setValue(cookieName, cookieValue); //放入值栈中
        }
        else {
			//如果cookieValues被指定,该cookie的值必须匹配,我们才注入它到Struts的action中
            if (cookiesValueSet.contains(cookieValue)) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("both configured cookie name and value matched, cookie ["+cookieName+"] with value ["+cookieValue+"] will be injected");
                }

                cookiesMap.put(cookieName, cookieValue);
                stack.setValue(cookieName, cookieValue);
            }
        }
    }

	 //设置cookiesMap到实现CookiesAware接口的action中的钩子
    protected void injectIntoCookiesAwareAction(Object action, Map<String, String> cookiesMap) {
        if (action instanceof CookiesAware) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("action ["+action+"] implements CookiesAware, injecting cookies map ["+cookiesMap+"]");
            }
            ((CookiesAware)action).setCookiesMap(cookiesMap);
        }
    }
}

使用演示

  • cookie拦截器的声明

  由于默认情况下不使用cookie拦截器(不在defaultStack中),因此在struts.xml配置文件中需要对其进行声明:

<action name="Index" class="com.lifelaf.blog.action.ExampleAction">
	<!--注意该拦截器的顺序很重要,因为需要使用ModelDrivenInterceptor中的Model
	这样才可以使用它的属性来存储username,password等属性-->
    <interceptor-ref name="defaultStack" />
    <interceptor-ref name="cookie">
        <param name="cookiesName">exampleKeyName1, exampleKeyName2</param>
        <param name="cookiesValue">*</param>
    </interceptor-ref>
    <result>/exampleResult.jsp</result>
</action>

  仅仅声明使用cookie拦截器是不够的,我们还需要对该拦截器的cookiesName参数和cookiesValue参数进行设定。如果不设定cookiesName参数,action类将不会收到任何Cookie:

//CookieInterceptor.java
for (Cookie cookie : cookies) {
    if (cookiesNameSet.contains("*")) {
        populateCookieValueIntoStack(name, value, cookiesMap, stack);
    } else if (cookiesNameSet.contains(cookie.getName())) {
        populateCookieValueIntoStack(name, value, cookiesMap, stack);
    }
}

  在之前的struts.xml配置文件实例中,cookiesName参数设定为exampleKeyName1exampleKeyName2,因此ExampleAction将会收到键为exampleKeyName1exampleKeyName2的Cookie。有趣的是,只要cookiesName中出现*(比如:exampleKeyName1, *, exampleKeyName2),那么action类将会收到所有的Cookie。

  而对于cookiesValue,我们可以用它来设定可接受的Cookie的值。如果cookiesValue未设定,或者cookiesValue中包含*,那么所有name属性符合cookiesName参数设定的Cookie都会被action收到。

  • ValueStack声明

  除了在struts.xml配置文件中声明cookie拦截器及其参数,cookie拦截器的使用还需要一个条件:ValueStack中必须包含cookiesName参数中所设定的那些属性;否则当截取Cookie的时候Struts2会抛异常(”No object in the CompoundRoot has a publicly accessible property named …”)。这是因为在截取Cookie的时候CookieInterceptor会尝试往ValueStack中写入cookie信息:

private String exampleKeyName1;
public String getExampleKeyName1() {
    return exampleKeyName1;
}
public void setExampleKeyName1(String exampleKeyName1) {
    this.exampleKeyName1 = exampleKeyName1;
}

private String exampleKeyName2;
public String getExampleKeyName2() {
    return exampleKeyName2;
}
public void setExampleKeyName2(String exampleKeyName2) {
    this.exampleKeyName2 = exampleKeyName2;
}

  至此,action类可以通过访问这些bean来读取Cookie的信息。

  • CookiesAware接口与cookiesMap

  除了设定ValueStack外,action类还可以通过实现CookiesAware接口来获取目标Cookie键值对的Map对象(cookiesMap):

public class ExampleAction extends ActionSupport implements CookiesAware {
    private Map<String, String> cookiesMap;
    @Override
    public void setCookiesMap(Map<String, String> cookiesMap) {
        this.cookiesMap = cookiesMap;
    }
}

  对于使用cookie拦截器而言,实现CookiesAware接口不是必需的(如果不实现CookiesAware,那么action类中不会含有Cookie键值对的Map对象),而在ValueStack中添加目标Cookie键则是必需的。

  • 结语

  与SessionAware等其它拦截器相比,cookie拦截器的使用显得更为复杂 — 既要声明目标Cookie,又要在ValueStack中加入相关支持。个人对此做法的理解是:这么做可以让action通过访问ValueStack来直接读取感兴趣的Cookie的值,相应的代价则是编程复杂度的上升。

  Struts2中的cookie拦截器只能用于读取Cookie,如果想向浏览器端发送Cookie则需要使用cookieProvider拦截器。

posted on 2016-03-30 22:11  LZH-2012  阅读(603)  评论(0编辑  收藏  举报