AWS ALBで特定のパスもしくはパスが存在しない場合のみリクエストを許可したい場合

https://example.com/app もしくは https://example.com ではアクセスを許可したいが、それ以外についてはアクセスを許可したくない(404を返したい)場合は以下のように設定する。

  1. パスが/app または /app/* または / の場合は許可する。
  2. パスが /* の場合は固定レスポンス(404)を返す。
  3. それ以外の場合(デフォルト)は許可する。

Javaのメソッド呼び出し階層を表示する

Threadオブジェクトからスタックトレースを取得できるのでそれを使用する。

System.out.println(java.util.Arrays.stream(Thread.currentThread().getStackTrace())
    .skip(1).limit(4).map(t -> t.getClassName() + "." + t.getMethodName()).collect(java.util.stream.Collectors.joining(",")));

skip(1)をかましているのは結果にgetStackTraceメソッド呼び出しも含まれているため。

JavaからActive Directory(LDAPS)接続(自己署名証明書の検証スルーコード付き)

自分向けの備忘録として。

基本はこちら。

kazuhira-r.hatenablog.com

こちらの記事だと自己証明書のスルーに使用しているのが X509TrustManager だが、新しいTLSに対応するためには代わりに X509ExtendedTrustManager を実装してあげる必要がある。

よって、以下のような感じになる。

接続側

        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, LDAP_URL);
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, AD_USER_NAME);
        env.put(Context.SECURITY_CREDENTIALS, AD_PASSWORD);
        env.put("java.naming.security.protocol", "ssl");
        env.put("java.naming.ldap.factory.socket", LooseSSLSocketFactory.class.getName());

        DirContext ctx = null;
        try {
            List<Map<String, String>> ret = new ArrayList<>();
            ctx = new InitialDirContext(env);

            for (NamingEnumeration<SearchResult> search = ctx.search("OU=XX,dc=XXXX,dc=local", null); search.hasMoreElements();) {
                SearchResult next = search.next();
                Attributes attributes = next.getAttributes();
                //実際の処理
            }
            return ret;
        } catch (AuthenticationException ae) {
            //認証に失敗した
            ae.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ctx != null) {
                try {
                    ctx.close();
                } catch (NamingException ex) {
                }
            }
        }

LooseSSLSocketFactory

package dev.megascus.ssl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;

public class LooseSSLSocketFactory extends SSLSocketFactory {

    SSLSocketFactory delegate;

    public LooseSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new X509TrustManager[] { new X509ExtendedTrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                @Override
                public void checkClientTrusted(X509Certificate[] xcs, String string, Socket socket)
                        throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] xcs, String string, Socket socket)
                        throws CertificateException {
                }

                @Override
                public void checkClientTrusted(X509Certificate[] xcs, String string, SSLEngine ssle)
                        throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] xcs, String string, SSLEngine ssle)
                        throws CertificateException {
                }
            } }, new SecureRandom());

            delegate = sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
        return delegate.createSocket(socket, s, i, b);
    }

    @Override
    public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
        return delegate.createSocket(s, i);
    }

    @Override
    public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
        return delegate.createSocket(s, i, inetAddress, i1);
    }

    @Override
    public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
        return delegate.createSocket(inetAddress, i);
    }

    @Override
    public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
        return delegate.createSocket(inetAddress, i, inetAddress1, i1);
    }

    public static SocketFactory getDefault() {
        return new LooseSSLSocketFactory();
    }
}

なお、Active Directoryは普通にやると1000件しか結果が返ってこない。 1000件以上登録されている可能性がある場合は以下の記事を参考にする。

qiita.com

JavaMailでメールアドレスに名前を付ける

JavaMailで送信したメールに宛先や送信元に名前を表示したい場合がある。

以下のような感じですね。

ゆとり<megascus@megascus.dev>

InternetAddressクラスの3つの引数があるバージョンを使えば対応できる。

docs.oracle.com

日本語を扱う場合、charsetとして"iso-2022-jp"を使用しないと文字化けるので注意。

ネットワークドライブの割り当てを行ったNASに保管されているExcelファイルの元の場所のURLを取得する

\XXXnas\share\hoge.xlsx みたいなでアクセスするのを嫌がって、 \XXXnas\share\ をZドライブに割り当てみたいなことをした場合に、他の人に共有する場合はZドライブではなく元のネットワークドライブ上のパスで指定したい場合があります。

その場合、リボンのユーザー設定からリボンにないコマンド→ドキュメントの場所を選択、追加することで、ネットワークドライブ上のパスをリボン上に表示することができるようになります。

f:id:megascus:20200313175911p:plain
リボンのユーザー設定

aws-sdk-java-v2を使用してs3からオブジェクトを取得する。

aws-sdk-javaのバージョン2が出ていたので試してみました。 v1と比べて、パッケージ名が変更されていたり、APIがビルダー形式(いわゆる流れるようなインターフェース)になっていたりと微妙に差はありますが、大きく差があるわけではないというのが印象です。 東京リージョンのs3のエンドポイントがきちんととれないというのも従来通り。なんでやねん。

pomには以下のような感じで記載します。group idが変わってます。

    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>ec2</artifactId>
            <version>2.10.82</version>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>2.10.82</version>
        </dependency>
        <!-- proxy設定を行う場合は以下を追加する必要がある。 -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>apache-client</artifactId>
            <version>2.10.82</version>
            <type>jar</type>
        </dependency>
    </dependencies>
package dev.megascus.s3access;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.http.apache.ProxyConfiguration;

public class Main1 {

    //おまじない()
    private static final String ENDPOINT_URL = "https://s3-ap-northeast-1.amazonaws.com";

    public static void main(String[] args) throws IOException, URISyntaxException {
        String accessKey = "your_access_key";
        String secret = "your_secret_key";

        //通常は認証情報をプログラム内では設定しない。環境変数等から指定する。
        AwsCredentials credentials = AwsBasicCredentials.create(accessKey, secret);
        
        //東京
        Region region = Region.AP_NORTHEAST_1;

        //Proxy設定が必要な場合
        ProxyConfiguration proxy = ProxyConfiguration.builder().endpoint(new URI("http://proxy_server_url")).build();
        SdkHttpClient client = ApacheHttpClient.builder().proxyConfiguration(proxy).build();

        S3Client s3 = S3Client.builder()
                .region(region)
                .endpointOverride(new URI(ENDPOINT_URL))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .httpClient(client) //Proxy設定が必要な場合
                .build();

        String bucketName = "your-bucket-name";
        String fileObjKeyName = "ファイル名";
        s3.getObject(GetObjectRequest.builder().bucket(bucketName).key(fileObjKeyName).build(),
                ResponseTransformer.toFile(Paths.get("c:\\temp", fileObjKeyName)));
    }
}

なんで作り直したのに、わざわざJavaの慣習に従わないパッケージ名に変更したのだろうか・・・・・・

なお、公式ドキュメントでも差分は公開されています。

https://docs.aws.amazon.com/ja_jp/sdk-for-java/v2/migration-guide/whats-different.htmldocs.aws.amazon.com

github

github.com

maven

mvnrepository.com