かなりの数の小さなパートナージュニアパートナーが歌Geは、この問題を助けていたがあります。
最初に私にはそれが小さな確率BUGかもしれないと思ったが、多くの人に尋ねられたとき、私は初心者のために、この問題だけでなく、一定の普遍性を考えて、慎重に記事があなたとチャットこの質問を書くことが必要ですピットのうち小さなパートナーを防ぎます。
問題を再現する1
あなたは春のセキュリティを使用している場合は、我々がログオンしたとき、あなたは現在、以下のメソッドを介してユーザ情報でログインして取得できます。
SecurityContextHolder.getContext().getAuthentication()
- コントローラ追加認証パラメータの方法で
両方のアプローチは、電流がログオンしているユーザ情報取得することができます。具体的な運用方法を、私たちは歌のGeのリリース前にチュートリアルを見ることができます:春のセキュリティを動的にユーザーのログイン情報を更新する方法?。
通常の状況では、ユーザーへのメッセージは、上記の2つの方法のいずれかによって、ログインしている私たちを得ることができます。
これらの2つの方法のいずれかで異常な状況、はNULLを返します。
(あなたは、システム内の任意の有用な情報はありませんので)、システムが受信したことを意味しますが、あなたがログインしている知っていない場合はnullが、これは二つの問題現在のリクエストがあります。
- 現在ユーザ情報に記録させることはできません。
- あなたはすべての要求を送信すると、システムはあなたに401のリターンが得られます。
2.いじります
この問題を理解するためには、ユーザ情報が春のセキュリティに保存されている場所を最後にあることを理解する必要がありますか?
我々は2つのデータ・アクイジション・モードでは、それがどこから来たの取得と、それ以前のデータ2つのデータ取得モードは言ったが、?
私たちは前に話した最初の曲のGeとは、SecurityContextHolder内のデータは、本質的には、中に格納されThreadLocal
、ThreadLocal
機能がアクセスするスレッドその内部のデータの存在、預金のスレッドです。
これは、スレッドAでのログイン要求などの要求は、スレッドのログイン要求にデータを取得することができないかもしれないことを理由に、後者のスタンド、別のスレッドに対処するために、別の要求がサーバーに来るとき、問題を作成し、格納されたユーザ情報に-ログオンThreadLocal
要求、処理スレッドBの背中、この時間は、ユーザのログイン情報を取得することができないということ。
しかし、実際には、通常の状況下で、我々はログインユーザ情報を取得することができますたびに、これはそれが起こったのかですか?
私たちは、この春のセキュリティを導入していますSecurityContextPersistenceFilter
。
リトル友人すべて知っている、それは春のセキュリティまたは史郎あるかどうか、それは一連の機能が実際に春のセキュリティ、あなたとフロントソングのGe話にフィルタによって行われますUsernamePasswordAuthenticationFilter
また、フィルタ前フィルタフィルタがありSecurityContextPersistenceFilter
、要求が到着し、UsernamePasswordAuthenticationFilter
通過する前SecurityContextPersistenceFilter
。
私たちは、そのソースコード(一部)を見てください:
public class SecurityContextPersistenceFilter extends GenericFilterBean {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
}
}
}
元のメソッドは、私はここで、より重要な部品リスト、非常に長いです。
- SecurityContextPersistenceFilter GenericFilterBeanから継承された、とGenericFilterBeanはフィルタ、それはのdoFilter内部にある最も重要な方法としてSecurityContextPersistenceFilterので、フィルターを実現しています。
- doFilterメソッドでは、それは最初のレポから出たSecurityContextを読み込み、実際にHttpSessionSecurityContextRepository、SecurityContextがたちが読書のコアメソッドを参照してくださいreadSecurityContextFromSessionアプローチ、に入りますリード動作され、ここでレポ
Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);
ここで、 springSecurityContextKeyオブジェクトの値は、最終的にSecurityContextがオブジェクトに変換されるオブジェクトを読み出し、SPRING_SECURITY_CONTEXTです。 - SecurityContextがインターフェイスであり、それはユニークな実装クラスSecurityContextImpl、実装クラスは、実際にセッション値に格納されたユーザ情報でいます。
- GET SecurityContextが後に、この方法によりSecurityContextHolder.setContextたSecurityContextにThreadLocalはなるように、現在のリクエストで、春のセキュリティのその後の動作は、我々が直接SecurityContextHolderへのユーザーからの情報を得ることができ、行くように配置されました。
- 次に、chain.doFilterによって要求が(この時間は入るだろう下るを続けて行い
UsernamePasswordAuthenticationFilter
、フィルタA)。 - フィルタチェーンの後に完了した後、フロントエンドへの応答データは、別のステップで最終的に仕上げ操作は、このステップは非常に重要です。SecurityContextHolderたSecurityContextから、ここで取得するには、取得した後、セッションにSecurityContextがに到達するためにrepo.saveContextメソッドを呼び出した後、空にSecurityContextHolderます。
この時点で、全体のプロセスは非常に明確です。
サーバーに到達するための各要求は、最初にすることは便利、休暇への要求は、SecurityContextHolderは、SecurityContextがセッションに戻すことになるクリアされたときに、その後の使用を容易にするために、SecurityContextが外のセッションから見つけ、その後、SecurityContextHolderに提供しますとき取得要求。
徹底的にそれを理解した後、現在のユーザーのログイン春のセキュリティログオンした後、この問題を解決するために行くために得ることができない、それは非常に簡単です。
3.問題解決
上記の分析の後、現在のユーザーが、このようなAの事についての情報を得ることができない後にログインがなぜ起こるかで見てみましょうか?
最も単純なケースは、あなたが実行するための新しいスレッドにいるということですSecurityContextHolder.getContext().getAuthentication()
、それは言うまでもないユーザー情報を取得確かではありません。次のような:
@GetMapping("/menu")
public List<Menu> getMenusByHrId() {
new Thread(new Runnable() {
@Override
public void run() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println(authentication);
}
}).start();
return menuService.getMenusByHrId();
}
この単純な質問は、私たちは簡単にトラブルシューティングすることができます信じています。
セッション中に深く隠されたのdoFilterメソッドSecurityContextPersistenceFilterのではないからで、空の内側SecurityContextHolderにつながったユーザー情報にロードさもあります。
SecurityContextPersistenceFilterでそのように、原因はより多くてもよく、ユーザ情報に負荷に失敗しました:
- どこへ行くのセッションに保存されたデータがない場合、要求を残します。
- 現在の要求は、フィルタチェイン自分自身を来ていません。
この問題は、次に起こるとき?設定SecurityConfig#のconfigure(WebSecurity社)メソッドは重要な点を無視すると、一部のパートナーは小さいかもしれません。
私たちは春のセキュリティリソースが匿名アクセスすることができたい場合は、我々は2つのオプションがあります。
- 春のセキュリティフィルターチェーンを服用しないでください。
- 春のセキュリティフィルタ・チェーンが、匿名アクセスを飲み続けます。
これらの2つのアプローチは、2つの異なる構成に対応します。ユーザ情報に-ログオンし、第二はので、ここで私たちが最初に焦点を見て、影響されないために私たちに影響を与える可能性が第一の構成。
春のセキュリティフィルタチェーンは、我々は、一般的にアレンジすることができ、歩いていません。
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**","/js/**","/index.html","/img/**","/fonts/**","/favicon.ico","/verifyCode");
}
通常、この設定は問題ではありません。
あなたは残念ながらアドレス置くために、ログオン要求であれば、それはGGです。ログイン要求は、誰もがアクセスすることができますが、ここに配置することはできませんが(と方法によって、それは、要求のリリースへの匿名アクセスを許可する必要があります)。あなたがここに行けば、行くログオン要求はないでしょうSecurityContextPersistenceFilter
手段がログインしていないユーザー情報後続の要求につながったセッションへのフィルタは、ログインユーザ情報を取得することはできません。
これは、小さなパートナーが直面する問題の始まりです。
さて、本明細書に提供される春のセキュリティを使用しているときは、同様の問題が発生した場合、ほとんどの友人は、それを解決するためのアイデアを望むことができます。あなたがやりがいを感じた場合は、右下隅ケインにタップすることを忘れないでください