成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

注解認(rèn)證

fancyLuo / 1677人閱讀

摘要:攔截器攔截下那些沒(méi)有與注解標(biāo)注的方法請(qǐng)求,并進(jìn)行用戶認(rèn)證。直接根據(jù)編寫(xiě)的代碼生成原生的代碼,所以不會(huì)存在任何性能問(wèn)題解決方案為了解決攔截器中使用反射的性能問(wèn)題,我們學(xué)習(xí)的設(shè)計(jì)思路,在啟動(dòng)時(shí)直接完成所有反射注解的讀取,存入內(nèi)存。

問(wèn)題描述 權(quán)限認(rèn)證

權(quán)限認(rèn)證一直是比較復(fù)雜的問(wèn)題,如果是實(shí)驗(yàn)這種要求不嚴(yán)格的產(chǎn)品,直接逃避掉權(quán)限認(rèn)證。

軟件設(shè)計(jì)與編程實(shí)踐的實(shí)驗(yàn),后臺(tái)直接用Spring Data REST,好使是好使,但是不能在實(shí)際項(xiàng)目中運(yùn)用,直接把api自動(dòng)生成了,誰(shuí)調(diào)用都行。

在商業(yè)項(xiàng)目中,沒(méi)有權(quán)限是不行的。

注解

關(guān)于權(quán)限,一直沒(méi)有找到很好的解決方案。直到網(wǎng)上送檢項(xiàng)目,因功能簡(jiǎn)單,且用戶角色單一,潘老師提出了利用注解實(shí)現(xiàn)權(quán)限認(rèn)證的方案。

兩個(gè)注解,AdminOnly標(biāo)注只能給管理員用的方法,Anonymous標(biāo)注對(duì)外的無(wú)需認(rèn)證的接口,其他的未標(biāo)注的是給普通用戶使用的。

示例代碼

示例代碼地址:auth-annotation - mengyunzhi

開(kāi)發(fā)環(huán)境:Java 1.8 + Spring Boot 2.1.2.RELEASE

實(shí)現(xiàn) 攔截器

根據(jù)三類方法,對(duì)用戶權(quán)限進(jìn)行攔截,使用攔截器 + AOP的模式實(shí)現(xiàn)。

攔截器攔截下那些沒(méi)有AdminOnlyAnonymous注解標(biāo)注的方法請(qǐng)求,并進(jìn)行用戶認(rèn)證。

攔截器過(guò)完之后,去執(zhí)行請(qǐng)求方法。

AOPAdminOnly注解的前置通知,植入一段管理員認(rèn)證的切面邏輯。

對(duì)Anonymous注解不進(jìn)行任何處理,實(shí)現(xiàn)了匿名用戶的訪問(wèn)。

區(qū)別

這樣一看,攔截器就和AOP很像。那是因?yàn)槲覀冞@個(gè)例子還遠(yuǎn)沒(méi)有發(fā)揮出AOP的實(shí)際價(jià)值。

AOP比這個(gè)例子中看上去,強(qiáng)大得多。

最近學(xué)習(xí)了設(shè)計(jì)模式中的代理模式,與AOP息息相關(guān),我會(huì)在以后的文章中與大家一同學(xué)習(xí)。

攔截器

聲明攔截器,第三個(gè)參數(shù)就是當(dāng)前被攔截的方法。

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
}

基本思路

利用反射獲取當(dāng)前方法中是否標(biāo)注有AdminOnlyAnonymous注解,如果沒(méi)有,則進(jìn)行普通用戶認(rèn)證。

AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);
Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);

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

boolean result = false;

// 進(jìn)行用戶認(rèn)證

return result;
性能優(yōu)化 反射

每次請(qǐng)求,都要走攔截器,調(diào)用getMethodAnnotation方法。

我們?nèi)タ纯?b>getMethodAnnotation方法的源碼實(shí)現(xiàn):

org.springframework.web.method.HandlerMethod中的getMethodAnnotation方法:

@Nullable
public  A getMethodAnnotation(Class annotationType) {
    return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);
}

該方法又調(diào)用了AnnotatedElementUtils.findMergedAnnotation方法,我們?cè)冱c(diǎn)進(jìn)去看看:

org.springframework.core.annotation.AnnotatedElementUtils中的findMergedAnnotation實(shí)現(xiàn):

@Nullable
public static  A findMergedAnnotation(AnnotatedElement element, Class annotationType) {
    // 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);
}

該方法是調(diào)用AnnotatedElement接口中聲明的getDeclaredAnnotation方法進(jìn)行注解獲?。?/p>

AnnotatedElement接口,存在于java反射包中:

話不多說(shuō),反射,就存在性能問(wèn)題!

個(gè)人理解

同樣是Java,我們看看Google對(duì)于Android反射的態(tài)度就好了。

我記得之前我去過(guò)Google Android的官網(wǎng),官方不推薦在Android中使用框架,這可能帶來(lái)嚴(yán)重的性能問(wèn)題,其中就有考慮到傳統(tǒng)Java框架中大量使用的反射。

這是國(guó)外一篇關(guān)于反射的文章,反射到底有多慢?:How Slow is Reflection in Android?

文中提到了一項(xiàng)規(guī)范,即用戶期待應(yīng)用的啟動(dòng)時(shí)間的平均值為2s。

NYTimes Android App中使用GoogleGson進(jìn)行數(shù)據(jù)解析,這個(gè)在我們后臺(tái)使用的還是挺廣泛的,和阿里的fastjson齊名,都是非?;鸬?b>json庫(kù)。

NYTimes的工程師發(fā)現(xiàn)Gson中使用反射來(lái)獲取數(shù)據(jù)類型,導(dǎo)致應(yīng)用啟動(dòng)時(shí)增加了大約700ms的延遲。

ActiveAndroid是一個(gè)使用反射實(shí)現(xiàn)的庫(kù),特意去Github逛了一手,4000star,這是相當(dāng)流行的開(kāi)源項(xiàng)目了!

Scribd1093ms for call com.activeandroid.ActiveAndroid.initialize。

Myntra1421ms for call com.activeandroid.ActiveAndroid.initialize。

Data-Binding

打臉?Android不是不推薦使用框架嗎?那為什么Google又推出了Data-Binding呢?

注意,Google考慮的是第三方框架高額的開(kāi)銷而引發(fā)性能問(wèn)題。

去看看Data-Binding的優(yōu)點(diǎn),最重要的一條就是該框架不使用反射,使用動(dòng)態(tài)代碼生成技術(shù),不會(huì)因?yàn)槭褂迷摽蚣芏斐尚阅軉?wèn)題。

直接根據(jù)編寫(xiě)的代碼生成原生Android的代碼,所以不會(huì)存在任何性能問(wèn)題!

解決方案

為了解決攔截器中使用反射的性能問(wèn)題,我們學(xué)習(xí)SpringBoot的設(shè)計(jì)思路,在啟動(dòng)時(shí)直接完成所有反射注解的讀取,存入內(nèi)存。

之后每次攔截器直接從內(nèi)存中讀取,提高性能。

監(jiān)聽(tīng)容器啟動(dòng)事件,在容器啟動(dòng)時(shí)執(zhí)行以下代碼,掃描所有控制器,及其方法上的注解,如果符合條件,則放到HashMap中。

// 初始化組件掃描Scanner,禁用默認(rèn)的filter
ClassPathScanningCandidateComponentProvider scanner =
        new ClassPathScanningCandidateComponentProvider(false);
// 添加過(guò)濾條件,要求組件上有RestController注解
scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
// 在當(dāng)前項(xiàng)目包下掃描所有符合條件的組件
for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) {
    // 獲取當(dāng)前組件的完整類名
    String name = beanDefinition.getBeanClassName();
    try {
        // 利用反射獲取相關(guān)類
        Class clazz = Class.forName(name);
        // 初始化方法名List
        List methodNameList = new ArrayList<>();
        // 獲取當(dāng)前類(不包括父類,所以要求控制器間不能繼承)中所有聲明方法
        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("掃描注解配置時(shí),發(fā)生了ClassNotFoundException異常");
    }
}
攔截器修改

原來(lái)的攔截器是這樣的:

AdminOnly adminOnly = handlerMethod.getMethodAnnotation(AdminOnly.class);
Anonymous anonymous = handlerMethod.getMethodAnnotation(Anonymous.class);

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

boolean result = false;

// 進(jìn)行用戶認(rèn)證

return result;

現(xiàn)在是這樣的:

logger.debug("獲取當(dāng)前請(qǐng)求方法的組件類型");
Class clazz = handlerMethod.getBeanType();

logger.debug("獲取當(dāng)前處理請(qǐng)求的方法名");
String methodName = handlerMethod.getMethod().getName();

logger.debug("獲取當(dāng)前類中需認(rèn)證的方法名");
List authMethodNames = AuthAnnotationConfig.getAnnotationsMap().get(clazz);

logger.debug("如果List為空或者不包含在認(rèn)證方法中,釋放攔截");
if (authMethodNames == null || !authMethodNames.contains(methodName)) {
    return true;
}

logger.debug("進(jìn)行用戶認(rèn)證");
boolean result = false;

// 用戶認(rèn)證

return result;

之前用了兩次反射,現(xiàn)在是調(diào)用了handlerMethod.getBeanType()handlerMethod.getMethod().getName()

再去看看這兩個(gè)的實(shí)現(xiàn):

getBeanType

public Class getBeanType() {
    return this.beanType;
}

getMethod

public Method getMethod() {
    return this.method;
}

都是在org.springframework.web.method.HandlerMethod類中直接返回屬性,我們推斷:這個(gè)HandlerMethod,應(yīng)該是Spring在容器啟動(dòng)時(shí)就已經(jīng)構(gòu)造好的方法對(duì)象,在攔截器執(zhí)行期間,沒(méi)有調(diào)用反射。

注解的注解

現(xiàn)在是注解少,我們寫(xiě)兩行,感覺(jué)問(wèn)題不大:

// 獲取方法上的注解
AdminOnly adminOnly = method.getAnnotation(AdminOnly.class);
Anonymous anonymous = method.getAnnotation(Anonymous.class);

以后如果認(rèn)證注解多了呢?

我們期待這樣,有一個(gè)通用的注解來(lái)判定當(dāng)前方法是否要被攔截,而AdminOnlyAnonymous應(yīng)繼承該注解的功能,這樣以后再想添加不被攔截器攔截的注解,就不需要修改啟動(dòng)時(shí)掃描的方法了。

// 獲取授權(quán)注解
AdminAuth adminAuth = method.getAnnotation(AdminAuth.class);

我們期望像Spring Boot一樣,在注解上加注解,實(shí)現(xiàn)復(fù)合注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
}
構(gòu)造注解

如果對(duì)Java自定義注解不了解,可以去慕課網(wǎng)學(xué)習(xí)相關(guān)課程:全面解析Java注解 - 慕課網(wǎng)

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminAuth {
}

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),該注解可以標(biāo)注在方法上,也可以標(biāo)注在其他注解上。

@Retention(RetentionPolicy.RUNTIME),該注解一直保留到程序運(yùn)行期間。

給注解加注解

AdminOnly:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@AdminAuth
public @interface AdminOnly {
}

Anonymous:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@AdminAuth
public @interface Anonymous {
}
解析注解

加注解很簡(jiǎn)單,重要的是怎么解析該注解。

調(diào)用反射包中的Method類提供的getAnnotation方法,只會(huì)告訴我們當(dāng)前標(biāo)注了什么注解。

比如:

@AdminOnly
public void test() {
}

我們可以通過(guò)getAnnotation獲取AdminOnly,但是獲取不到注解在@AdminOnly上的@AdminAuth注解。

怎么獲取注解的注解呢?

找了一上午,不得不說(shuō),我解決這個(gè)問(wèn)題還是靠一定的運(yùn)氣的。在我要放棄的時(shí)候,在Google搜出了SpringFramework中的注解工具類AnnotationUtils

隨手打開(kāi)文檔:Class AnnotationUtils - Spring Core Docs

第四個(gè)方法就是我想要的:

使用該工具類,能直接獲取方法上標(biāo)注在注解上的注解:

@AdminOnly
public void test() {
}
AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class);

這種方法能獲取到標(biāo)注在test方法上繼承而來(lái)的@AdminAuth注解。

最終代碼:

@Component
public class InitAnnotationsConfig implements ApplicationListener {

    // 基礎(chǔ)包名
    private static final String basePackageName = "com.mengyunzhi.checkApplyOnline";
    // 日志
    private static final Logger logger = LoggerFactory.getLogger(InitAnnotationsConfig.class);

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 初始化組件掃描Scanner,禁用默認(rèn)的filter
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);
        // 添加過(guò)濾條件,要求組件上有RestController注解
        scanner.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
        // 在當(dāng)前項(xiàng)目包下掃描所有符合條件的組件
        for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackageName)) {
            // 獲取當(dāng)前組件的完整類名
            String name = beanDefinition.getBeanClassName();
            try {
                // 利用反射獲取相關(guān)類
                Class clazz = Class.forName(name);
                // 初始化方法名List
                List methodNameList = new ArrayList<>();
                // 獲取當(dāng)前類(不包括父類,所以要求控制器間不能繼承)中所有聲明方法
                for (Method method : clazz.getDeclaredMethods()) {
                    // 獲取授權(quán)注解
                    AdminAuth adminAuth = AnnotationUtils.getAnnotation(method, AdminAuth.class);
                    // 如果該方法不被授權(quán),則需要認(rèn)證
                    if (adminAuth == null) {
                        // 添加到List中
                        methodNameList.add(method.getName());
                    }
                }
                // 添加到Map中
                AuthAnnotationConfig.getAnnotationsMap().put(clazz, methodNameList);
            } catch (ClassNotFoundException e) {
                logger.error("掃描注解配置時(shí),發(fā)生了ClassNotFoundException異常");
            }
        }
    }
}
總結(jié)

學(xué)會(huì)了一個(gè)解決問(wèn)題的新辦法:某個(gè)框架應(yīng)該也遇到過(guò)你所遇到的問(wèn)題,去找找框架中的工具類,這可能會(huì)很有幫助。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://hztianpu.com/yun/73201.html

相關(guān)文章

  • 基于spring-security-oauth2實(shí)現(xiàn)單點(diǎn)登錄(持續(xù)更新)

    摘要:認(rèn)證服務(wù)器和瀏覽器控制臺(tái)也沒(méi)有報(bào)錯(cuò)信息。這里簡(jiǎn)單介紹下如何查閱源碼,首先全局搜索自己的配置因?yàn)檫@個(gè)地址是認(rèn)證服務(wù)器請(qǐng)求授權(quán)的,所以,請(qǐng)求認(rèn)證的過(guò)濾器肯定包含他。未完待續(xù),下一篇介紹資源服務(wù)器和認(rèn)證服務(wù)器的集成。 基于spring-security-oauth2-實(shí)現(xiàn)單點(diǎn)登錄 文章代碼地址:鏈接描述可以下載直接運(yùn)行,基于springboot2.1.5,springcloud Green...

    妤鋒シ 評(píng)論0 收藏0
  • Shiro【授權(quán)過(guò)濾器、與ehcache整合、驗(yàn)證碼、記住我】

    摘要:為了達(dá)到很好的效果,我們使用來(lái)對(duì)的緩存進(jìn)行管理配置會(huì)話管理器,對(duì)會(huì)話時(shí)間進(jìn)行控制手動(dòng)清空緩存由于驗(yàn)證用戶名和密碼之前,一般需要驗(yàn)證驗(yàn)證碼的。 前言 本文主要講解的知識(shí)點(diǎn)有以下: Shiro授權(quán)過(guò)濾器使用 Shiro緩存 與Ehcache整合 Shiro應(yīng)用->實(shí)現(xiàn)驗(yàn)證碼功能 記住我功能 一、授權(quán)過(guò)濾器測(cè)試 我們的授權(quán)過(guò)濾器使用的是permissionsAuthorization...

    K_B_Z 評(píng)論0 收藏0
  • 基于shiro的自定義注解的擴(kuò)展

    摘要:的自身注解的用法。所以自定義注解的作用很廣。但是在這里,我僅僅基于的來(lái)實(shí)現(xiàn)適用于它的自定義注解。其他的自定義的注解的編寫(xiě)思路和這個(gè)也是類似的。 基于shiro的自定義注解的擴(kuò)展 根據(jù)我的上一篇文章,權(quán)限設(shè)計(jì)的雜談中,涉及到了有關(guān)于前后端分離中,頁(yè)面和api接口斷開(kāi)表與表層面的關(guān)聯(lián),另辟蹊徑從其他角度找到方式進(jìn)行關(guān)聯(lián)。這里我們主要采取了shiro的自定義注解的方案。本篇文章主要解決以下的...

    YuboonaZhang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<