SpringBootとVue.js、uni-appを活用した書籍管理ミニアプリの設計・実装

本システムは、現代的なフルスタック技術スタックを採用し、ユーザーがスマートフォン上で手軽に蔵書を管理・閲覧できるミニアプリケーションです。バックエンドにはSpringBoot、フロントエンドにはVue.jsとuni-appを組み合わせ、データ永続化にはMyBatis-Plusを使用しています。

アーキテクチャ概要

SpringBootは内蔵サーバーと自動設定機能により、最小限の設定で高効率なAPIサーバーを構築可能。依存関係に基づいて必要なBeanやコンポーネントを自動生成し、開発スピードを大幅に向上させます。また、Spring SecurityやSpring Data JPAとの統合も容易です。

Vue.jsはリアクティブなデータバインディングと仮想DOMにより、UIの更新を自動化。開発者はビジネスロジックに集中でき、コードの保守性と再利用性が向上します。uni-appとの連携により、1つのコードベースで複数プラットフォーム(微信小程序、H5、Appなど)への対応が可能です。

MyBatis-Plusは、CRUD操作の簡略化、動的SQL生成、ページネーション、楽観ロックなどの機能を提供。エンティティクラスとMapperインターフェースを自動生成するツールもあり、ボイラープレートコードを削減できます。

認証・セッション管理の実装

ログイン処理では、ユーザー名とパスワードを検証後、一時トークンを発行。トークンは1時間有効とし、リクエストヘッダー経由で送信されます。認証インターセプターが各リクエストを監視し、無効なトークンの場合は401エラーを返却します。

@RestController
public class AuthController {

    @Autowired
    private UserService userService;
    @Autowired
    private TokenManager tokenManager;

    @PostMapping("/auth/login")
    public ResponseEntity<ApiResponse> authenticate(@RequestBody LoginRequest req) {
        User user = userService.findByUsername(req.getUsername());
        if (user == null || !user.getPassword().equals(req.getPassword())) {
            return ResponseEntity.status(401).body(ApiResponse.error("認証失敗"));
        }

        String sessionToken = tokenManager.createSession(user.getId(), user.getUsername(), user.getRole());
        return ResponseEntity.ok(ApiResponse.success().add("token", sessionToken));
    }
}

@Component
public class AuthInterceptor implements HandlerInterceptor {

    private static final String TOKEN_HEADER = "X-Auth-Token";

    @Autowired
    private TokenManager tokenManager;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (isPreflight(request)) {
            handlePreflight(response);
            return false;
        }

        if (handler instanceof HandlerMethod && ((HandlerMethod) handler).hasMethodAnnotation(IgnoreAuth.class)) {
            return true;
        }

        String token = request.getHeader(TOKEN_HEADER);
        if (token == null || !tokenManager.isValid(token)) {
            writeUnauthorized(response);
            return false;
        }

        SessionInfo session = tokenManager.getSession(token);
        request.setAttribute("userId", session.getUserId());
        request.setAttribute("userRole", session.getRole());

        return true;
    }

    private void writeUnauthorized(HttpServletResponse response) throws IOException {
        response.setStatus(401);
        response.setContentType("application/json;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            out.print("{\"code\":401,\"msg\":\"未認証\"}");
        }
    }
}

データベース設計

セッション管理用のauth_sessionテーブルは、ユーザーID、トークン文字列、有効期限などを保持。トークンはUUID形式で生成され、セキュリティを確保します。

CREATE TABLE `auth_session` (
  `session_id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'セッションID',
  `user_id` BIGINT NOT NULL COMMENT 'ユーザーID',
  `username` VARCHAR(64) NOT NULL,
  `user_role` VARCHAR(32) NOT NULL COMMENT 'ロール',
  `access_token` VARCHAR(128) UNIQUE NOT NULL COMMENT 'アクセストークン',
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  `expires_at` TIMESTAMP NOT NULL COMMENT '有効期限'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- サンプルデータ
INSERT INTO `auth_session` VALUES 
(101, 201, 'alice_reader', 'reader', 'tok_7x9a2mzq8nvp3kls0r4c6yb1e5wd9f', '2025-04-01 10:00:00', '2025-04-01 11:00:00'),
(102, 301, 'bob_admin', 'admin', 'tok_k9p2nvm7x4zc1rq8s5t3y6w0e2fa7b', '2025-04-01 10:05:00', '2025-04-01 11:05:00');

テスト戦略

主にブラックボックステストを実施。代表的なテストケースとして:

  • ログイン機能:正しい資格情報でログイン成功、誤ったパスワードでエラーメッセージ表示、空のユーザー名でバリデーションエラー
  • ユーザー管理:重複ユーザー登録時のエラー、必須項目未入力時の警告、削除確認ダイアログの表示

すべてのテストケースは、実際のユーザー体験を模倣し、UX観点からも検証を行っています。

タグ: SpringBoot vue.js uni-app MyBatis-Plus 微信小程序

6月20日 17:55 投稿