注釈認証


問題の説明
権限認証
権限認証はずっと複雑な問題であり,実験という要求が厳しくない製品であれば,権限認証を直接逃れる.
ソフトウェアの设计とプログラミングの実践の実験、バックグラウンドは直接Spring Data RESTを使って、使いやすいのは使いやすいですが、実际のプロジェクトの中で运用することができなくて、直接apiを自动的に生成して、谁が呼び出してもいいです.
ビジネスプロジェクトでは、権限がないとだめです.
注釈
権限については、良い解決策が見つからなかった.ネット上でプロジェクトを検査するまで、機能が簡単で、ユーザーの役割が単一であるため、潘先生は注釈を利用して権限認証を実現する案を提出した.
2つの注釈,AdminOnly注釈は管理者にしか使用できない方法,Anonymous注釈は対外的に認証を必要としないインタフェースであり,その他の非注釈は一般ユーザーに使用される.
サンプルコード
サンプルコードアドレス:auth-annotation-mengyunzhi
開発環境:Java 1.8+Spring Boot 2.1.2.RELEASEインプリメンテーション
ブロッキング
3つの方法に従って、ユーザ権限をブロックし、ブロック+AOPのモードを使用して実現する.
ブロッカーは、AdminOnlyおよびAnonymous注記のないメソッド要求をブロックし、ユーザ認証を行う.
ブロッキングが完了したら、リクエストメソッドを実行します.AOPAdminOnly注釈の前置き通知は、管理者が認証した切面論理を植え込む.Anonymous注釈に対しては何の処理も行わず,匿名ユーザへのアクセスを実現した.
区別する
このように見ると、ブロックはAOPに似ています.それは私たちのこの例がまだAOPの実際の価値を発揮していないからです.AOPこの例よりずっと強いように見えます.
最近デザインモードのエージェントモードを学びました.AOPと密接に関係しています.これからの文章で皆さんと一緒に勉強します.
ブロッキング
ブロッキングを宣言します.3番目のパラメータは、現在ブロックされている方法です.
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
}

基本的な考え方
現在のメソッドにAdminOnlyAnonymousの注記が表示されているかどうかを反射で取得し、なければ一般ユーザ認証を行う.
AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);
Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);

if (adminOnly != null && anonymous != null) {
    return true;
}

boolean result = false;

//       

return result;

パフォーマンスの最適化
はんしゃ
要求されるたびに、ブロッキングを実行し、getMethodAnnotationメソッドを呼び出します.getMethodAnnotationメソッドのソースコード実装を見てみましょう.org.springframework.web.method.HandlerMethodgetMethodAnnotationメソッド:
@Nullable
public A getMethodAnnotation(ClassannotationType) {
return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);
}

このメソッドはまたAnnotatedElementUtils.findMergedAnnotationメソッドを呼び出し、さらにクリックしてみましょう.org.springframework.core.annotation.AnnotatedElementUtilsfindMergedAnnotation実装:
@Nullable
public static A findMergedAnnotation(AnnotatedElement element, ClassannotationType) {
// Shortcut: directly present on the element, with no merging needed?
A annotation = element.getDeclaredAnnotation(annotationType);
if (annotation != null) {
return AnnotationUtils.synthesizeAnnotation(annotation, element);
}
// Exhaustive retrieval of merged annotation attributes...
AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false);
return (attributes != null ? AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element) : null);
}

この方法は、AnnotatedElementインタフェースで宣言されたgetDeclaredAnnotationメソッドを呼び出して注釈取得を行う.AnnotatedElementインタフェース、java反射パケットに存在する:
あまり話さないで、反射して、性能の問題があります!
個人的な理解
同様にJavaであり、GoogleAndroidに対して反射する態度を見ればよい.
以前Google Androidの公式サイトに行ったことがあるのを覚えていますが、公式にはAndroidでフレームを使用することをお勧めしていません.これは深刻な性能問題をもたらす可能性があります.その中には、従来のJavaフレームで大量に使用されている反射を考慮しています.
これは外国の反射に関する文章です.反射はいったいどれだけ遅いですか.How Slow is Reflection in Android?
ユーザがアプリケーションの起動時間を期待する平均値は2sであるという仕様について述べた.NYTimes Android AppGoogleGsonを使用してデータ解析を行います.これは私たちのバックグラウンドで広く使用されています.アリのfastjsonと並んで、非常に人気のあるjsonライブラリです.NYTimesのエンジニアは、Gsonで反射を使用してデータ型を取得することを発見し、アプリケーションの起動時に約700msの遅延が増加した.ActiveAndroidは反射を使って実現されたライブラリで、わざわざGithubをぶらぶらして、4000starは、かなり流行しているオープンソースプロジェクトです!Scribd : 1093ms for call com.activeandroid.ActiveAndroid.initialize . Myntra : 1421ms for call com.activeandroid.ActiveAndroid.initialize .
Data-Binding
顔を殴る?Androidフレームワークの使用はお勧めしないのではないでしょうか.では、なぜGoogleがまたData-Bindingを発売したのでしょうか.
なお、Googleは、サードパーティ製フレームワークの高コストによるパフォーマンスの問題を考慮している.Data-Bindingの利点を見てみると、最も重要なのは、フレームワークが反射を使用しないことであり、ダイナミックコード生成技術を使用して、フレームワークを使用することによって性能の問題が発生しないことです.
作成したコードに直接基づいてオリジナルAndroidのコードを生成するので、パフォーマンスの問題はありません.
ソリューション
ブロッキングで反射を使用する性能の問題を解決するために、SpringBootの設計構想を学び、起動時にすべての反射注釈の読み取りを直接完了し、メモリに格納します.
その後、ブロックごとにメモリから直接読み取り、パフォーマンスを向上させます.
コンテナ起動イベントをリスニングし、コンテナ起動時に以下のコードを実行し、すべてのコントローラとそのメソッドの注釈をスキャンし、条件に合致する場合はHashMapに配置します.
//        Scanner,     filter
ClassPathScanningCandidateComponentProvider scanner =
        new ClassPathScanningCandidateComponentProvider(false);
//       ,      RestController  
scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
//                   
for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) {
    //            
    String name = beanDefinition.getBeanClassName();
    try {
        //          
        Class> clazz = Class.forName(name);
        //       List
        List methodNameList = new ArrayList<>();
        //      (     ,            )       
        for (Method method : clazz.getDeclaredMethods()) {
            //         
            AdminOnly adminOnly = method.getAnnotation(AdminOnly.class);
            Anonymous anonymous = method.getAnnotation(Anonymous.class);
            //         AdminOnly Anonymous  
            if (adminOnly == null && anonymous == null) {
                //    List 
                methodNameList.add(method.getName());
            }
        }
        //    Map 
        AuthAnnotationConfig.getAnnotationsMap().put(clazz, methodNameList);
    } catch (ClassNotFoundException e) {
        logger.error("       ,   ClassNotFoundException  ");
    }
}

ブロッキングの変更
元のブロッキングはこうでした.
AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);
Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);

if (adminOnly != null && anonymous != null) {
    return true;
}

boolean result = false;

//       

return result;

今はこうです.
logger.debug("             ");
Class> clazz = handlerMethod.getBeanType();

logger.debug("            ");
String methodName = handlerMethod.getMethod().getName();

logger.debug("             ");
List authMethodNames = AuthAnnotationConfig.getAnnotationsMap().get(clazz);

logger.debug("  List             ,    ");
if (authMethodNames == null || !authMethodNames.contains(methodName)) {
    return true;
}

logger.debug("      ");
boolean result = false;

//     

return result;

以前は2回反射していましたが、現在はhandlerMethod.getBeanType()handlerMethod.getMethod().getName()が呼び出されています.
この2つの実装を見てみましょう.getBeanType
public Class> getBeanType() {
    return this.beanType;
}
getMethod
public Method getMethod() {
    return this.method;
}

いずれもorg.springframework.web.method.HandlerMethodクラスで直接属性を返し,このHandlerMethodSpringコンテナ起動時に既に構築されたメソッドオブジェクトであり,ブロッキング実行中に反射を呼び出さなかったと推定した.
注記の注記
今は注釈が少ないので、私たちは2行書いて、問題は大きくないと思います.
//         
AdminOnly adminOnly = method.getAnnotation(AdminOnly.class);
Anonymous anonymous = method.getAnnotation(Anonymous.class);

後で認証注釈が多くなったら?
このように,現在の方法がブロックされるか否かを判定する汎用的な注釈があり,AdminOnlyおよびAnonymousはその注釈の機能を継承すべきであり,これにより,後でブロックにブロックされない注釈を追加したい場合に,起動時のスキャン方法を修正する必要がなくなることを期待する.
//       
AdminAuth adminAuth = method.getAnnotation(AdminAuth.class);
Spring Bootのように,注釈に注釈を加え,複合注釈を実現することを期待する.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
}

注釈の作成Javaカスタム注釈が分からない場合は、慕課網に行って関連課程を学ぶことができます:Java注釈-慕課網を全面的に解析します
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminAuth {
}
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),この注釈は方法に表記してもよいし、他の注釈に表記してもよい.@Retention(RetentionPolicy.RUNTIME),この注記は、プログラムの実行中まで保持される.
注釈に注釈を付けるAdminOnly:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@AdminAuth
public @interface AdminOnly {
}
Anonymous:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@AdminAuth
public @interface Anonymous {
}

解析注記
注釈を付けるのは簡単で、どのように注釈を解析するかが重要です.
反射パケット内のMethodクラスが提供するgetAnnotationメソッドを呼び出すと、現在表示されている注釈のみが表示されます.
例:
@AdminOnly
public void test() {
}
getAnnotationAdminOnlyを取得できますが、@AdminOnlyに注記されている@AdminAuthの注記は取得されません.
どのようにして注釈の注釈を取得しますか?
午前中探して、私がこの問題を解決するのはやはり一定の運に頼っていると言わざるを得ません.私が諦めようとしたとき、GoogleSpringFrameworkの注釈ツール類AnnotationUtilsが見つかりました.
ドキュメントを簡単に開く:Class AnnotationUtils-Spring Core Docs
4つ目の方法は私が望んでいることです
このツールクラスを使用すると、メソッドに注記が表示されている注記を直接取得できます.
@AdminOnly
public void test() {
}
AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class);

この方法は、testメソッドで継承された@AdminAuth注釈を取得することができる.
最終コード:
@Component
public class InitAnnotationsConfig implements ApplicationListener {

    //     
    private static final String basePackageName = "com.mengyunzhi.checkApplyOnline";
    //   
    private static final Logger logger = LoggerFactory.getLogger(InitAnnotationsConfig.class);

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //        Scanner,     filter
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        //       ,      RestController  
        scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
        //                   
        for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) {
            //            
            String name = beanDefinition.getBeanClassName();
            try {
                //          
                Class> clazz = Class.forName(name);
                //       List
                List methodNameList = new ArrayList<>();
                //      (     ,            )       
                for (Method method : clazz.getDeclaredMethods()) {
                    //       
                    AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class);
                    //          ,     
                    if (adminAuth == null) {
                        //    List 
                        methodNameList.add(method.getName());
                    }
                }
                //    Map 
                AuthAnnotationConfig.getAnnotationsMap().put(clazz, methodNameList);
            } catch (ClassNotFoundException e) {
                logger.error("       ,   ClassNotFoundException  ");
            }
        }
    }
}

まとめ
問題を解決する新しい方法を学びました.あるフレームワークもあなたが直面した問題に遭遇したことがあるはずです.フレームワークのツールクラスを探してみると、役に立つかもしれません.