まえがき
本文
この記事の対象者
ここも第1回とほぼ同じですね。
要するに、完全未経験を除いた初心者です。
前提
以下インストール済(インストール方法は省略)
・React 19.0.0
・Next.js 16.0.0
・App Router
・Java 21
・Spring Boot 4.1
・jjwt-api 0.12.5
1.サイドバー
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useAuth } from '../context/AuthContext'
function Sidebar() {
const router = useRouter()
const { logout } = useAuth()
const handleLogout = async () => {
if (confirm("ログアウトしますか?")) {
await logout()
router.push("/")
}
}
return (
<nav>
<ul>
<li>
<Link href="/home">
ホーム
</Link>
</li>
(上記と同じものがいくつか続く)
</ul>
<button
onClick={handleLogout}
>
ログアウト
</button>
</nav>
)
}
export default Sidebar
2.ログイン後画面共通のlayout.tsx
import Sidebar from "../../components/Sidebar";
return (
<div>
<div>
<Sidebar />
</div>
<div>
{children}
</div>
</div>
);
3.useAuthの共通部品
以下のように追記します。
type AuthContextType = {
isLoggedIn: boolean;
isLoading: boolean;
login: () => Promise<void>;
logout: () => void;
checkAuth: () => Promise<void>;
};
// ログアウト時は未ログイン状態に戻す
const logout = async () => {
try {
await fetch("/api/auth/logout", {
method: "POST",
credentials: "include",
});
} finally {
setIsLoggedIn(false);
}
};
4.API部品
route.ts
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const springResponse = await fetch(
`${process.env.SPRING_BOOT_API_BASE_URL}/api/auth/logout`,
{
method: "POST",
headers: {
Cookie: request.headers.get("cookie") ?? "",
},
}
);
const response = new NextResponse(null, {
status: springResponse.status,
});
springResponse.headers.getSetCookie().forEach((cookie) => {
response.headers.append("set-cookie", cookie);
});
return response;
}
5.Controller
以下のように追記。
@RestController
@RequestMapping("/api/auth")
public class AuthController {
// ログアウトしてJWT Cookieを削除する
@PostMapping ("/logout")
public ResponseEntity<?> logout(Authentication authentication) {
// accessTokenを削除するCookieを返す ※LoginControllerとほぼ同じ構成
ResponseCookie cookie = ResponseCookie.from("accessToken", "")
.httpOnly(true)
.secure(false)
.sameSite("Lax")
.path("/")
.maxAge(Duration.ZERO)
.build();
// 削除用のCookieをブラウザに返す
return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.toString())
.build();
}
}
あとがき
上記によりログアウトボタンを実装したサイドバーが作れました。
これでCookieを用いた認証情報も削除しているので、ログアウトした後でリンク直打ちしても入れないです。
サイドバーを実装する機会がなくともログアウトボタンとリンクオブジェクトを実装する必要はあるでしょうし、オブジェクトの種類が違っていても本体はOnClick属性による関数の呼び出しなので流用可能。