2

ユーザーがサーバー上の自分の情報にアクセスできるようにするために REST API を必要とするプロジェクトに取り組んでいます。Spring MVC を使用してシステムを動作MappingJacksonJsonViewさせ、必要に応じて JSON を返すようにしました。

システムにセキュリティを組み込みたいと考えています。まず、ユーザーが自分自身を認証し、リソースにアクセスするための正しいアクセス許可を持っていることを確認し、次に、ユーザーが公開されているアクセスのみを許可するように、データを十分にきめ細かく制御したいと考えています。リソースの一部、またはリソース全体に対する適切なアクセス許可がある場合。

たとえば、次のようになります。

シナリオ A:ユーザー A は、メッセージのリスト、名前、電話番号にアクセスしたいと考えています。その後、彼らは認証され、彼らの許可により、彼ら自身のすべての情報にアクセスできるようになります.

シナリオ B:ユーザー A はユーザー B の電話番号にアクセスしたい - したがって、A にはその部分の情報にアクセスする権限がないため、応答でユーザー B のメッセージのリストを除外します。

  • Q1: Apache Shiro と Spring MVC を使用してこれを行うための適切な方法はありますか?
  • Q2:他の誰かがこれをどのように達成したかについて、例やチュートリアルへのリンクを持っている人はいますか?
  • Q3: Shiro を使用して、この種のきめ細かな制御を許可するには、どのようなアクセス許可スキームが最も効率的ですか?

私はこれまで Shiro と仕事をしたことがありませんが、例を試したりドキュメントを読んだりすると、これは可能であり、Shiro がソリューションに最適であるように見えます。しかし、私は他の解決策を受け入れています。

編集:解決策の 1 つは、Shiro と Jackson でこれが可能かどうかはわかりませんが、POJO でパブリックまたはプライベートとしてマークできるプロパティに注釈を付けることです。または、さらに良いことに、それらにアクセスするために必要な許可でそれらをマークします。次に、Jackson がオブジェクトの JSON 表現を出力すると、現在のプロパティのアクセス許可を調べて、その注釈からプロパティを出力するかどうかを決定できます。

4

1 に答える 1

1

この問題の解決策は非常に簡単です。Jackson の一連のビュー クラスを作成しました。

public class SecureViews {
    public static class Public{};
    public static class Authenticated extends Public{};
    public static class User extends Authenticated{};
    public static class Internal extends User {};
}

次に、以下を追加して、保護したい各エンティティのすべてのゲッター メソッドに注釈を付けました。

@JsonView(SecureViews.Authenticated.class)
public String getPhoneNumber() {
    return phoneNumber;
}

上記のルールが意味することは、システム内で認証されたユーザーのみがユーザーの電話番号を表示できるということです。

または

@JsonView(SecureViews.User.class)
public List<Event> getEvents() {
    return Collections.unmodifiableList(events);
}

次に、インターフェイスを作成し、そのインターフェイスをすべてのセキュア エンティティに実装しました。

public interface SecurePropertyInterface {
    Class<?> getMaxPermissionLevel(Long userID);
}

そして、これがそのメソッドの実装です:

public Class<?> getMaxPermissionLevel(Long userID) {
        if (userID != null && userID == uid) {
            return SecureViews.User.class;
        }
        if (SecurityUtils.getSubject().isAuthenticated()) {
            return SecureViews.Authenticated.class;
        }

        return SecureViews.Public.class;
    }

今魔法のために。次のメソッドを拡張MappingJacksonJsonViewしてオーバーライドします。

@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        Long userID = CTConversionUtils.convertToLong(request.getParameter("id"));
        model = (Map<String, Object>) super.filterModel(model);

        Class<?> viewClass = SecureViews.Public.class;

        if(SecurityUtils.getSubject().isAuthenticated()) {
            viewClass = SecureViews.Authenticated.class;
        }

        for (Entry<String, Object> modelEntry : model.entrySet()) {
            if (modelEntry.getValue() instanceof SecurePropertyInterface) {
                viewClass = ((SecurePropertyInterface)modelEntry.getValue()).getMaxPermissionLevel(userID);
            }
        }
        objectMapper.viewWriter(viewClass).writeValue(getJsonGenerator(response), model);
    }

SpringsetObjectMapperMappingJacksonJsonView.

私のSpring設定は次のようになります:

<bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
        p:order="1">
        <property name="mediaTypes">
            <map>
                <entry key="json" value="*/*" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <bean
                    class="com.bytesizecreations.connecttext.json.SecureMappingJacksonJsonView">
                    <property name="objectMapper">
                        <bean
                            class="org.codehaus.jackson.map.ObjectMapper" />
                    </property>
                </bean>
            </list>
        </property>
    </bean>

では、今ここには何がありますか? 応答を JSON に逆シリアル化する必要があるたびに、 renderMergedOutputModel メソッドが呼び出されます。ユーザーの ID がリクエストに含まれている場合は、それを保存します。次に、モデル マップをループして、各値が SecurePropertyInterface であるかどうか、および最大のアクセス許可レベルを取得しているかどうかを確認できます。

この戦略は、私の目的を適切に果たしているようです。ユーザーがユーザーのリストを要求すると、認証済みのプロパティのみが取得されますが、ユーザーが自分の詳細を要求すると、プライベート プロパティのみが取得されます。

このコードはさらに改善できると思いますが、複雑さを軽減するために多くの時間を費やしました。

于 2011-03-30T06:44:27.023 に答える