2016年5月27日 星期五

【Travel】東京-富士山行_[5天4夜]_廉航晚去早回攻略 PartⅡ

pre-dayMystays Haneda Hotel
first-day新宿(東新宿歌舞伎町 APA 飯店 check-in)
second-day富士山
third-day台場(東新宿歌舞伎町 APA 飯店 check-out)
post-day大江戶溫泉物語
頭尾兩天搞定,再來進入主題,中間三天玩什麼?目標是去富士山河口湖,主題明確就很好規劃,去富士山的巴士是從新宿出發,當然也可以坐地鐵,但要轉車實在很累.. 自由行要玩得 graceful 當然最好一鏡到底,所以三天就是住東新宿,第一天新宿第二天富士山第三天台場

住宿可以選擇在新宿或附近的東新宿,因為兩者差一站價格有差,選在東新宿可以省一點錢。另外星期六的房價,不知日本星期六房價這麼誇張,快要是平日的兩倍,在台灣兩倍價格我遇到是出現在除夕到初三,若可以避開星期六,訂房則可以省一天的錢。
  1. 第一天:
  2. pre-day 住 mystrays 就 11 點在 checkout睡到超值,直接前往東新宿(東新宿歌舞伎町 APA 飯店 )免費寄放行李, 並跟櫃台人員說明大約晚上7點check-in,這樣省去寄放行李費用,又可以很沒有時間壓力下,在新宿玩一整個下午
  3. 第二天:
  4. 富士山的巴士要先預約新宿~富士五湖線   一個月前左右開放預約,像要訂5/15的巴士,就大約4/15訂就可以選到日期。要選往返價錢一個人 3,500 日幣
  5. 第三天:
  6. 11點在東新宿歌舞伎町 APA 飯店 checkout,直接前往大江戶溫泉(百合海鷗線「テレコムセンター」站) 放行李,他的門口有寄放行的置物櫃,一個400日幣,再前往台場逛。為何要先去寄放行李,因為在台場寄物 兩件1,600日幣(800×2),晚上去大江戶溫泉再付兩個櫃子800日幣(400×2),扣掉多坐兩站再折返回台場的交通費,少說也省下 1,000 日幣 (1,600-494[註1]=1,106)
[註1]:多坐兩站多62,テレコムセンター → 台場=185,兩個人乘2,算式: (62+185)*2=494

這樣解決住宿與去富士山的交通問題,小結一下 part1 的機票與這篇的住宿費,hotels.com 訂房集滿 10 晚送一晚,這一晚的價格可以扣 10 晚的均價,所以星期六 double 價格就壓下來,感覺像平日價一樣,這樣想心情愉快許多。這樣機+ 酒10,400+3,512+5,517=19,429,兩個人加總控制在二萬元內


東京5 Day 4 Night攻略,未完成,待續…

2016年5月25日 星期三

【Travel】東京-富士山行_[5天4夜]_廉航晚去早回攻略 PartⅠ

3 月時樂桃航空有4周年感恩回饋促銷,看起來真得很便宜。兩個人去回,掛一件行李,只要10,200 NT

3/3 號時立刻就訂了 5 月 的5 天 4 夜的東京行,但航班晚去早回,等於只有三天時間可以玩。但如何解決頭一天與最後一天的住宿問題,是首要的課題,這就是傳說中的紅眼班機。
  1. one night
  2. 去程的班機時間是晚上 8:45 到日本是凌晨 1 點了,一開始是看到有機場的膠囊旅館,3 月訂 5 月算是早鳥,早鳥優惠一個人也要 1500 台幣左右,兩個人加起了也要 3000 台幣,那不如住飯店,居住品質遠勝過限制諸多的膠囊旅館,所以放棄膠囊旅館開始搜尋羽田機場附近的飯店,當然要 24 小時櫃檯才能 check in 。坦白說,選擇性還真小,因為我要便宜、要離機場近,找到了 Hotel Mystays Haneda(羽田滿意飯店),3,500 台幣搞定,當然晚上已經沒有免費的接駁車,寫信去飯店問凌晨二點還接不接客,他還幫我解決了交通的疑惑,告知此時刻沒有接駁車,只能搭 taxi ,價錢是 1300 日幣,實際驗證後還真得是 一千三日幣分毫不差,讓小弟見證奇跡,好生感動。
    by the way, 我到羽田機場,看到一堆來睡機場, 為何不直接睡機場?
    如果你帶女朋友去,想活著回來,花點錢肯定值得 XD~

  1. last night
  2. 回程班機一大早5:55,等於4點多就要到機場,是可以玩到很晚去睡機場,但就太克難,整個就像去東京受苦受難,一點都不graceful。當然有為廉航而生的解決方案就是大江戶溫泉物語的「羽田空港 早朝便フライト前泊プラン」方案,只要3300日幣(入場加上幫你訂去機場的bus),可以在那休息,像我們是晚上在那用餐,泡湯,再到2F的休息室的沙發坐椅睡,3點50在到門口外等bus,3:50準時出發,20分鐘就到機場了。
東京5 Day 4 Night攻略,未完成,待續…

2016年5月24日 星期二

【Andriod】Google maps api key setup

  • Issue:
  • pub API key Caused by: java.lang.RuntimeException: API key not found. Check that <meta-data android:name="com.google.android.geo.API_KEY" android:value="your API key"> is in the <application> element of AndroidManifest.xml
  • Solution:
  • 使用 andriod project 申請 google map api ,需要三個步驟:
  1. 建立憑證來 Signed APK
  2. 在憑證取得 sha1 指紋
  3. google dev console 中開啟用 google map api ,貼上你的 project name 與步驟 1 的 sha1 指紋
說明:
  1. 建立憑證來 Signed APK
  2. 參考來源:developer.android.com
    可以手動指令或使用android studio ide來建立
    1. 方法一:手動輸入指令
    2. 
      keytool -genkey -v -keystore  -alias  -keyalg RSA -keysize 2048 -validity 10000
               
    3. 方法二:android studio IDE
    4. Build -> Generate Signed APK
  3. 在憑證取得 sha1 指紋
  4. 指令:keytool -list -v -keystore <key_store_path>
    快速完成:
    
    keytool -list -v -keystore <key_store_path> | grep SHA1 | awk -F"SHA1: " '{print $2}'
    
  5. google dev console 中開啟用 google map api ,貼上你的 project name 與步驟 1 的 sha1 指紋
  6. 進入google dev consoleGoogle Maps Android API啟用Google Maps Android API
    建立API 金鑰後,記下你的「API 金鑰」,並新增「套件名稱和指紋」
    輸入「套件名稱」與「SHA-1 憑證指紋」後,5分鐘內生效
最後進行部署android project 設定android project使用google map api key
  1. 編輯AndroidManifest.xml
  2. 
    <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key"></meta-data></key_store_path></key_alias></key_store_path></application></meta-data>
    
  3. 設定google_maps_key的value,編輯res/values/strings.xml
  4. 
    <string name="google_maps_key">YOUR_API_KEY</string>
    

2016年5月19日 星期四

【JAVA】LDAPS整合AD驗證

參考來源:ActiveDirectoryAuthentication
原作者是使用ldap,下面修改為ldaps驗證
為了安全考量,有一份堅持,
原作者是用nsLookup去取得AD的主機列表,
下面是先hardcode加上一台AD主機位址,並且加上憑證
當然憑證要匯到JDK的ca路徑
範例網域:contoso.com,當然要換成你要用的Domain


package com.uitox.shared.util;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.security.auth.login.AccountException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;

public class ActiveDirectoryAuthentication {
 private static final String CONTEXT_FACTORY_CLASS = "com.sun.jndi.ldap.LdapCtxFactory";

 private String ldapServerUrls[]={"ldap://fdc.contoso.com:636"};

 private int lastLdapUrlIndex;

 private final String domainName;

 public ActiveDirectoryAuthentication(String domainName) {
  this.domainName = domainName.toUpperCase();

  try {
//   ldapServerUrls = nsLookup(domainName);  
  } catch (Exception e) {
   e.printStackTrace();
  }
  lastLdapUrlIndex = 0;
 }

 public boolean authenticate(String username, String password)
   throws LoginException {
  if (ldapServerUrls == null || ldapServerUrls.length == 0) {
   throw new AccountException("Unable to find ldap servers");
  }
  if (username == null || password == null
    || username.trim().length() == 0
    || password.trim().length() == 0) {
   throw new FailedLoginException("Username or password is empty");
  }
  int retryCount = 0;
  int currentLdapUrlIndex = lastLdapUrlIndex;
  do {
   retryCount++;
   try {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY_CLASS);
    env.put(Context.PROVIDER_URL,
      ldapServerUrls[currentLdapUrlIndex]);
    env.put(Context.SECURITY_PROTOCOL, "ssl");

    System.setProperty("javax.net.ssl.trustStore","/jre/lib/security/cacerts");
    System.setProperty("javax.net.ssl.trustStorePassword","changeit");

    env.put(Context.SECURITY_PRINCIPAL, username + "@" + domainName);
    env.put(Context.SECURITY_CREDENTIALS, password);
    DirContext ctx = new InitialDirContext(env);
    ctx.close();
    lastLdapUrlIndex = currentLdapUrlIndex;
    return true;
   } catch (CommunicationException exp) {
    // TODO you can replace with log4j or slf4j API
    exp.printStackTrace();
    // if the exception of type communication we can assume the AD
    // is not reachable hence retry can be attempted with next
    // available AD
    if (retryCount < ldapServerUrls.length) {
     currentLdapUrlIndex++;
     if (currentLdapUrlIndex == ldapServerUrls.length) {
      currentLdapUrlIndex = 0;
     }
     continue;
    }
    return false;
   } catch (Throwable throwable) {
    throwable.printStackTrace();
    return false;
   }
  } while (true);
 }

 private static String[] nsLookup(String argDomain) throws Exception {
  try {
   Hashtable env = new Hashtable();
   env.put(Context.INITIAL_CONTEXT_FACTORY,
     "com.sun.jndi.dns.DnsContextFactory");
   env.put("java.naming.provider.url", "dns:");
   DirContext ctx = new InitialDirContext(env);
   Attributes attributes = ctx.getAttributes(
     String.format("_ldap._tcp.%s", argDomain),
     new String[] { "srv" });
   // try thrice to get the KDC servers before throwing error
   for (int i = 0; i < 3; i++) {
    Attribute a = attributes.get("srv");
    if (a != null) {
     List domainServers = new ArrayList();
     NamingEnumeration enumeration = a.getAll();
     while (enumeration.hasMoreElements()) {
      String srvAttr = (String) enumeration.next();
      // the value are in space separated 0) priority 1)
      // weight 2) port 3) server
      String values[] = srvAttr.toString().split(" ");
      domainServers.add(String.format("ldap://%s:%s",
        values[3], values[2]));
     }
     String domainServersArray[] = new String[domainServers
       .size()];
     domainServers.toArray(domainServersArray);
     return domainServersArray;
    }
   }
   throw new Exception("Unable to find srv attribute for the domain "
     + argDomain);
  } catch (NamingException exp) {
   throw new Exception("Error while performing nslookup. Root Cause: "
     + exp.getMessage(), exp)
  }
 }
}
主要改造說明
  1. ldapServerUrls直接加入主機 22行處改成,加上DC主機,ex:ldap://fdc.contoso.com:636
    
     private String ldapServerUrls[]={"ldap://fdc.contoso.com:636"};
    
    註解掉原nsLookup查表,因為我hardcode了 XD
  2. 指定憑證CA檔 先將企業內部root CA匯入,至/jre/lib/security/cacerts
    然後指定CA檔,讓java知道就行了
    
        System.setProperty("javax.net.ssl.trustStore","<JDK ROOT>/jre/lib/security/cacerts");
        System.setProperty("javax.net.ssl.trustStorePassword","changeit");
    
最後,在寫一個驗證的Class,作登錄就大功告成。

public class UserCredentialManager {
        ...
 public synchronized void login(String name, String password){
  boolean IsAuth = false;
  ActiveDirectoryAuthentication ADAuth = new ActiveDirectoryAuthentication("CONTOSO.COM");
  try {
   IsAuth = ADAuth.authenticate(name, password);
  } catch (LoginException e) {
   e.printStackTrace();
  }
  
  if(IsAuth == true){
   user = name;
  }
  
 }
        ...
}