goo blog サービス終了のお知らせ 

ウィリアムのいたずらの、まちあるき、たべあるき

ウィリアムのいたずらが、街歩き、食べ物、音楽等の個人的見解を主に書くブログです(たま~にコンピューター関係も)

サーブレット等で、同一ブラウザでユーザーごとにセッションの中身を替えたい

2014-02-21 17:20:23 | JavaとWeb
品質は後付けできる

1つのブラウザで、複数タブを使った場合、サーバー側では、同一セッションになる。
ということは、

・はじめ、ユーザー名xmldtpでログインしておき、
 その後、新たなタブでウィリアムとログインしても、
 同じセッションが使われる

・はじめ、ユーザー名xmldtpでログインしておき、
 その後、新たなタブでxmldtp(と同名で)ログインした場合、
   (1)先にログインしているからエラー
   (2)後でログインしたものを優先し、セッションを作り直す
 のどちらもできる。ただし、(2)でセッションを作り直した後に、
 はじめ操作していたタブでアクセスすると(2)で新しいセッション
 になっているのでおかしくなる可能性がある(場合によっては落ちる)




■お題

ここで、

xmldtpでログインすると、

のように、アクセス回数とセッションIDを返すプログラムで、
何回もxmldtpでアクセスして

のようになった状態で、

のように、新たなIDでログインすると

のように初期化され

と戻すと

のように前の値を持ってくることを考える

※上記のセッションIDを見ると、みんな同じ。つまり、値は変わっているが、
 同一セッションを使いまわしている




■概要

Tomcat7で指定URLのみクッキーを無効に
http://questionbox.jp.msn.com/qa7881678.html

の「ANo.3」に書かれているとおり。つまり、

・Filterに、ハッシュマップをstaticで用意する(=全体で1個)、
  →そのハッシュマップには、
    キーはログイン名、値は保存したい値を持っておく

・FilterのdoFilterで、
   パラメータから、ログイン名をチェック
   上記ハッシュマップに値が入っていたら、その値をセッションに入れる
   →ここで、ログイン名ごとに、セッションの値が入れ替わる

・セッションのリスナーで
   セッションの値が書き換わったら、上記ハッシュマップの値も書き換え

・セッションが破棄されれたら、上記ハッシュマップのログイン名のデータも破棄

ということをする。




■お題に関係ない部分

お題に関係ない、はじめの画面は、こんな感じのJSP(index.jsp)
<html>
<head>
<title>MySession</title>
</head>
<body>
<form method="post" action="kekka.jsp">
login name<input type="text" name="loginName"/>
<input type="submit"/>
</form>
</body>


ここで、ログイン名を入れているので、上記フィルタにより、
noの値がログイン名ごとに入れ替えられ
結果表示のJSP(kekka.jsp)

<%

int ino;

String no = (String)session.getAttribute("no");
if ( no == null )
{
ino=0;
}
else
{
ino = Integer.parseInt(no);
ino++;
}
session.setAttribute("no",String.valueOf(ino));
%>

<%=ino%>
<%=session.getId()%>


にわたる。ここでnoに1足すと、リスナーが呼ばれる。

ここまでは、あまり今回のお題に関係ない




■セッションの値を入れ替える部分:フィルタ

フィルタの値を入れ替えるJavaプログラムMyFilter1.javaはこんなかんじ。

package mytomcat1;

import java.io.IOException;
import java.util.HashMap;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class MyFilter1 implements Filter {
static HashMap<String,String> svdata = new HashMap<String,String>();
@Override
public void destroy() {
// TODO 自動生成されたメソッド・スタブ

}

@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {

// http://www.javaroad.jp/servletjsp/sj_servlet10.htm
HttpServletRequest myreq = (HttpServletRequest)req;

HttpSession session = myreq.getSession();
String loginName = myreq.getParameter("loginName");

// セーブ領域から、データ復元
if ( loginName != null)
{
String no = svdata.get(loginName);
if ( no == null)
{
no = "0";
}
session.setAttribute("loginName",loginName);
session.setAttribute("no", no);
}

chain.doFilter(req, res);

}

@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO 自動生成されたメソッド・スタブ

}

}





■値が書き換わったとき:リスナー

セッションの値が書き換わったら、セーブしてあるデータも書き換える
MyFilterListener1.javaはこんなかんじ

package mytomcat1;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MyFilterListener1 implements HttpSessionListener,
HttpSessionAttributeListener {

@Override
public void attributeAdded(HttpSessionBindingEvent arg0) {
}

@Override
public void attributeRemoved(HttpSessionBindingEvent arg0) {

}

@Override
public void attributeReplaced(HttpSessionBindingEvent arg0) {
// http://www.javaroad.jp/servletjsp/sj_servlet9.htm
// セッションが変更されたら、保存しているデータも変更
HttpSession session =arg0.getSession();
String loginName = (String)session.getAttribute("loginName");
String no = (String)session.getAttribute("no");
if ( loginName != null)
{
if ( no == null)
{
no = "0";
}
MyFilter1.svdata.put(loginName,no);
}


}

@Override
public void sessionCreated(HttpSessionEvent arg0) {
}

@Override
public void sessionDestroyed(HttpSessionEvent arg0) {

// セッションが削除されたら、保存しているデータも削除
HttpSession session = arg0.getSession();
String loginName = (String)session.getAttribute("loginName");
if ( loginName != null)
{
MyFilter1.svdata.remove(loginName);
}
}

}






■web.xml

フィルターとリスナーをweb.xmlに登録する。こんな感じ

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Test Servlet</display-name>
<description>Test Servlet</description>
<filter>
<filter-name>MyFilter1</filter-name>
<filter-class>mytomcat1.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
<listener-class>mytomcat1.MyFilterListener1</listener-class>
</listener>
<servlet>
<servlet-name>MyServlet1</servlet-name>
<servlet-class>MyServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet1</servlet-name>
<url-pattern>/MyServlet1</url-pattern>
</servlet-mapping>
</web-app>






■つけたし

(A)以上でできるのだが、これを実行すると

のように、違うブラウザでも、ログイン名が同じだと、同じセッションデータ
を使ってしまう。これはこれでいいかもしれないが、いけない場合、
ログイン名のほかにセッションIDを足したものをキーとすればよい

(B)後がちにするには、保存しているデータに、ログイン時間を持ち、
  サーバーアクセスする際には、ログイン名とログイン時間を常に
  パラメータで渡す。ログイン名は一致するが、ログイン時間が一致
  しない場合は、後からログインされたものとみなして、
  すぐに終了処理をするなり、セッションにその旨の値を入れる

(C)ユーザーAがログインしたら、B,Cはキャンセルという場合、
 ユーザーAがログインしたら、ハッシュマップのB,Cデータを
 消せば、B,Cがアクセスしたとき、ログインデータをなくせる

(D)これは、排他制御はしていない。したがって、同時アクセスされるとおかしくなる。
 排他制御をするなら、ハッシュマップの(上記のソースだとsvdataの)ログイン名と
 値を直接変更し、その際排他制御をし・・・とかいうと、セッションそのものを
 自作しているのと、なんらかわらない(^^;)




■いいたいこと

で、いいたいことは、このやりかたでなく、
ユーザーごとにログインしたいとか、(A),(B),(C)のバグ修正
をするとしても、基本的に1を足すというロジック部分は変わっていない
(変わっているのはセッション部分で、主にフィルターとリスナーの中)
つまり、ロジック部分を分離すれば、あとは入出力部分で品質は変えられる。
この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« ユーザーは、ソフトの品質・... | トップ | 米Microsoft、ブラウザ版無料... »
最新の画像もっと見る

JavaとWeb」カテゴリの最新記事