
JSP MVCモデルを使った模擬アプリ課題
以下に、Servlet、JSP、JDBC、フロントコントローラーパターンを活用した3つの課題を提案します。 課題1: 簡易タスク管理アプリケーション 要件 模範 […]
HTTPセッションは、複数のリクエストにわたってユーザーの状態を保持する仕組みです。サーバー側でセッションIDを発行し、クライアント(通常はCookieを通じて)と共有することで、同一ユーザーの複数リクエストを関連付けます。
HttpSession session = request.getSession(); // 存在しなければ新規作成
HttpSession session = request.getSession(false); // 存在しない場合null
// 保存
session.setAttribute("key", object);
// 取得
Object value = session.getAttribute("key");
// 削除
session.removeAttribute("key");
Cookieは、サーバーがクライアントに送信する小さなデータで、クライアント側に保存され、以降のリクエストでサーバーに送信されます。セッション管理やパーソナライズなどに使用されます。
Cookie cookie = new Cookie("name", "value");
cookie.setMaxAge(60 * 60 * 24); // 1日
response.addCookie(cookie);
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("name")) {
String value = cookie.getValue();
// 処理...
}
}
}
Filterは、リクエストがServletに到達する前やレスポンスがクライアントに返される前に処理を追加するコンポーネントです。認証、ロギング、エンコーディング設定、キャッシュ制御などに使用されます。
1. ServletContextListener - アプリケーションの開始/終了を監視
2. HttpSessionListener - セッションの作成/破棄を監視
3. ServletRequestListener - リクエストの開始/終了を監視
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
// 方法1: web.xmlで設定
30
// 方法2: プログラムで設定
session.setMaxInactiveInterval(30 * 60); // 秒単位
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
Cookie cookie = new Cookie("name", "value");
cookie.setMaxAge(60 * 60 * 24 * 7); // 1週間(秒単位)
response.addCookie(cookie);
Cookie cookie = new Cookie("name", "value");
cookie.setSecure(true); // HTTPSのみで送信
cookie.setHttpOnly(true); // JavaScriptからアクセス不可
response.addCookie(cookie);
Filterチェーンは、複数のFilterが連鎖的に実行される仕組みです。各Filterはchain.doFilter()を呼び出すことで次のFilterやServletに処理を渡します。web.xmlでの定義順や@WebFilterのorder属性で実行順が決まります。
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest wrappedRequest = new CustomRequestWrapper((HttpServletRequest)request);
HttpServletResponse wrappedResponse = new CustomResponseWrapper((HttpServletResponse)response);
chain.doFilter(wrappedRequest, wrappedResponse);
}
}
@WebFilter("/secure/*")
public class AuthFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute("user") == null) {
((HttpServletResponse)response).sendRedirect(httpRequest.getContextPath() + "/login");
return;
}
chain.doFilter(request, response);
}
}
@WebListener
public class AppInitializer implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
// アプリケーション起動時の初期化処理
ServletContext context = sce.getServletContext();
context.setAttribute("startTime", System.currentTimeMillis());
}
public void contextDestroyed(ServletContextEvent sce) {
// アプリケーション終了時のクリーンアップ処理
}
}
@WebListener
public class SessionTracker implements HttpSessionListener {
private AtomicInteger sessionCount = new AtomicInteger();
public void sessionCreated(HttpSessionEvent se) {
sessionCount.incrementAndGet();
System.out.println("Session created. Total: " + sessionCount);
}
public void sessionDestroyed(HttpSessionEvent se) {
sessionCount.decrementAndGet();
System.out.println("Session destroyed. Total: " + sessionCount);
}
}
@WebFilter("/*")
public class EncodingFilter implements Filter {
private String encoding = "UTF-8";
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
}
@WebFilter("/*")
public class CacheControlFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse)response;
httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
httpResponse.setHeader("Pragma", "no-cache");
httpResponse.setHeader("Expires", "0");
chain.doFilter(request, response);
}
}
HttpSession oldSession = request.getSession();
// セッション属性を保持
Map attributes = new HashMap<>();
Collections.list(oldSession.getAttributeNames())
.forEach(name -> attributes.put(name, oldSession.getAttribute(name)));
// 古いセッションを無効化
oldSession.invalidate();
// 新しいセッションを作成
HttpSession newSession = request.getSession(true);
attributes.forEach(newSession::setAttribute);
@WebServlet("/visit")
public class VisitServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
String lastVisit = "This is your first visit";
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("lastVisit")) {
lastVisit = "Last visit: " + cookie.getValue();
break;
}
}
}
String currentTime = Instant.now().toString();
Cookie visitCookie = new Cookie("lastVisit", currentTime);
visitCookie.setMaxAge(60 * 60 * 24 * 365); // 1年間有効
response.addCookie(visitCookie);
response.setContentType("text/html");
response.getWriter().println("" + lastVisit + "
");
}
}
// アプリケーション全体でデータを保存
getServletContext().setAttribute("globalData", data);
// データを取得
Object data = getServletContext().getAttribute("globalData");
// データを削除
getServletContext().removeAttribute("globalData");
@WebFilter("/admin/*")
public class AdminFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
String ip = httpRequest.getRemoteAddr();
if (!ip.startsWith("192.168.")) {
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied");
return;
}
chain.doFilter(request, response);
}
}
分散環境では、セッション情報を複数のサーバー間で共有する必要があります。主な方法:
// 設定例 (Tomcatのcontext.xml)
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
データベースをセッションストアとして使用する方法:
Tomcat設定 (context.xml):
セッションテーブル作成:
CREATE TABLE sessions (
session_id VARCHAR(100) PRIMARY KEY,
session_data BLOB,
creation_time BIGINT,
last_accessed_time BIGINT,
max_inactive_interval INT
);
//カスタムSessionManager実装:
public class DatabaseSessionManager extends ManagerBase {
public Session createSession(String sessionId) {
// DBに新規セッション作成
}
public Session findSession(String id) {
// DBからセッション取得
}
public void remove(Session session) {
// DBからセッション削除
}
}
@WebFilter(asyncSupported = true, value = "/*")
public class AsyncFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request.isAsyncSupported() && request.isAsyncStarted()) {
// 非同期リクエスト処理
AsyncContext context = request.getAsyncContext();
context.addListener(new AsyncListener() {
public void onComplete(AsyncEvent event) {
// 非同期処理完了時
}
// 他のメソッド実装...
});
}
chain.doFilter(request, response);
}
}
@WebListener
public class AppMonitoringListener implements ServletContextListener,
HttpSessionListener,
ServletRequestListener {
// アプリケーションライフサイクル
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().log("Application started");
}
public void contextDestroyed(ServletContextEvent sce) {
sce.getServletContext().log("Application stopped");
}
// セッションライフサイクル
public void sessionCreated(HttpSessionEvent se) {
se.getSession().getServletContext().log("Session created: " + se.getSession().getId());
}
public void sessionDestroyed(HttpSessionEvent se) {
se.getSession().getServletContext().log("Session destroyed: " + se.getSession().getId());
}
// リクエストライフサイクル
public void requestInitialized(ServletRequestEvent sre) {
sre.getServletContext().log("Request started: " +
((HttpServletRequest)sre.getServletRequest()).getRequestURI());
}
public void requestDestroyed(ServletRequestEvent sre) {
sre.getServletContext().log("Request completed: " +
((HttpServletRequest)sre.getServletRequest()).getRequestURI());
}
}
public class CookieRequestWrapper extends HttpServletRequestWrapper {
private Map cookiesMap;
public CookieRequestWrapper(HttpServletRequest request) {
super(request);
this.cookiesMap = new HashMap<>();
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
cookiesMap.put(cookie.getName(), cookie.getValue());
}
}
}
@Override
public Cookie[] getCookies() {
return cookiesMap.entrySet().stream()
.map(e -> new Cookie(e.getKey(), e.getValue()))
.toArray(Cookie[]::new);
}
public String getCookieValue(String name) {
return cookiesMap.get(name);
}
public void addCookie(String name, String value) {
cookiesMap.put(name, value);
}
}
// 使用例
@WebFilter("/*")
public class CookieFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
CookieRequestWrapper wrappedRequest = new CookieRequestWrapper((HttpServletRequest)request);
chain.doFilter(wrappedRequest, response);
}
}
RESTful APIにおけるセッション管理と認証のベストプラクティス:
// JWTフィルター例
@WebFilter("/api/*")
public class JwtFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
String token = httpRequest.getHeader("Authorization");
try {
if (token != null && JwtUtil.validateToken(token)) {
String username = JwtUtil.getUsernameFromToken(token);
httpRequest.setAttribute("username", username);
chain.doFilter(request, response);
} else {
((HttpServletResponse)response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
} catch (Exception e) {
((HttpServletResponse)response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}