org.apache.shiro.session.UnknownSessionException:IDを持つセッションがありません[XXXX]的解决方案
背景説明
史郎著作権管理を使用してSpringBootプロジェクト、。通常の実行ファイルがインポートされ始めて、すべてをテスト中に発見、しかし、いくつかのインポートインポートはいつものように他の機能を実行するには、この時点で与えられて再び実行されます後
調査プロセス
- [X] 1.オンライン検索は、それらのほとんどは、次のことを言っています:
史郎のデフォルト名はクッキーJSESSIONID、サーブレットコンテナとの競合です。史郎は、セッションIDを変更することができます
- [X] 2。それが理由の上にある場合、あなたは最初から問題にログインする必要があり、非常に少数の動作上の問題ではありません。そして、ここで私は繰り返し、この時点でインポートした後に間違って行くと、他の機能は正常です。したがって、この理由、自分の調査を除きます。
- [X] 3。のみ、この問題をインポートするので、他の機能はすべて正常である;それは、コードがインポート問題があることが疑われました。
- [X] 4.インポートコード調査は、現在のユーザーのみが、史郎文を保存し、関連するコードの実装で使用していますことを発見し、それはその問題がここに疑われました。
this.operator = (String) SecurityUtils.getSubject().getPrincipal();
- ここで、[X] 5は、手動で一括インポートデータとステートメントが、二つ取得した被検体矛盾を実行するためのデータを追加することが見出されブレークポイント。だから、トラブルシューティングするためにソースコードを読みます。
// 从SecurityUtils中获取Subject源码如下
// package: org.apache.shiro.SecurityUtils
public static Subject getSubject() {
Subject subject = ThreadContext.getSubject(); // ①
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}
// 继续跟进上面①中的方法
// package: org.apache.shiro.util.ThreadContext
public static Subject getSubject() {
return (Subject) get(SUBJECT_KEY); // ②
}
// 继续跟进上面②中的方法
// package: org.apache.shiro.util.ThreadContext
public static Object get(Object key) {
if (log.isTraceEnabled()) {
String msg = "get() - in thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
Object value = getValue(key); // ③
if ((value != null) && log.isTraceEnabled()) {
String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" +
key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]";
log.trace(msg);
}
return value;
}
// 继续跟进上面③中的方法
// package: org.apache.shiro.util.ThreadContext
private static Object getValue(Object key) {
Map<Object, Object> perThreadResources = resources.get(); // ④
return perThreadResources != null ? perThreadResources.get(key) : null;
}
// 上面④中的resources在ThreadContext中定义如下
// package: org.apache.shiro.util.ThreadContext
private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
- [X] 6.ソース顔追跡によれば、対象はThreadLocalの結合スレッドであるが見つかりました。その後、再作成し、現在のスレッドにバインドされていない場合は、件名を取得バインドされ、現在のスレッドを取得する対象。そして私は、転送効率、マルチスレッドの使用を改善するために導入されたとき。これは、問題の原因を発見しました
問題の原因
- コアスレッドのデフォルトのタイムアウトが回収されることはありませんしながら、プロジェクトは、スレッドプールが10であるカーネルスレッドの数を設定すると仮定すると、
PS:threadPoolExecutor.allowCoreThreadTimeOut(真)によって、提供カーネルスレッドのタイムアウト回復
- ユーザAがログインすると、インポート操作は、スレッドプールから5のスレッドは、その後、5つのスレッドには、件名、ユーザーをバインドするとき
- ユーザAは、数回、すべてのコアスレッドプールスレッドと、ユーザはサブジェクト結合インポート操作を実行する場合。ユーザーAログの後、コアスレッドプールのスレッドが破壊されることはありません。
- 後続のユーザBは、インポート操作は、操作を再分配する。このとき、スレッドプールスレッドを実行されるが、この時間は、すべてのスレッドは、ユーザAに接続されているので、取得した被検体テーマは、ユーザAであり、被写体からのセッションを取得しますこのセッションでは、破壊された際に、エラーので、
// 根据sessionId获取session,获取为空则报错
// package: org.apache.shiro.session.mgt.eis.AbstractSessionDAO
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = doReadSession(sessionId);
if (s == null) {
throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
}
return s;
}
ソリューション
マルチスレッド史郎関連するコードを使用しないでください。パラメータとしてユーザー名は、個別に使用できなくなりました。
PS:メイクトラブルシューティング手順上記のように、このソリューションは、実際の状況をトラブルシューティングするために、すべてのケースではありません。