2014年4月30日水曜日

Apache HTTP Server 2.4 64bit SSL 設定

C:\Program Files\Apache Software Foundation\Apache24\conf\httpd.conf を編集
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf 
ServerAdmin admin@s6131.jp
ServerName www.s6131.jp:80

C:\Program Files\Apache Software Foundation\Apache24\conf\extra\httpd-ssl.conf を編集

"C:/Apache24" を "C:/Program Files/Apache Software Foundation/Apache24" に変更
ServerName www.s6131.jp:443
ServerAdmin admin@s6131.jp 
SSLCertificateFile "c:/Program Files/Apache Software Foundation/Apache24/conf/server.crt"
SSLCertificateKeyFile "c:/Program Files/Apache Software Foundation/Apache24/conf/server.key"
SSLCACertificateFile "c:/OpenSSL-Win64/bin/demoCA/cacert.pem"

DNS 無いので C:\Windows\System32\Drivers\etc\hosts を編集
127.0.0.1       www.s6131.jp

C:\OpenSSL-Win64\bin\openssl.cfg をopenssl.cfg.sav にバックアップしてから編集。
[usr_cert]
basicConstraints=CA:FALSE
nsCertType = server
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

秘密鍵(server.key)、公開鍵(server.csr)、証明書(server.crt)、PKCS#12(server.p12) の作成
CSR作成時の common name は www.s6131.jp を指定
cd C:\Program Files\Apache Software Foundation\Apache24\conf
openssl genrsa -out server.key 2048
openssl rsa -in server.key -out server.key
openssl req -new -days 365 -key server.key -out server.csr 
cd C:\OpenSSL-Win64\bin
openssl ca -days 365 -in "/Program Files/Apache Software Foundation/Apache24/conf/server.csr" -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out "/Program Files/Apache Software Foundation/Apache24/conf/server.crt" 
cd C:\Program Files\Apache Software Foundation\Apache24\conf
openssl pkcs12 -export -in server.crt -inkey server.key -certfile /OpenSSL-Win64/bin/demoCA/cacert.pem -out server.p12 
copy server.p12 ..\htdocs

Apache サービスを再起動
ブラウザで  PKCS#12 ファイルを実行、証明書のインポート。
http://www.s6131.jp/server.p12
デフォルトのまま「次へ」を繰り返し、「完了」
(証明書ストアで「証明書の種類に基づいて、自動的に証明書ストアを選択する」を選択)

ブラウザで警告なしで SSL アクセスできることを確認。(Firefox 除く)

https://www.s6131.jp/


2014年4月28日月曜日

Windows 8.1 64bit OpenSSL インストール 認証局の作成

OpenSSL 64bit v1.0.1g をダウンロード。
http://slproweb.com/products/Win32OpenSSL.html
ダウンロードした Win64OpenSSL-1_0_1g.exe を実行し、インストール。
環境変数 Path に C:\OpenSSL-Win64\bin; を追加。
環境変数 OPENSSL_CONF に C:\OpenSSL-Win64\bin\openssl.cfg を設定。
認証局を作成する為、C:\OpenSSL-Win64\bin\CA.pl を動かしたい。
ActivePerl Community Edition 64bit をダウンロード。
http://www.activestate.com/
ダウンロードした ActivePerl-5.16.3.1604-MSWin32-x64-298023.msi を実行し、インストール。

C:\OpenSSL-Win64\bin\openssl.cfg をopenssl.cfg.sav にバックアップしてから編集。
[ req ]
default_bits = 2048
[usr_cert]
basicConstraints=CA:TRUE
nsCertType = sslCA, emailCA
keyUsage = cRLSign, keyCertSign
コマンドプロンプト(管理者)で以下を実行し、認証局を作成する。
CA.pl での common name は s6131.jp を指定
cd C:\OpenSSL-Win64\bin
perl CA.pl -newca
C:\OpenSSL-Win64\bin\openssl.cfg をバックアップから戻しておく。

2014年4月17日木曜日

Android Spring Security ログイン

ログイン状態(Cookie)を保持する為、HttpClient を Singleton で取得する。
保持している static が初期化された場合、再度ログインが必要。

jp.s6131.sampleApp.SampleHttpClient.java
public class SampleHttpClient {
    private static DefaultHttpClient httpClient = new DefaultHttpClient();
    private SampleHttpClient() {}
    public static DefaultHttpClient getInstance() {
        return httpClient;
    }
}

メイン画面にテキストボックスとボタンを配置、クリックで AsyncHttpRequest 実行、
同クラスから呼び出す setResult メソッドで GridView に結果を表示。

jp.s6131.sampleApp.HelloAndroidActivity.java
public class HelloAndroidActivity extends Activity {
    private static String TAG = "sampleApp";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        setContentView(R.layout.main);
    }
    public void onClick(View v) {
        EditText searchText = (EditText) findViewById(id.search_text);
        Uri.Builder builder = new Uri.Builder();
        AsyncHttpRequest task = new AsyncHttpRequest(
                this,
                searchText.getText().toString()
                );
        task.execute(builder);
    }
    public void setResult(Code[] resList) {
        if (resList != null && resList.length != 0) {
            List<String> resultList = new ArrayList<String>();
            for (Code res : resList) {
                resultList.add((res.id == null ? "null" : res.id));
                resultList.add((res.code == null ? "null" : res.code));
                resultList.add((res.name == null ? "null" : res.name));
            }
            ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, R.layout.rowdata, resultList);
            GridView gridTable = (GridView) findViewById(id.grid_table);
            gridTable.setAdapter(arrayAdapter);
        }
    }
}

Spring Security 未ログインの場合、LoginActivity へ遷移。

jp.s6131.sampleApp.AsyncHttpRequest.java
public class AsyncHttpRequest extends AsyncTask<Uri.Builder, Void, Code[]> {
    private HelloAndroidActivity mainActivity;
    private String searchText;
    public AsyncHttpRequest(HelloAndroidActivity activity, String searchText) {
        this.mainActivity = activity;
        this.searchText = searchText;
    }
    @Override
    protected Code[] doInBackground(Uri.Builder... builder) {
        RestTemplate template = new RestTemplate();
        template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        DefaultHttpClient httpClient = SampleHttpClient.getInstance();
        template.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
        String url = "http://10.0.2.2/sample/sampleList?cd=" + searchText;
        try {
            ResponseEntity<Code[]> responseEntity = template.exchange(url, HttpMethod.GET, null, Code[].class);
            Code[] resList = responseEntity.getBody();
            return resList;
        } catch (Exception e) {
            Log.d("Error", e.toString());
        }
        return null;
    }
    @Override
    protected void onPostExecute(Code[] resList) {
        if (resList != null && resList.length != 0) {
            if ("-1".equals(resList[0].getId())) {
                Intent intent = new Intent(mainActivity.getApplicationContext(), LoginActivity.class);
                mainActivity.startActivity(intent);
                return;
            }
        }
        mainActivity.setResult(resList);
    }
}

ログイン画面にユーザー、パスワードのテキストボックスとボタンを配置、クリックで AsyncLoginRequest 実行。

jp.s6131.sampleApp.LoginActivity.java
public class LoginActivity extends Activity {
    private static String TAG = "sampleApp";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        setContentView(R.layout.login);
    }
    public void onClick(View v) {
        EditText useridText = (EditText) findViewById(id.userid_text);
        EditText passwordText = (EditText) findViewById(id.password_text);
        Uri.Builder builder = new Uri.Builder();
        AsyncLoginRequest task = new AsyncLoginRequest(
                this,
                useridText.getText().toString(),
                passwordText.getText().toString()
                );
        task.execute(builder);
    }
}

とりあえず無条件に HelloAndroidActivity に遷移

jp.s6131.sampleApp.AsyncLoginRequest.java
public class AsyncLoginRequest extends AsyncTask<Uri.Builder, Void, String> {
    private LoginActivity mainActivity;
    String userId;
    String password;
    public AsyncLoginRequest(LoginActivity mainActivity, String userId, String password) {
        this.mainActivity = mainActivity;
        this.userId = userId;
        this.password = password;
    }
    @Override
    protected String doInBackground(Uri.Builder... builder) {
        try {
            URI url = new URI( "http://10.0.2.2/sample/j_spring_security_check" );
            HttpPost request = new HttpPost( url );
            List<NameValuePair> post_params = new ArrayList<NameValuePair>();
            post_params.add(new BasicNameValuePair("j_username", userId));
            post_params.add(new BasicNameValuePair("j_password", password));
            request.setEntity(new UrlEncodedFormEntity(post_params, "UTF-8"));
            DefaultHttpClient httpClient = SampleHttpClient.getInstance();
            HttpResponse response = httpClient.execute(request);
            return null;
        }catch (Exception e){
            Log.d("Error", e.toString());
        }
        return null;
    }
    @Override
    protected void onPostExecute(String result) {
        Intent intent = new Intent(mainActivity.getApplicationContext(), HelloAndroidActivity.class);
        mainActivity.startActivity(intent);
    }
}

メイン画面

res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <EditText
        android:id="@+id/search_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text">
        <requestFocus />
    </EditText>
    <Button
        android:id="@+id/get_json_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="@string/json" />
    <GridView
        android:id="@+id/grid_table"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:numColumns="3" >
    </GridView>
</LinearLayout>

GridView の TextView を別ファイルで定義。

res/layout/rowdata.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
</TextView>

ログイン画面

res/layout/login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/userid"
    />
    <EditText
        android:id="@+id/userid_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        >
        <requestFocus />
    </EditText>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/password"
    />
    <EditText
        android:id="@+id/password_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
    />
    <Button
        android:id="@+id/login_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="@string/login"
    />
</LinearLayout>

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="jp.s6131.sampleApp" android:versionCode="1" android:versionName="0.0.1-SNAPSHOT">
  <uses-sdk android:minSdkVersion="11"
            android:targetSdkVersion="19" />
  <uses-permission android:name="android.permission.INTERNET" />
  <application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".HelloAndroidActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name=".LoginActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:noHistory="true">
    </activity>
  </application>
</manifest>


人気ブログランキングへ

2014年4月13日日曜日

Spring for Android Json 取得

Spring Json 返却 VB.NET 表示 で作成した
jp.s6131.sample.controller.HomeController.sampleList
を android アプリから呼び出す。

jp.s6131.sampleApp.Code.java
getter/setter 無いと HttpMessageNotReadableException 発生
public class Code {
    String id;
    String code;
    String name;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

jp.s6131.sampleApp.HelloAndroidActivity.java
public class HelloAndroidActivity extends Activity {
    private static String TAG = "sampleApp";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        Uri.Builder builder = new Uri.Builder();
        AsyncHttpRequest task = new AsyncHttpRequest(this);
        task.execute(builder);
        setContentView(R.layout.main);
    }
}

jp.s6131.sampleApp.AsyncHttpRequest.java
public class AsyncHttpRequest extends AsyncTask<Uri.Builder, Void, String> {
    private Activity mainActivity;
    public AsyncHttpRequest(Activity activity) {
        this.mainActivity = activity;
    }
    @Override
    protected String doInBackground(Uri.Builder... builder) {
        RestTemplate template = new RestTemplate();
        template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        String url = "http://10.0.2.2/sample/sampleList?cd=cd1";
        try {
            ResponseEntity<Code[]> responseEntity = template.exchange(url, HttpMethod.GET, null, Code[].class);
            Code[] resList = responseEntity.getBody();
            for (Code res : resList) {
                Log.i("sampleApp id = ", (res.id == null ? "null" : res.id));
                Log.i("sampleApp code = ", (res.code == null ? "null" : res.code));
                Log.i("sampleApp name = ", (res.name == null ? "null" : res.name));
            }
            return resList.toString();
        } catch (Exception e) {
            Log.d("Error", e.toString());
        }
        return "";
    }
    @Override
    protected void onPostExecute(String result) {
    }
}

AndroidManifest.xml 追加
<uses-permission android:name="android.permission.INTERNET" />

LogCat ビュー(ログ)の表示
「ウィンドウ」「ビューの表示」「その他」「Android」「LogCat」

仮想デバイスの作成
「ウィンドウ」「Android仮想デバイス・マネージャ」「新規」
装置:Nexus 5
ターゲット:Android 4.4.2 - API Level 19
CPU/ABI:ARM (armeabi-v7a)
メモリー・オプション:RAM:1024
「ホスト GPU を使用する」にチェック
OKで保存、開始ボタンで AVD 開始。
sample プロジェクトサーバー(Tomcat)も起動。

android アプリプロジェクト右クリック、「デバッグ」「Androidアプリケーション」
Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define Lorg/springframework/core/ErrorCoded
エラーが発生。
Java のビルド・パス、順序およびエクスポートでMaven依存関係からチェックを外すとエラーが消えるが、
実行時に spring for android クラスが not found となってしまう。
pom.xml で exclusion 設定を行う。さらに
java.lang.NoClassDefFoundError: com.fasterxml.jackson.databind.ObjectMapper
が発生するので fasterxml も追加

pom.xml
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-rest-template</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-auth</artifactId>
    <version>1.0.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-core</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-core</artifactId>
    <version>1.0.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-core</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.2.3</version>
</dependency>

LogCat ビュー で結果を確認。


人気ブログランキングへ

2014年4月10日木曜日

Eclipse Android プラグイン Spring for Android インストール

Eclipse Android プラグイン インストール


Eclipse「ヘルプ」「Eclipse マーケットプレース」
android で検索
Eclipse Android 開発ツール
をインストール。
インストール途中で SDK もインストールできる。
SDK マネージャで HAXM もインストールする。

Eclipse に SDK マネージャを表示させる


「ウィンドウ」「パースペクティブのカスタマイズ」「コマンド・グループ可用性」
「Android SDK および AVD マネージャー」にチェック。

Android Configurator for M2Eのインストール


Eclipse「ヘルプ」「Eclipse マーケットプレース」
m2e で検索
Android Configurator for M2E
をインストール。

Android Maven プロジェクトの作成


「ファイル」「新規プロジェクト」「MavenMaven プロジェクト」「アーキタイプの選択」
android で検索
「android-quickstart」
を選択。

project.properties の編集


target=android-16 を
target=android-19 に変更

Spring for Android インストール


pom.xml
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-rest-template</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-auth</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-core</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>


人気ブログランキングへ

2014年4月1日火曜日

VB.NET Spring Security ログイン


  • VB.NET でクッキーを保持
  • Spring ログインリクエストでJSONを返却
  • VB.NET 画面からログインPOST


SavedRequest が取得出来ない為、ログイン後リダイレクトが不可。
ログイン後レスポンスを変更する場合は SavedRequestAwareAuthenticationSuccessHandler で指定

Form1.vb
Public cookieData As CookieContainer = New CookieContainer
Private Sub search_proc(cd As String)
    Dim url As String = "http://localhost/sample/sampleList?cd=" & cd
    Dim req As HttpWebRequest = HttpWebRequest.Create(url)
    req.CookieContainer = cookieData
    Dim res As HttpWebResponse = req.GetResponse()
    Dim reader As StreamReader = New StreamReader(res.GetResponseStream())
    Dim jsonString As String = reader.ReadToEnd()
    Dim jsonList As List(Of JToken) = JArray.Parse(jsonString).Children.ToList
    If (jsonList.Count <> 0) Then
        Dim id As String = jsonList(0).Item("id")
        If (id = "-1") Then
            Form2.ShowDialog()
            Exit Sub
        End If
    End If
    For Each itemClass As JObject In jsonList
        itemClass.CreateReader()
        DataGridView1.Rows.Add(itemClass("id"), itemClass("code"), itemClass("name"))
    Next
    res.Close()
End Sub

Form2 に以下コントロールを配置
  • TextBox1 : ユーザ名
  • TextBox2 : パスワード
  • Button1 : ログインボタン

Form2.vb
Imports System.Net
Imports System.IO
Imports System.Text
Public Class Form2
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim ht As Hashtable = New Hashtable
        ht.Add("j_username", TextBox1.Text)
        ht.Add("j_password", TextBox2.Text)
        Dim param As String = ""
        For Each key As String In ht.Keys
            param = param & String.Format("&{0}={1}", key, ht(key))
        Next
        Dim paramBytes As Byte() = System.Text.Encoding.ASCII.GetBytes(param)
        Dim req As HttpWebRequest = HttpWebRequest.Create("http://localhost/sample/j_spring_security_check")
        Form1.cookieData = New CookieContainer
        req.CookieContainer = Form1.cookieData
        req.Method = "POST"
        req.ContentType = "application/x-www-form-urlencoded"
        req.ContentLength = paramBytes.Length
        Dim reqStream As Stream = req.GetRequestStream()
        reqStream.Write(paramBytes, 0, paramBytes.Length)
        reqStream.Close()
        Dim res As HttpWebResponse = req.GetResponse()
        For Each cookie In res.Cookies
            Console.WriteLine("cookie name = " & cookie.Name & ", value = " & cookie.Value)
            Form1.cookieData.Add(cookie)
        Next
        Dim resStream As Stream = res.GetResponseStream()
        Dim sr As New StreamReader(resStream, Encoding.UTF8)
        Console.WriteLine("test = " & sr.ReadToEnd())
        sr.Close()
        Me.Close()
    End Sub
End Class

jp/s6131/sample/controller/HomeController.java
@RequestMapping(value = "/login", method = RequestMethod.GET)
@ResponseBody
public List<Sample> login(HttpServletRequest req, Locale locale, Model model) {
    List<Sample> sampleList = new ArrayList<Sample>();
    Sample sample = new Sample();
    sample.setId(new Long(-1));
    sampleList.add(sample);
    return sampleList;
}


人気ブログランキングへ