ShiroのrememberMe機能使用指導(なぜrememberMe設定が効かなかったのか?)

6003 ワード

に質問
shiroではrememberMe機能を提供しており、このような機能を使用しています.
UsernamePasswordToken token = new UsernamePasswordToken(loginForm.getUsername(),loginForm.getPassword());
		
		if(loginForm.getRememberMe() != null && "Y".equals(loginForm.getRememberMe())){
			token.setRememberMe(true);
		}

自分でフラグビットを設定して、このフラグビットに基づいてユーザーがチェックして私を覚えているかどうかを判断して、チェックしたらtokenを使用します.setRememberMe(true)は私を覚えるように設定されています.
多くの人が私が最初に考えたように、このように設定が終わったと思って、それからブラウザを閉じてからブラウザを開けないと、私たちのサイトに入ると自動的にログインします.しかし、結果は、ブラウザを再開した後も、Webサイトにアクセスしてユーザー名とパスワードを入力させます.
では、いったいこの機能はどのように使うのでしょうか.
原理の解釈
ヒロはクッキーに何をしたの?
実はあなたはこのrememberMeを設定した後shiroはまだ少し仕事をしています.それはrememberMeというcookie値を生成してブラウザに保存します.そして、このパラメータはsubjectを呼び出すにつれて.logout()は自動的に消去されます.このパラメータの値は長いBase 64で暗号化された文字列で、多分長いです.
  :	rememberMe
  :	6gYvaCGZaDXt1c0xwriXj/Uvz6g8OMT3VSaAK4WL0Fvqvkcm0nf3CfTwkWWTT4EjeSS/EoQjRfCPv4WKUXezQDvoNwVgFMtsLIeYMAfTd17ey5BrZQMxW+xU1lBSDoEM1yOy/i11ENh6eXjmYeQFv0yGbhchGdJWzk5W3MxJjv2SljlW4dkGxOSsol3mucoShzmcQ4VqiDjTcbVfZ7mxSHF/0M1JnXRphi8meDaIm9IwM4Hilgjmai+yzdVHFVDDHv/vsU/fZmjb+2tJnBiZ+jrDhl2Elt4qBDKxUKT05cDtXaUZWYQmP1bet2EqTfE8eiofa1+FO3iSTJmEocRLDLPWKSJ26bUWA8wUl/QdpH07Ymq1W0ho8EIdFhOsELxM66oMcj7a/8LVzypJXAXZdMFaNe8cBSN2dXpv4PwiktCs3J9P9vP4XrmYees5x27UmXNqYFk86xQhRjFdJsw5A9ctDKXzPYvJmWFouo3qT5hugX0uxWALCfWg8MHJnG9w7QgVKM8oy3Xy4Ut8lSvYlA==

この文字列は、あなたがログインしたPrincipalをシーケンス化してBase 64にした結果です.Principalはshiroの概念で、唯一の文字列があなたのこのユーザーを表すことができることを示しています.もしあなたが最も簡単なユーザー名パスワードでログインして、SimpleAuthenticationInfoオブジェクトを使用しているならば、このPrincipalは実は文字列で、あなたのユーザー名usernameです.
だからこの解読はあなたのusernameです
shiroはrememberMeが安全ではないと思った
shiroはrememberMeを登録したのと同じようにしてはいけないと思っています.これは安全ではありません.だからshiroはrememberMe=trueでもauthcではなくuserレベルだと思っています.
パスブロックを設定するのは一般的にこのように設定されています
/** = authc

これにより、すべてのパスがアクセスするためにログインする必要があることが保証されます.たとえあなたがrememberMe=trueであってもアクセスできません.公式には、ユーザーにブロックレベルを設定すればアクセスできます.例えば
/** = user

これでアクセスできますが、公式には敏感ではない部分はuserを使うことをお勧めします.敏感な部分はユーザーにもう一度ログインさせなければなりません.淘宝網にログインしなくても、前回ログインしたことがあれば、私の淘宝のページを直接見ることができますが、私の宝物をクリックするとまたログインさせます.
でも!私たちは確かに多くの場合
ユーザーがログインしていることを覚えておく必要があります!
userに設定するという案にはもう一つの問題があります.私たちの実際のプロジェクトでは、ログイン後にユーザーコンテキストを設定する仕事がたくさんあります.例えば、sessionを設定するなどです.もし私たちがブロックレベルをuserに設定するだけであれば、再び入るときにアクセスできますが、sessionは空で、私たちのページは必ず異常に頻出します.
ソリューション
前提条件
このソリューションを採用する前提は、自分でrealmを実装する必要がありますが、これは誰もが実現すると信じています.結局、デフォルトはjdbcRealmではありません.本当のプロジェクトはデータベースを調べてこそ、ユーザーがログインしているかどうかを判断することができます.では、皆さんのプロジェクトにログインを検証するJdbcRealmがあり、ユーザー名パスワードで認証されていると仮定します.doGetAuthenticationInfoメソッドでは、次のような方法で認証されています.
...
info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());

この前提条件はあなたのprincipalがusernameであることを保証して、大部分の人がチュートリアルに従ってshiroをする時すべてこのような方式を採用したと信じています
STEP 1 FormAuthenticationFilterのisAccessAllowedメソッドを複写する
新しいクラスを作ってFormAuthenticationFilterを継承し、isAccessAllowedメソッドを複写します.
package com.yqr.jxc.shiro;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import com.yqr.jxc.service.global.GlobalUserService;

public class RememberAuthenticationFilter extends FormAuthenticationFilter {
	
	@Resource(name="globalUserService")
	private GlobalUserService globalUserService;
	
        /**
        *                
        */
	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        Subject subject = getSubject(request, response);

        //   isAuthenticated   false         ,   isRememberd  true                   
        if(!subject.isAuthenticated() && subject.isRemembered()){

                //  session       
        	Session session = subject.getSession(true);
                
                //   session       session       ,  userId,           
        	if(session.getAttribute("userId") == null){

        		//         ,         ,     
                        //           ,    username
        		String username = subject.getPrincipal().toString();
        		
        		//                   ,            session ,      
        		globalUserService.initUserContext(username, subject);
        	}
        }

        //          subject.isAuthenticated()        subject.isRemembered()        remember    
        return subject.isAuthenticated() || subject.isRemembered();
    }
}

STEP 2この新しいAuthenticationFilter(認証フィルタ)を使用する設定
スプリングを使っているなら
<!--    rememberMe   filter -->
<bean id="rememberAuthFilter" class="com.yqr.jxc.shiro.RememberAuthenticationFilter" ></bean>

<!--     /** = authc     rememberAuthFilter
...
/** = rememberAuthFilter
...

iniファイルを使用している場合は
rememberAuthFilter=com.yqr.jxc.shiro.RememberAuthenticationFilter

#     /** = authc     rememberAuthFilter
...
/** = rememberAuthFilter

それからプロジェクトを再起動してテストして、まずシステムにログインして、それから直接ブラウザを消して、それからブラウザを開けて直接システムのあるページの住所を入力して、直接入ることができることを発見して、sessionなども設定しました
きれいに見えますか?でも!
長い間忙しく働いて、最後に私はやはり私のシステムの中でこの機能を撤去することにしました.どうしてですか.この機能には致命的なセキュリティ欠陥があるので、誰がこのクッキーの値を他のブラウザに持って行ってもログインすることができます.いくら暗号化されていても、このクッキー値がブラウザのそれぞれの属性に基づいてこのブラウザのみで使用できるようになっても、ハッカーにとって、フォームを通じて物を送信すれば、フォーム全体が偽造されます.期限切れが増えても、その間に偽造されるリスクがあり、良い解決策は考えられませんでした.
唯一考えられるのは、シーンを使う選択に対して、厳格なビジネスシステムでは私のこの機能を覚えることができません.非厳格なシステムでは、例えば敏感ではないシステムでは、流量を見て微博を見るなど、rememberMeの問題を解決するために以上の方法を使うことができます.
したがって、rememberMeの機能範囲を拡大するかどうかは慎重に選択してください!
最後にロシアからのmeriのこの透徹したshiro研究文に感謝します.http://meri-stuff.blogspot.com/2011/03/apache-shiro-part-1-basics.html 
本文はmeriとblurblurNickのすばらしい問答に基づいて書いたのです