2017년 12월 11일 월요일

AngularJS & Angular의 url에서 hashtag(#) 제거

  • AngularJS인 경우 설정 (** html에 <base href="/"> 추가)

1
2
3
angular.config(['$locationProvider'], function ($locationProvider) {
    $locationProvider.html5Mode(true);
});
  • Angular인 경우 설정 (** html에 <base href="/"> 추가)

1
2
3
4
5
6
@NgModule({
    imports: [
        RouterModule.forRoot(LAYOUT_ROUTES, { useHash: false })
    ],
    ...
})
  • UrlRewriteFilter 설정 (Spring Framework, Gradle)

    • 의존성 설정
compile("org.tuckey:urlrewritefilter:${urlrewritefilter_version}")
    • Custom UrlRewriteFilter 생성 (url-rewrite.xml 위치를 변경을 위해)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tuckey.web.filters.urlrewrite.Conf;
import org.tuckey.web.filters.urlrewrite.utils.StringUtils;

import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.InputStream;
import java.net.URL;

/**
 * Subclass of {@link org.tuckey.web.filters.urlrewrite.UrlRewriteFilter}
 * that overrides the configuration file loading mechanism.
 */
public class UrlRewriteFilter extends org.tuckey.web.filters.urlrewrite.UrlRewriteFilter {

    private final Logger log = LoggerFactory.getLogger(UrlRewriteFilter.class);

    @Override
    protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
        ServletContext context = filterConfig.getServletContext();
        String confPath = filterConfig.getInitParameter("confPath");
        boolean modRewriteStyleConf = getModRewriteStyleConf(filterConfig.getInitParameter("modRewriteConf"));
        try {
            final InputStream inputStream = getClass().getClassLoader().getResourceAsStream(confPath);
            final URL confUrl = getClass().getClassLoader().getResource(confPath);

            if (inputStream == null) {
                log.error("unable to find urlrewrite conf file at " + confPath);
                // set the writer back to null
                if (isLoaded()) {
                    log.error("unloading existing conf");
                    destroyUrlRewriter();
                }

            } else {
                Conf conf = new Conf(context, inputStream, confPath, confUrl.toString(), modRewriteStyleConf);
                checkConf(conf);
            }
        } catch (Throwable e) {
            log.error("unloading urlrewrite conf file at " + confPath, e);
            throw new ServletException(e);
        }
    }

    private boolean getModRewriteStyleConf(String modRewriteConf) {
        boolean modRewriteStyleConf = false;
        if (!StringUtils.isBlank(modRewriteConf)) {
            modRewriteStyleConf = "true".equals(StringUtils.trim(modRewriteConf).toLowerCase());
        }
        return modRewriteStyleConf;
    }
}
    • Servlet Filter에 넣기
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private void initUrlRewriteFilter(ServletContext servletContext, EnumSet<DispatcherType> disps) {
    FilterRegistration.Dynamic urlRewriteFilter =
        servletContext.addFilter("urlRewriteFilter", new UrlRewriteFilter());

    urlRewriteFilter.setInitParameter("confPath", "url-rewrite.xml");
    urlRewriteFilter.setInitParameter("confReloadLastCheck", "-1");
    urlRewriteFilter.setInitParameter("logLevel", "WARM");

    urlRewriteFilter.addMappingForUrlPatterns(disps, true, "/*");
    urlRewriteFilter.setAsyncSupported(true);
}
    • url-rewrite.xml 작성 (위치: src/main/resources)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 4.0//EN" "https://www.tuckey.org/res/dtds/urlrewrite4.0.dtd">

<urlrewrite >
    <!-- default -->
    <rule match-type="regex">
        <condition type="request-uri" operator="notequal">
            ^.*\.(html?|htc|js|css|xml|map|json|ico|bmp|gif|jpe?g|png|svg|eot|woff|woff2|ttf|pdf|swf|txt)$
        </condition>
        <from>^/(.*)$</from>
        <to last="true">/index.html</to>
    </rule>
</urlrewrite>

참고

  • https://github.com/spring-io/sagan/search?utf8=%E2%9C%93&q=urlrewrite.xml&type=


2017년 12월 7일 목요일

Nginx로 Reverse-Proxy 구성

Reverse-Proxy란?
  • 사용자의 요청을 받아서 반대편 네트워크에 있는 웹 서버에 전달하는 역할을 함
  • 단순 요청 전달만 함
  • 로드밸런서로의 역할을 할 수 있음
Nginx로 Reverse-Proxy 설정
그림1) Nginx로 Reverse-Proxy 환경 구성도
  • 정적 리버스 프락시 서버 구성
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# cat /etc/nginx/sites-available/default
upstream sample_proxy {
    server 192.168.56.2:8080;
    server 192.168.56.3:8080;
}
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;

    index index.html index.htm index.nginx-debian.html;

    server_name _;

    location / {
        proxy_pass http://sample_proxy;
    }
}
    • upstream영역에 프락시 대상 호스트의 목록 입력
    • upstream은 proxy할 타겟 서버를 설정을 위해 사용
    • location / 에 대한 모든 요청에 대해서, sample_proxy로 중계한다는 의미
  • 로드 밸런싱 메서드
    • 라운드로빈(Round-robin)은 기본적인  메서드로 모든 서버에 동등하게 요청
      1
      2
      3
      4
      upstream sample_proxy {
          server 192.168.56.2:8080;
          server 192.168.56.3:8080;
      }
      
    • least_conn은 연결이 가장 작은 서버로 요청
      1
      2
      3
      4
      5
      6
      upstream sample_proxy {
          least_conn;
      
          server 192.168.56.2:8080;
          server 192.168.56.3:8080;
      }
      
    • ip_hash는 클라이언트 IP주소를 기준으로 요청을 분배한다. IP주소가 같다면, 동일한 서버로 요청
      1
      2
      3
      4
      5
      6
      upstream sample_proxy {
          ip_hash; 
      
          server 192.168.56.2:8080;
          server 192.168.56.3:8080;
      }
      
    • hash는 유저가 정의한 key나 변수 혹은 이들의 조합을 해시해서 분산
      1
      2
      3
      4
      5
      6
      upstream sample_proxy {
          hash $request_uri consistent;
      
          server 192.168.56.2:8080;
          server 192.168.56.3:8080;
      }
      
    • least_time메서드는 NginX Plus에서 지원, 평균 레이턴시와 연결을 기준으로 검사해서 로드가 적은 서버로 요청
      1
      2
      3
      4
      5
      6
      upstream sample_proxy {
          least_time header; 
      
          server 192.168.56.2:8080;
          server 192.168.56.3:8080;
      }
참고
  • https://www.joinc.co.kr/w/man/12/proxy

2017년 12월 5일 화요일

Spring Data Common 1.x -> 2.x에서 CrudRepository 변동된 점

CrudRepository내에 함수 및 반환형태 변동
  • findOne이 사라짐
  • findById 는 Optional<T> 형태로 반환됨 (* JDK 8을 써야 함)

2017년 9월 28일 목요일

OffsetDateTime, ZonedDateTime, Instant

The javadocs say this:

  • "OffsetDateTime, ZonedDateTime and Instant all store an instant on the time-line to nanosecond precision. Instant is the simplest, simply representing the instant. OffsetDateTime adds to the instant the offset from UTC/Greenwich, which allows the local date-time to be obtained. ZonedDateTime adds full time-zone rules."

2017년 9월 20일 수요일

Tomcat 포트(80->8080, 443->8443) 라우팅 설정

iptables 설정
  • # iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
  • # iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
  • # netstat -ntl or iptables -t nat -L
  • # iptables restart
server.xml 변경
  • <Connector port="8080" proxyPort="80" protocol="HTTP/1.1"
               connectionTimeout="20000"
               enableLookups="false"
               redirectPort="443"
               URIEncoding="UTF-8" />
  • <Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
           keyAlias="tomcat"
           port="8443" proxyPort="443" maxThreads="200"
           scheme="https" secure="true" SSLEnabled="true"
           keystoreFile="conf/keystore파일" keystorePass="키스토어비밀번호"
           clientAuth="false" sslProtocol="TLS" />
참고
  • http://devzeroty.tistory.com/entry/Tomcat-80-443%ED%8F%AC%ED%8A%B8-NonRoot-%EA%B3%84%EC%A0%95%EC%9C%BC%EB%A1%9C-%EB%9D%84%EC%9A%B0%EA%B8%B0

2017년 5월 23일 화요일

개인서명 SSL 인증서 생성


  • 준비
    • openssl
  • 개인키 생성
    • $ openssl genrsa -des3 -out server.key 2048
  • 인증요청서 생성
    • $ openssl req -new -key server.key -out server.csr
      • Country Name (2 letter code) [XX]: KR
      • State or Province Name (full name) []: Seoul
      • Locality Name (eg, city) [Default City]:
      • Organization Name (eg, company) [Default Company Ltd]: Company
      • Organizational Unit Name (eg, section) []:
      • Common Name (eg, your name or your server's hostname) []: company.com
      • Email Address []:
  • 개인키에서 패스워드 제거
    • $ cp server.key server.key.origin
    • $ openssl rsa -in server.key.origin -out server.key
  • 인증서 생성
    • $ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
  • 참고
    • https://zetawiki.com/wiki/%EB%A6%AC%EB%88%85%EC%8A%A4_%EA%B0%9C%EC%9D%B8%EC%84%9C%EB%AA%85_SSL_%EC%9D%B8%EC%A6%9D%EC%84%9C_%EC%83%9D%EC%84%B1