Vueでのログインパスワード暗号化機能の実装

実際の運用シナリオでは、HTTPS経由のフォーム送信は既に暗号化されていますが、プロキシサーバー経由で平文が取得される可能性があるため、このような暗号化機能が必要となりました。技術的な詳細については、ご質問があればお気軽にお問い合わせください。技術面接の履歴書に記載するための実践的な内容として共有します。Vueに関する技術的な問題については、いつでもお手伝いします。

まず、login.vueファイルを作成します。

<template>
  <div class="auth-container">
    <el-card class="auth-card">
      <h2 class="auth-title">システムログイン</h2>
      <el-form ref="authForm" :model="authData" :rules="authRules" label-width="80px">
        <el-form-item label="ユーザー名" prop="username">
          <el-input v-model="authData.username" placeholder="ユーザー名を入力してください"></el-input>
        </el-form-item>
        <el-form-item label="パスワード" prop="password">
          <el-input 
            v-model="authData.password" 
            type="password" 
            placeholder="パスワードを入力してください"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button 
            type="primary" 
            class="auth-btn" 
            @click="processLogin"
            :loading="isLoading"
          >
            ログイン
          </el-button>
        </el-form-item>
      </el-form>
    </el-card>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { fetchPublicKey } from '@/services/auth';
import { authenticate } from '@/services/auth';
import { initializePublicKey, encryptCredentials, checkEncryptionStatus } from '@/utils/security';

// 認証フォームデータ
const authData = ref({
  username: '',
  password: ''
});

// フォームバリデーションルール
const authRules = ref({
  username: [
    { required: true, message: 'ユーザー名を入力してください', trigger: 'blur' }
  ],
  password: [
    { required: true, message: 'パスワードを入力してください', trigger: 'blur' }
  ]
});

// ローディング状態
const isLoading = ref(false);
const router = useRouter();

// ページ初期化時に公開鍵を取得
onMounted(async () => {
  try {
    const response = await fetchPublicKey();
    if (response.code === 200 && response.data) {
      // 公開鍵の初期化
      initializePublicKey(response.data);
    } else {
      ElMessage.error('暗号化公開鍵の取得に失敗しました。ページをリロードしてください');
    }
  } catch (error) {
    console.error('公開鍵の取得に失敗しました:', error);
    ElMessage.error('暗号化公開鍵の取得に失敗しました。後でもう一度お試しください');
  }
});

// ログイン処理
const processLogin = async () => {
  // フォームバリデーション
  const formRef = ref(null);
  if (!formRef.value) return;
  
  try {
    await formRef.value.validate();
    isLoading.value = true;
    
    // 暗号化機能の可用性チェック
    if (!checkEncryptionStatus()) {
      ElMessage.error('暗号化の初期化に失敗しました。ページをリロードしてください');
      isLoading.value = false;
      return;
    }
    
    // パスワードの暗号化
    const encryptedPassword = encryptCredentials(authData.value.password);
    
    // ログインAPIの呼び出し
    const response = await authenticate({
      username: authData.value.username,
      password: encryptedPassword
    });
    
    if (response.code === 200) {
      ElMessage.success('ログインに成功しました');
      // トークン情報の保存
      localStorage.setItem('authToken', response.data.token);
      // ホームページへリダイレクト
      router.push('/');
    } else {
      ElMessage.error(response.message || 'ログインに失敗しました');
    }
  } catch (error) {
    console.error('ログインエラー:', error);
    ElMessage.error(error.message || 'ログインに失敗しました。後でもう一度お試しください');
  } finally {
    isLoading.value = false;
  }
};
</script>

<style scoped>
.auth-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background-color: #f5f7fa;
}

.auth-card {
  width: 400px;
  padding: 20px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.auth-title {
  text-align: center;
  margin-bottom: 20px;
  color: #1f2329;
}

.auth-btn {
  width: 100%;
}
</style>

次に、暗号化処理をutilsディレクトリに暗号化用のJavaScriptファイル(security.js)を作成します。

// 暗号化ライブラリのインポート
import JSEncrypt from 'jsencrypt';

// 暗号化インスタンスの作成
const cryptoHandler = new JSEncrypt();

/**
 * RSA公開鍵の初期化
 * @param {string} publicKey - サーバーから返された公開鍵
 */
export const initializePublicKey = (publicKey) => {
  // 公開鍵の設定
  cryptoHandler.setPublicKey(publicKey);
};

/**
 * パスワードのRSA暗号化
 * @param {string} password - 暗号化が必要なパスワード
 * @returns {string} 暗号化されたパスワード
 */
export const encryptCredentials = (password) => {
  // 暗号化処理
  return cryptoHandler.encrypt(password) || '';
};

/**
 * 暗号化機能の可用性チェック
 * @returns {boolean} 利用可能かどうか
 */
export const checkEncryptionStatus = () => {
  return cryptoHandler.isReady();
};

最後に、APIディレクトリにインターフェース用のJavaScriptファイルを作成します。

import request from '@/utils/request';

/**
 * RSA公開鍵の取得
 */
export const fetchPublicKey = () => {
  return request({
    url: '/api/auth/public-key',
    method: 'get'
  });
};

/**
 * ユーザー認証
 * @param {Object} data - 認証情報
 * @param {string} data.username - ユーザー名
 * @param {string} data.password - 暗号化されたパスワード
 */
export const authenticate = (data) => {
  return request({
    url: '/api/auth/login',
    method: 'post',
    data
  });
};

タグ: Vue RSA Element Plus javascript Web Security

7月11日 18:37 投稿