L08084のブログ

技術記事の執筆は、祈りに近い

アカウント登録メールの実装をしています

f:id:l08084:20181110163634p:plain
アカウント登録メールの例

はじめに

Webサービスで会員登録の情報を入力した後に、メールアドレス確認のメールが飛んでくるサービスよくありますよね。
あれの実装をいま仕事でしています。よくある機能だと思うので、実装方針を備忘録として記録しました。

作成しているシステムについて

アカウント登録機能のあるWebアプリケーションで、採用している言語・ミドルウェアは下記の一覧を参照

  • Java
  • Angular
  • Ionic
  • PostgreSQL
  • AWS

メールの具体的な仕様

今回実装しているアカウント登録メールの大まかな仕様

  1. ユーザーが仮登録(メールアドレスなどを入力)を完了する
  2. 本登録画面へのURLリンクを含んだメールをユーザーに送信する
  3. URLリンクをクリックしたユーザーを認証して問題なければ、本登録画面に遷移させる

この記事では、URLリンクをクリックしたアカウントの認証(というか特定)方法に絞って解説します。
HTMLメールのレイアウトなどについては、こちらの記事をご参照ください。

アカウント登録のためのURLリンクを作成する

アカウント登録メールでは、アカウント本登録画面に遷移するURLだけではく、クリックしたユーザーを特定する情報が必要になります。

本システムでは、メールを送信する前の画面で入力させる情報がメールアドレスしかなかったため、URLのパラメーターにメールアドレスを含めることでアカウントを特定することにしました。

メールに載せるURLのイメージとしてはこんな感じです。
https://[システムのドメイン]/[アカウント本登録画面のパス]/[暗号化したメールアドレス]

メールアドレス暗号化の手順

URLに生のメールアドレスをそのまま載せるのはセキュリティ的にアレだよね...ってなったので暗号化することにしました。

  1. サーバサイド(Java)でAWS Key Management Service (KMS)を利用してメールアドレスを暗号化する
  2. 暗号化したテキストをBase64エンコードする
  3. 2.のテキストをURLエンコードする

3.URLエンコードを最初忘れていたせいで、暗号化したテキストにURLに使用できない文字が混じって認証に失敗したりしました。

ユーザーがURLリンクをクリックした時の動作としては、URLから本登録画面に遷移した後、WebAPIを呼び出して暗号化したテキストをデコードして元のメールアドレスに戻し、DBを参照してアカウントを特定するという流れにしています。

言いたいこと

完全に霊感で作ったので、その実装ヤバくね?みたいなやつがあったらコソッと教えてください。まだ間に合います

【Vue.js】Nuxt.jsのひな形を作成する

Nuxt.jsに入門したいので、ひな形の作成からはじめました。

バージョン情報

  • npm 5.6.0
  • yarn 1.5.1

手順

ターミナルを開いて、下記のコマンド実行する。

$ npm i -g @vue/cli @vue/cli-init

Vue CLIが正しくインストールされていることを確認する。

$ vue -V
3.0.3

vue initコマンドでVue.jsのひな形を作成する。
vue initコマンド後に色々と聞かれるが、特にこだわりがなければ全部EnterでOK。

$ vue init nuxt-community/starter-template <project-name>

上記のコマンドでは、nuxt-community/starter-templateというスターターテンプレートを使用して、Vue.jsのプロジェクトを作成している。

$ cd <project-name>
$ yarn # npm iでも可、パッケージのインストールを行なっている
$ yarn dev # npm run dev でも可

上記のコマンド実行後に、ブラウザでhttp://localhost:3000を開くと下記のNuxt.jsのデフォルト画面が表示される。

f:id:l08084:20181021171754p:plain
Nuxt.jsのデフォルトの画面

参考サイト

インストール - Nuxt.js

【JavaScript】return なしでもPromiseはメソッドチェーンできる

はじめに

then関数に新しいPromiseオブジェクトを返す機能があるので、then内でPromiseをreturnする処理を書かなくても、Promiseはメソッドチェーンが可能だということを最近知ったのでメモ。

Promiseチェーンの色々な例

はじめにで述べたことを理解するために、色々なタイプのPromiseをメソッドチェーンで繋ぎます

returnなし

then()内でreturnしない場合でも、thenが新しいPromiseを返してくれるのでメソッドチェーンが可能です

// new Promise(resolve => resolve()) と同じ
const promise = Promise.resolve();

// 実行結果
//    A
//    B
//    C
promise.then(console.log('A'))
       .then(console.log('B'))
       .then(console.log('C'));

returnあり

チェーンの次の処理に前の処理の結果を渡したい場合は、then()内に渡したい値をreturnする処理を書く必要があります。
returnした値はthenの機能でPromiseオブジェクトに変換されるので、数値や文字列だけでなく、オブジェクトでもPromiseでもどの値を返しても、Promiseはメソッドチェーンが可能です

数値を伝搬するPromiseチェーン

数値をreturnで次の処理に渡しています。returnした数値はthenの機能でPromiseオブジェクトに変換して渡されます

// new Promise(resolve => resolve()) と同じ
const promise = Promise.resolve();

// 実行結果
// 6
// arrow functionを使っているのでreturnは省略されているが、
// 実際には数値がreturnされている
promise.then(() => 1)
       .then(value => value + 2)
       .then(value => value + 3)
       .then(value => console.log(value));
Promiseを伝搬するPromiseチェーン

PromiseをPromiseチェーンを使って次の処理に渡すこともできます。
次の例では、戻り値がPromiseのサードパーティ製ライブラリのメソッドをreturnしています。

// 戻り値がPromiseのメソッド
this.keychainTouchId.isAvailable()
    .then((res: any) => {
        // 戻り値がPromiseのメソッド
        return this.keychainTouchId.has(BioAuthService.KEY_A);
    }).then((res: any) => {
        // 戻り値がPromiseのメソッド
        return this.keychainTouchId
            .verify(BioAuthService.KEYCHAIN_KEY, `ロックを解除してください`);
    }).then((res: any) => {
        this.password = res;
        // 戻り値がPromiseのメソッド
        return this.storage.get(BioAuthService.KEY_B);
    }).then((res: any) => {
        this.userId = res;
        const params = {
            loginId: this.userId,
            password: this.password,
            deviceToken: null
        };
        this.action.login(params);
    }).catch((error: any) => {
        // catchは一つでよい
        console.error(error);
    });

結論

何も考えずにthenでつないどけば、Promiseはメソッドチェーンが可能

参考サイト

JavaScript Promiseの本

Promiseを使う - JavaScript | MDN

HTMLメールのCSSなどレイアウト設定について

はじめに

最近仕事でHTMLメールのレイアウトを設定する機会があったので、HTMLメールを作成するときの注意点や参考サイトなどをまとめました

ベースになるテンプレート

HTMLメールを作るのが初めての人は、そもそもHTMLメールのHTMLってどう書けばいいの?ってなると思うんですが、それについてはGmailの公式サイトの方で説明してくれています。

CSS Support  |  Gmail & Inbox Sender Resources  |  Google Developers

  • Gmail公式が紹介しているHTMLメールのテンプレート
<html>
  <head>
    <style>
      .colored {
        color: blue;
      }
      #body {
        font-size: 14px;
      }
    </style>
  </head>
  <body>
    <div id='body'>
      <p>Hi Pierce,</p>
      <p class='colored'>This text is blue.</p>
      <p>Jerry</p>
    </div>
  </body>
</html>

Gmailについては上記のテンプレートでいいですが、メールクライアントによっては、インラインCSSでないとダメなクライアントもあると思います(調べてませんが)。
そのようなメールクライアントへの対応が必要な場合は、下記のようなCSSをインラインCSSに変換するサービスを使うと便利です。

CSSをインラインCSSに変換してくれるWebサービス「Inline styler」

メールクライアントごとの違い

メールクライアント(Gmail, Outlook, Yahoo!メール...)ごとに、使用できるCSSやHTMLタグが大きく異なるため特に注意が必要です。

HTMLメールのレイアウトを検証できるサービス

メールアドレスとHTMLメールのテンプレートを入力するとメールを送信してくれるサービスもあります。メールを送信する環境を構築しなくてもレイアウトの確認ができるので便利

New Email Test — Litmus PutsMail

【HTML】押すと電話がかかるボタンを作る

はじめに

押すと、電話がかかるボタンを作りたい

TELリンクを使う

a要素href="tel:[電話番号]"を設定すると、電話発信用のリンクを作ることができる。

<a href="tel:090XXXXXXXX">電話をかける(090XXXXXXXX)</a>

f:id:l08084:20180901153652p:plain
実際の表示

TELリンクをボタン風に表示する

TELリンクを設定したa要素(アンカータグ)でボタンを囲むことによって、クリックすると、電話を発信するボタンが完成する

<a href="tel:090XXXXXXXX">
  <button ion-button>電話をかける(090XXXXXXXX)</button>
</a>

f:id:l08084:20180901154743p:plain
実際の表示

PCでこのボタンを押すと、FaceTimeだったりSkypeだったりを呼び出してくれる

f:id:l08084:20180901154901p:plain

参考サイト

HTML5/テキスト/a要素 電話発信用のリンクを設定する - TAG index

【Java8】年月日の計算が簡単にできる Date and Time APIについて

新卒ぶりくらいにJavaを触ったら、日時の計算が楽にできるようになってて驚いた

この記事について

Java8で導入された Date and Time API による、日時の計算についてのメモ

Date and Time API

Date and Time APIでは、Java8以前の日付クラス(java.util.Date, java.util.Calender)と比べて、下記の特徴・メリットがあります。

  • 日付・時間・日時をそれぞれ別クラスで扱っている(不要な情報を持たなくてよい)
  • 年月日の計算が楽

日付・時間・日時をそれぞれ別クラスで扱う

Date and Time APIでは、日付・時間・日時を扱うクラスが次のように3つに別れています。

  • 日付
    • java.time.LocalDate クラス
  • 時間
    • java.time.LocalTime クラス
  • 日時
    • java.time.LocalDateTime クラス

コード

実際にDate and Time APIを使って、日時処理をしていく

現在の日付、時間、日時の取得

// 日付
LocalDate date = LocalDate.now();
// 出力: 2018-08-26
System.out.println(date);

// 時間
LocalTime time = LocalTime.now();
// 出力: 14:06:44.359369
System.out.println(time);

// 日時
LocalDateTime dateTime = LocalDateTime.now();
// 出力: 2018-08-26T14:06:44.359400
System.out.println(dateTime);

実行結果

2018-08-26
14:06:44.359369
2018-08-26T14:06:44.359400

StringからLocalDateに変換

文字列をLocalDate型に変換する

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class TestDate {
    public static void main(String... args) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");

        String text = "2017/03/08";

        // StringからLocalDateに変換
        LocalDate localDate = LocalDate.parse(text, formatter);

        // 出力: 2017-03-08
        System.out.println(localDate);
    }
}

実行結果

2017-03-08

LocalDateからStringに変換

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class TestDate {
    public static void main(String... args) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");

        // 現在の日付を取得
        LocalDate localDate = LocalDate.now();

        // StringからLocalDateに変換
        String formattedString = localDate.format(formatter);
        // 出力: 2018年08月26日
        System.out.println(formattedString);
    }
}

実行結果

2018年08月26日

生年月日から年齢を計算する

生年月日から、その人の年齢を計算する

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class TestDate {
    public static void main(String... args) {

        // 生年月日
        String birthdate = "1989/10/16";

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");

        // 生年月日を表す文字列から、LocalDateを生成
        LocalDate localBirdhdate = LocalDate.parse(birthdate, formatter);

        // 現在の日付を取得
        LocalDate nowDate = LocalDate.now();

        // 現在と生年月日の差分を年単位で算出することによって、年齢を計算する
        long age = ChronoUnit.YEARS.between(localBirdhdate, nowDate);

        // 年齢: 28
        System.out.println("年齢: " + age);
    }
}

実行結果

年齢: 28

年月日の計算

日単位の加算を行ったり、差分を日・時間・分 単位で出したりなど

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class TestDate {
    public static void main(String... args) {

        // 現在の日時を取得
        LocalDateTime nowLocalDate = LocalDateTime.now();

        // 現在の日時の1日後を取得
        LocalDateTime nextLocalDate = nowLocalDate.plusDays(1);

        // 今日と明日の差分を日単位で取得
        long days = ChronoUnit.DAYS.between(nowLocalDate, nextLocalDate);

        // 今日と明日の差分を時間単位で取得
        long hours = ChronoUnit.HOURS.between(nowLocalDate, nextLocalDate);

        // 今日と明日の差分を分単位で取得
        long minutes = ChronoUnit.MINUTES.between(nowLocalDate, nextLocalDate);
        
        System.out.println("現在の日付: " + nowLocalDate);
        System.out.println("明日の日付: " + nextLocalDate);
        System.out.println("差分(日): " + days);
        System.out.println("差分(時間): " + hours);
        System.out.println("差分(分): " + minutes);

    }
}

実行結果

現在の日付: 2018-08-26T17:37:26.337370
明日の日付: 2018-08-27T17:37:26.337370
差分(日): 1
差分(時間): 24
差分(分): 1440

参考サイト

Javaで2つの期間より差を検出する。 - m_shige1979のささやかな抵抗と欲望の日々

【CSS】PCでもモバイルと同じレイアウトの画面で表示する(スマホのフレーム画像に画面をはめる)

今回やること

PCからみると、スマホの枠の中にコンテンツが表示されるようなデザインの画面を作る。参考サイト(フィッシャーマン・コール)

f:id:l08084:20180819164939p:plain
PCだとこーいう表示になる

f:id:l08084:20180819165154p:plain
モバイルからだとこういう表示になる

メリット

PC用の画面デザインを用意せずに、スマホ用のレイアウト一本でいける

大まかな流れ

  1. 表示している端末がPCかモバイルかを判定する
  2. (PCの場合)スマホのフレーム画像を表示する
  3. (PCの場合)画面をスマホの枠に当てはまるように、指定の高さ、幅で表示する
  4. (モバイルの場合)スマホのフレーム画像を表示しない
  5. (モバイルの場合)画面をスクリーンいっぱいに表示する

コード

HTMLとCSSを書いていく

メディアクエリを使用する

メディアクエリを使って、画面の幅でPCかモバイルかを判定する

  @media screen and (max-width: 760px) {
     /* 760pxの幅まで適用される(モバイルの時のレイアウト) */
    .frame {
      /* モバイルの時は、スマートフォンのフレーム画像を表示しない */  
      display: none;
    }
  }

  @media screen and (min-width: 760px) {
     /* 幅が760pxより大きい時に適用される(PC用のレイアウト) */
    .frame {
      /* スマホのフレーム画像を上下中央に表示 */
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      margin: auto;
      z-index: 5;
      width: 450px;
      height: 787px;
      .frame-img {
        position: relative;
        margin-top: 20px;
        margin-left: -1.3px;
      }
      /* コンテンツをスマホのフレーム内に収まるように上下中央に表示 */
      .main {
        width: 376px;
        height: 603px;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        margin: auto;
        z-index: 10;
      }
    }

HTMLはだいたいこんな感じで

<div class="frame">
  <!-- スマホのフレーム画像 -->
  <img alt="" src="./assets/imgs/phone-frame.png" class="frame-img">
</div>
<!-- 表示するコンテンツ -->
<ion-nav class="main" [root]="rootPage"></ion-nav>