ここでは主にhiroのカスタムアノテーションソリューションを採用します。この記事では主に以下の問題を解決します。
ロジックを通じてページを API インターフェースに関連付ける方法。
Shiro の自己アノテーションの使用法。
カスタム注釈の書き方。
テーブル間の構造的関係において、ページとインターフェースのテーブルは最終的に権限テーブルに関連付けられます(詳しくは、以前の記事「権限」設計の話」を参照してください) 。
今後は、ある程度の権限制御を考慮しながら、別のソリューションに置き換えて低コストを実現したいと考えています。ここでは 2 つの概念を紹介します。 ビジネスモジュール、操作タイプ。
ビジネスモジュール
コンセプト: システム内のビジネスモジュールを一種のデータに抽象化します。これは文字列の形式で表現できます。たとえば、ロール管理は role-manage に対応します。ユーザー管理は user-manage などに対応します。システム内に存在する業務モジュールを「最小権限の原則」に基づいて分割し、最終的に分散可能なデータの塊を形成します。
使用原則: API インターフェース、ページ、関数は本質的にビジネス モジュールに論理的に関連しているため、API インターフェースとページ (および関数ポイント) を論理的に照合して、ページが一致しているかどうかを判断できます。ビジネスモジュールの関係。
操作タイプ
コンセプト: システム内のすべての操作タイプを一種のデータに抽象化することもできます。たとえば、新しい追加は追加と割り当てに対応します。対応するのはallotなどです。システム内のあらゆる操作を業務モジュールごとの「データライセンス」で分割し、最終的に分散可能なデータの塊を形成します。
使用原則: ページは表示であり、関数ポイントはアクションであり、インターフェイスは最終アクションのためのリソースの提供です。呼び出されるリソースは「ビジネス モジュール」によって決定され、リソースの使用量は「操作タイプ」によって決定されます。この 2 つにより、ページのファンクション ポイントによってトリガーされたインターフェイスが認証内にあるかどうかを大まかかつ正確に判断できます。
さて、この 2 つの概念が提案されたところで、実際の最終的な用途は何でしょうか。まずは以下の観点から考えてみましょう。
データベース内のページテーブルまたはAPIインターフェーステーブルのデータは本物で有効ですか?
ページまたはインターフェイスの実際の使用は、関数の存在またはデータベース テーブル内のデータの存在に基づいています。
権限構造において、「制御オブジェクト」を保存する唯一の方法はデータベースですか?
これらの質問を結論から見てみましょう。まず、「コントロール オブジェクト」の保存先はデータベース、コード、または設定ファイルにあります。必ずしもデータベースにある必要はありません。 ; 次に 2 番目の質問に答えます。 問題は、インターフェイス情報がデータベースに存在するが、サーバーがこのインターフェイスを開発していない場合、データベース情報自体に問題があるか、データベース内の新しいインターフェイスが次のインターフェイスである必要があることです。有効になるためにサーバーにデプロイされているかどうか、最初の疑問は、データベース内の「コントロール オブジェクト」テーブルのデータが必ずしも真実で有効であるとは限らないということです。そこで、次のような解決策を考え出すことができます
インターフェース上で アノテーション を使用して、「ビジネスモジュール」と「操作タイプ」のデータ情報を補うことができます。どちらのタイプの情報も定数クラスに格納できます。
ただし、このソリューションは非強力なコントロール インターフェイス プロジェクトにのみ適しています。強力なコントロール インターフェイス プロジェクトでは、ページとインターフェイスをバインドする必要がありますが、これには莫大な運用コストとメンテナンス コストがかかります。さらに、/api/page/xxxx/ (ページのみに使用)、/api/mobile/xxxxx (モバイル端末のみに使用) などのインターフェイス ルーティング ルールによって分割して、のみ使用されるインターフェイスを分類することもできます。このクラス インターフェイスは認証のみを実行し、認可は実行しませんが、これによっても目的を達成できます。
理論的なアイデアが承認された後、残りは技術的な実践に移されます。以下のシロの注釈を簡単に説明します。
アノテーション名 | 関数 |
---|---|
@RequiresAuthentication | は、クラス、メソッド、インスタンスに作用します。呼び出された場合、現在のサブジェクトは認証される必要があります。 |
@RequiresGuest | は、クラス、メソッド、インスタンスに作用します。呼び出されたとき、サブジェクトはゲスト状態になることができます。 |
@RequiresPermissions | は、クラス、メソッド、インスタンスに作用します。呼び出すときは、プロジェクトの現在のインターフェイスに Permission (権限情報) が含まれているかどうかを確認する必要があります。 |
@RequiresRoles | は、クラス、メソッド、インスタンスに作用します。呼び出し時に、サブジェクトに現在のインターフェイスのロール (ロール情報) が含まれているかどうかを判断する必要があります。 |
@RequiresUser | は、クラス、メソッド、インスタンスに作用します。呼び出し時には、対象者が現在アプリケーションを使用しているユーザーであるかどうかを判断する必要があります。 |
/** * 1.当前接口需要经过"认证"过程 * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresAuthentication public String test(){ return "恭喜你,拿到了参数信息"; } /** * 2.1.当前接口需要经过权限校验(需包含 角色的查询 或 菜单的查询) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了参数信息"; } /** * 2.2.当前接口需要经过权限校验(需包含 角色的查询 与 菜单的查询) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了参数信息"; } /** * 3.1.当前接口需要经过角色校验(需包含admin的角色) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresRoles(value={"admin"}) public String test(){ return "恭喜你,拿到了参数信息"; } /** * 3.2.当前接口需要经过角色与权限的校验(需包含admin的角色,以及角色的查询 或 菜单的查询) * @return */ @RequestMapping(value = "/info",method = RequestMethod.GET) @RequiresRoles(value={"admin"}) @RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR) public String test(){ return "恭喜你,拿到了参数信息"; }
実際の使用プロセスでは、@RequiresPermissions と @RequiresAuthentication を使用するだけで十分です。 前のセクションの最後で、ビジネス モジュールとオペレーションの組み合わせを採用しました。ページとAPIインターフェースの関係を切り離す方法は、Apache Rokuの方法とまったく同じです。ただし、ロールの組み合わせが多すぎて、インターフェイス内でロール名を一意に表現できないため、 @RequiresRoles はできるだけ使用しないようにします (インターフェイスが特定のロールに属していることを指定するのは困難ですが、インターフェイスが特定のビジネス モジュールに属していることを明確に知ることができます) 一部の操作)
次に、操作プロセス全体を確認してみましょう。
しかし、hiroにこれら5つのアノテーションがあるだけでは絶対に十分ではありません。実際の使用プロセスでは、必要に応じて、権限認証に独自のビジネス ロジックを追加し、便宜上、カスタム アノテーションを使用できます。この方法は、Apachehiro だけでなく、Hibernate Validator、SpringMVC などの他の多くのフレームワークにも適用でき、aop でアクセス許可を検証する検証システムを作成することもできますが、これは問題ありません。したがって、カスタム アノテーションには幅広い用途があります。ただし、ここでは hiro に基づいてそれに適したカスタム アノテーションのみを実装します。
アノテーションクラスの定義
/** * 用于认证的接口的注解,组合形式默认是“或”的关系 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Auth { /** * 业务模块 * @return */ String[] module(); /** * 操作类型 */ String[] action(); }
アノテーション処理クラスの定義
/** * Auth注解的操作类 */ public class AuthHandler extends AuthorizingAnnotationHandler { public AuthHandler() { //写入注解 super(Auth.class); } @Override public void assertAuthorized(Annotation a) throws AuthorizationException { if (a instanceof Auth) { Auth annotation = (Auth) a; String[] module = annotation.module(); String[] action = annotation.action(); //1.获取当前主题 Subject subject = this.getSubject(); //2.验证是否包含当前接口的权限有一个通过则通过 boolean hasAtLeastOnePermission = false; for(String m:module){ for(String ac:action){ //使用hutool的字符串工具类 String permission = StrFormatter.format("{}:{}",m,ac); if(subject.isPermitted(permission)){ hasAtLeastOnePermission=true; break; } } } if(!hasAtLeastOnePermission){ throw new AuthorizationException("没有访问此接口的权限"); } } } }
hiroインターセプト処理クラスの定義
/** * 拦截器 */ public class AuthMethodInterceptor extends AuthorizingAnnotationMethodInterceptor { public AuthMethodInterceptor() { super(new AuthHandler()); } public AuthMethodInterceptor(AnnotationResolver resolver) { super(new AuthHandler(), resolver); } @Override public void assertAuthorized(MethodInvocation mi) throws AuthorizationException { // 验证权限 try { ((AuthHandler) this.getHandler()).assertAuthorized(getAnnotation(mi)); } catch (AuthorizationException ae) { if (ae.getCause() == null) { ae.initCause(new AuthorizationException("当前的方法没有通过鉴权: " + mi.getMethod())); } throw ae; } } }
hiroのAOPアスペクトクラス
/** * shiro的aop切面 */ public class AuthAopInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor { public AuthAopInterceptor() { super(); // 添加自定义的注解拦截器 this.methodInterceptors.add(new AuthMethodInterceptor(new SpringAnnotationResolver())); } }
定義hiro のカスタム アノテーション スタートアップ クラス
/** * 启动自定义注解 */ public class ShiroAdvisor extends AuthorizationAttributeSourceAdvisor { public ShiroAdvisor() { // 这里可以添加多个 setAdvice(new AuthAopInterceptor()); } @SuppressWarnings({"unchecked"}) @Override public boolean matches(Method method, Class targetClass) { Method m = method; if (targetClass != null) { try { m = targetClass.getMethod(m.getName(), m.getParameterTypes()); return this.isFrameAnnotation(m); } catch (NoSuchMethodException ignored) { } } return super.matches(method, targetClass); } private boolean isFrameAnnotation(Method method) { return null != AnnotationUtils.findAnnotation(method, Auth.class); } }
全体的な考え方: アノテーション クラス を定義 (ビジネスで使用できる変数を定義) -> アノテーション処理クラス を定義 (アノテーション内の変数を使用してビジネス ロジックを実行)処理中) -> アノテーション インターセプタを定義 - > aop のアスペクト クラスを定義 - > 最後に hiro のカスタム アノテーション有効化クラスを定義します。他のカスタム アノテーションの書き方のアイデアはこれと似ています。
関連する推奨事項:
カスタムアノテーションマッピング thinkPHP21カスタムタグライブラリのインポート方法の詳細説明
以上がhiroをベースにしたカスタムアノテーションの拡張 - 画像とテキストで詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。