2015. 1. 26.

iOS Push Notification Service with JDK7

Java 7 에서는 Apple 에서 발급한 인증서를 사용하지 못한다.
javapns.communication.exceptions.InvalidCertificateChainException: Invalid certificate chain (Received fatal alert: certificate_unknown)!  Verify that the keystore you provided was produced according to specs...
 at javapns.notification.PushNotificationManager.sendNotification(PushNotificationManager.java:410)
 at javapns.notification.PushNotificationManager.sendNotification(PushNotificationManager.java:350)
 at javapns.notification.PushNotificationManager.sendNotification(PushNotificationManager.java:320)
 at javapns.Push.sendPayload(Push.java:177)
 at javapns.Push.payload(Push.java:149)
 ...
jdk1.6에서 인증서 파일 포맷을 변환하고 (.p12 to .jks)
그 파일을 다시 jdk1.7에서 변환하면(.jks to .p12) 정상적으로 작동 한다.

/usr/local/jdk1.6/bin
keytool -importkeystore -destkeystore CERTIFICATES.jks -srckeystore CERTIFICATES.p12 -srcstoretype PKCS12

/usr/local/jdk1.7/bin
keytool -importkeystore -srckeystore CERTIFICATES.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore CERTIFICATES.jdk7.p12

참고 : https://code.google.com/p/javapns/issues/detail?id=165

2014. 10. 13.

Configuring SiteMesh3 with DeviceResolver - Java based configuration

web.xml
<filter>
    <filter-name>deviceResolverHandlerFilter</filter-name>
    <filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class>
</filter>

<filter>
    <filter-name>sitemesh</filter-name>
    <filter-class>com.yoursite.common.filter.SiteMeshFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>sitemesh</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

SiteMeshFilter.java
package com.yoursite.common.filter;

import com.yoursite.common.SiteMeshDecoratorSelector;

import org.sitemesh.builder.SiteMeshFilterBuilder;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * sitemesh 설정
 * 
 * @author ysh3342
 * @since 2014.10.07
 *
 */
public class SiteMeshFilter extends ConfigurableSiteMeshFilter {
 private static final Logger logger = LoggerFactory
   .getLogger(SiteMeshFilter.class);

 @Override
 protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
  logger.debug("applyCustomConfiguration");

  final String path = "/WEB-INF/vm/_decorators/";
  SiteMeshDecoratorSelector decoratorSelector = new SiteMeshDecoratorSelector();

  decoratorSelector.putMapping(SiteMeshDecoratorSelector.DEFAULT, path
    + "default.vm");
  decoratorSelector.putMapping(SiteMeshDecoratorSelector.MOBILE, path
    + "mobile.vm");

  decoratorSelector.put("/popup/*", path + "popup.vm");
  decoratorSelector.put("/ajax/*", path + "null.vm"); // TEST

  decoratorSelector.setUseCookie(true);

  // Assign
  builder.setCustomDecoratorSelector(decoratorSelector)
    .addExcludedPath("/jsp/*").addExcludedPath("/*.jsp");
 }

}

SiteMeshDecoratorSelector.java
package com.yoursite.common;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.sitemesh.config.PathBasedDecoratorSelector;
import org.sitemesh.content.Content;
import org.sitemesh.content.ContentChunk;
import org.sitemesh.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mobile.device.site.SitePreference;
import org.springframework.mobile.device.site.SitePreferenceHandler;

/**
 * 상황별 데코레이터를 선택한다
 * 
 * @author ysh3342
 * @since 2014.10.07
 */
public class SiteMeshDecoratorSelector extends
  PathBasedDecoratorSelector<WebAppContext> {

 private static final Logger logger = LoggerFactory
   .getLogger(SiteMeshDecoratorSelector.class);

 public static final String DEFAULT = "default";
 public static final String MOBILE = "mobile";
 public static final String TABLET = "tablet";

 private final Map<String, String> mappings = new HashMap<String, String>();

 private boolean useCookie = false;
 private String cookieName = "PC";

 @Override
 public String[] selectDecoratorPaths(Content content,
   WebAppContext siteMeshContext) throws IOException {
  logger.debug("selectDecoratorPaths(Content content, WebAppContext siteMeshContext)");

  // If page contains <meta name='decorator'
  // content='/some/dec.html,/another/dec.html'>
  ContentChunk decoratorMetaTag = content.getExtractedProperties()
    .getChild("meta.decorator");
  if (decoratorMetaTag.hasValue()) {
   logger.debug("decoratorMetaTag : {}", decoratorMetaTag.getValue());
   return decoratorMetaTag.getValue().split(",");
  }

  // request path 기준으로 매핑된 decorator path를 가져온다
  String[] pathDecorator = super.getPathMapper().get(
    siteMeshContext.getPath());
  if (pathDecorator != null) {
   logger.debug("pathDecorator : {}", (Object[]) pathDecorator);
   return pathDecorator;
  }

  String decorator;
  final SitePreference sitePreference = (SitePreference) siteMeshContext
    .getRequest()
    .getAttribute(
      SitePreferenceHandler.CURRENT_SITE_PREFERENCE_ATTRIBUTE);

  if (sitePreference.isMobile()) {
   logger.debug("Mobile");
   decorator = mappings.get(MOBILE);
  } else if (sitePreference.isTablet()) {
   logger.debug("Tablet");
   decorator = mappings.get(TABLET);
  } else {
   logger.debug("PC");
   decorator = mappings.get(DEFAULT);
  }

  if (decorator == null) {
   decorator = "/WEB-INF/vm/_decorators/default.vm";
   logger.info("Fail to load decorator path.");
   logger.info("Using decorator path [{}] instead.", decorator);

   // String[] empty = {};
   // return empty;
  }
  logger.debug("decorator : {}", decorator);

  return decorator.split(",");
 }

 public void putMapping(String pattern, String value) {
  if (value != null) {
   mappings.put(pattern, value);
  }
 }

 public boolean isUseCookie() {
  return useCookie;
 }

 public void setUseCookie(boolean useCookie) {
  this.useCookie = useCookie;
 }

 public String getCookieName() {
  return cookieName;
 }

 public void setCookieName(String cookieName) {
  this.cookieName = cookieName;
 }

}

2014. 10. 8.

Mobile version of sitemesh decorator

pom.xml

    org.springframework.mobile
    spring-mobile-device
    1.1.2.RELEASE


Spring interceptor
<mvc:interceptors>
    <!-- request.setAttribute(DeviceUtils.CURRENT_DEVICE_ATTRIBUTE = "currentDevice", org.springframework.mobile.device.Device); -->
    <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
    <!-- request.setAttribute(SitePreferenceHandler.CURRENT_SITE_PREFERENCE_ATTRIBUTE = "currentSitePreference", org.springframework.mobile.device.site.SitePreference); -->
    <bean class="org.springframework.mobile.device.site.SitePreferenceHandlerInterceptor" />
</mvc:interceptors>

SpringMobileParameterDecoratorMapper.java
package com.yoursite.common.mapper;

import java.util.Properties;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mobile.device.site.SitePreference;
import org.springframework.mobile.device.site.SitePreferenceUtils;

import com.opensymphony.module.sitemesh.Config;
import com.opensymphony.module.sitemesh.Decorator;
import com.opensymphony.module.sitemesh.DecoratorMapper;
import com.opensymphony.module.sitemesh.Page;
import com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper;

/**
 * sitemesh 2.4 용 DecoratorMapper
 * @author ysh3342
 *
 */
public class SpringMobileParameterDecoratorMapper extends ConfigDecoratorMapper {
 private static final Logger logger = LoggerFactory
   .getLogger(SpringMobileParameterDecoratorMapper.class);
 // private String decoratorName = null;

 private static final String MOBILE_DECORATORNAME = "mobile";
 private static final String TABLET_DECORATORNAME = "tablet";

 public void init(Config config, Properties properties,
   DecoratorMapper parent) throws InstantiationException {
  super.init(config, properties, parent);
  // decoratorName = properties.getProperty("decorator.name", "mobile");
  // logger.debug("init decoratorName : {}", decoratorName);
 }

 public Decorator getDecorator(final HttpServletRequest request,
   final Page page) {
  SitePreference sitePreference = SitePreferenceUtils
    .getCurrentSitePreference(request);
  if (sitePreference != null) {
   logger.debug("sitePreference.isNormal() : {}",
     sitePreference.isNormal());
   logger.debug("sitePreference.isMobile() : {}",
     sitePreference.isMobile());
   logger.debug("sitePreference.isTablet() : {}",
     sitePreference.isTablet());
   if (sitePreference.isMobile()) {
    return getNamedDecorator(request, MOBILE_DECORATORNAME);
   } else if (sitePreference.isTablet()) {
    return getNamedDecorator(request, TABLET_DECORATORNAME);
   } else {
    return super.getDecorator(request, page);
   }
  }
  logger.debug("super.getDecorator(request, page);");
  return super.getDecorator(request, page);
 }
}

sitemesh.xml
<property name="decorators-file" value="/WEB-INF/decorators.xml"/>
<decorator-mappers>
    <mapper class="com.yoursite.common.mapper.SpringMobileParameterDecoratorMapper">
        <param name="config" value="${decorators-file}"/>
    </mapper>
</decorator-mappers>

2013. 1. 7.

sitemesh FileDecoratorMapper 취약점


문제
파라미터를 사용하여 특정 파일을 데코레이터로 사용.
해당 파일의 내용이 노출됨.

Ex) /WEB-INF/web.xml 의 내용이 노출되는 경우
http://domain/?confirm=true&decorator=%2FWEB-INF%2Fweb.xml


해결 방안
sitemesh.xml 에서 아래 설정을 제거한다. (사용여부 확인 필)
<mapper class="com.opensymphony.module.sitemesh.mapper.FileDecoratorMapper">
</mapper>

2013. 1. 3.

[eclipse] urlrewrite.xml error

urlrewrite,xml error
Referenced file contains errors (http://tuckey.org/res/dtds/urlrewrite3.2.dtd).

DTD 경로에 www를 추가 한다
http://www.tuckey.org/res/dtds/urlrewrite3.2.dtd