<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:SpringBoot 項(xiàng)目鑒權(quán)的 4 種方式說(shuō)下

          共 6425字,需瀏覽 13分鐘

           ·

          2022-05-14 17:07


          來(lái)源:zhenbianshu.github.io/

          • 前言
          • 傳統(tǒng)AOP
            • 實(shí)現(xiàn)
            • 擴(kuò)展
          • Interceptor
            • 實(shí)現(xiàn)
            • 擴(kuò)展
          • ArgumentResolver
            • 實(shí)現(xiàn)
            • 擴(kuò)展
          • Filter
            • 擴(kuò)展
          • 小結(jié)

          文章介紹了spring-boot中實(shí)現(xiàn)通用auth的四種方式,包括 傳統(tǒng)AOP、攔截器、參數(shù)解析器和過(guò)濾器,并提供了對(duì)應(yīng)的實(shí)例代碼,最后簡(jiǎn)單總結(jié)了下他們的執(zhí)行順序。

          前言

          最近一直被無(wú)盡的業(yè)務(wù)需求淹沒(méi),沒(méi)時(shí)間喘息,終于接到一個(gè)能讓我突破代碼舒適區(qū)的活兒,解決它的過(guò)程非常曲折,一度讓我懷疑人生,不過(guò)收獲也很大,代碼方面不明顯,但感覺(jué)自己抹掉了 java、Tomcat、Spring 一直擋在我眼前的一層紗。對(duì)它們的理解上了一個(gè)新的層次。

          好久沒(méi)輸出了,于是挑一個(gè)方面總結(jié)一下,希望在梳理過(guò)程中再了解一些其他的東西。由于 Java 繁榮的生態(tài),下面每一個(gè)模塊都有大量的文章專(zhuān)門(mén)講述。所以我選了另外一個(gè)角度,從實(shí)際問(wèn)題出發(fā),將這些分散的知識(shí)串聯(lián)起來(lái),各位可以作為一個(gè)綜述來(lái)看。各個(gè)模塊的極致詳細(xì)介紹,大家可以去翻官方文檔或看網(wǎng)絡(luò)上的其他博客。

          需求很簡(jiǎn)單清晰,跟產(chǎn)品們提的妖艷需求一點(diǎn)也不一樣:在我們的 web 框架里添加一個(gè)通用的 appkey 白名單校驗(yàn)功能,希望它的擴(kuò)展性更好一些。

          這個(gè) web 框架是部門(mén)前驅(qū)者基于 spring-boot 實(shí)現(xiàn)的,介于業(yè)務(wù)和 Spring 框架之間,做一些偏向于業(yè)務(wù)的通用性功能,如 日志輸出、功能開(kāi)關(guān)、通用參數(shù)解析等。平常是對(duì)業(yè)務(wù)透明的,最近一直忙于把需求做好,代碼寫(xiě)好,甚至從沒(méi)注意過(guò)它的存在。

          傳統(tǒng)AOP

          對(duì)于這種需求,首先想到的當(dāng)然是 Spring-boot 提供的 AOP 接口,只需要在 Controller 方法前添加切點(diǎn),然后再對(duì)切點(diǎn)進(jìn)行處理即可。

          實(shí)現(xiàn)

          其使用步驟如下:

          1. 使用 @Aspect 聲明一下切面類(lèi) WhitelistAspect
          2. 在切面類(lèi)內(nèi)添加一個(gè)切點(diǎn) whitelistPointcut(),為了實(shí)現(xiàn)此切點(diǎn)靈活可裝配的能力,這里不使用 execution 全部攔截,而是添加一個(gè)注解 @Whitelist,被注解的方法才會(huì)校驗(yàn)白名單。
          3. 在切面類(lèi)中使用 spring 的 AOP 注解 @Before 聲明一個(gè)通知方法 checkWhitelist() 在 Controller 方法被執(zhí)行之前校驗(yàn)白名單。

          切面類(lèi)偽代碼如下

          @Aspect
          public?class?WhitelistAspect?{
          ???
          ?@Before(value?=?"whitelistPointcut()?&&?@annotation(whitelist)")
          ?public?void?checkAppkeyWhitelist(JoinPoint?joinPoint,?Whitelist?whitelist)?{
          ?????checkWhitelist();
          ?????//?可使用?joinPoint.getArgs()?獲取Controller方法的參數(shù)
          ?????//?可以使用?whitelist?變量獲取注解參數(shù)
          ?}
          ???
          ???
          ?@Pointcut("@annotation(com.zhenbianshu.Whitelist)")
          ?public?void?whitelistPointCut()?{
          ?}
          }
          1. 在Controller方法上添加 @Whitelist 注解實(shí)現(xiàn)功能。

          擴(kuò)展

          本例中使用了 注解 來(lái)聲明切點(diǎn),并且我實(shí)現(xiàn)了通過(guò)注解參數(shù)來(lái)聲明要校驗(yàn)的白名單,如果之后還需要添加其他白名單的話,如通過(guò) UID 來(lái)校驗(yàn),則可以為此注解添加 uid() 等方法,實(shí)現(xiàn)自定義校驗(yàn)。

          此外,spring 的 AOP 還支持 execution(執(zhí)行方法) 、bean(匹配特定名稱(chēng)的 Bean 對(duì)象的執(zhí)行方法)等切點(diǎn)聲明方法和 @Around(在目標(biāo)函數(shù)執(zhí)行中執(zhí)行) 、@After(方法執(zhí)行后) 等通知方法。

          如此,功能已經(jīng)實(shí)現(xiàn)了,但領(lǐng)導(dǎo)并不滿意=_=,原因是項(xiàng)目中 AOP 用得太多了,都用濫了,建議我換一種方式。嗯,只好搞起。

          Interceptor

          Spring 的 攔截器(Interceptor) 實(shí)現(xiàn)這個(gè)功能也非常合適。顧名思義,攔截器用于在 Controller 內(nèi) Action 被執(zhí)行前通過(guò)一些參數(shù)判斷是否要執(zhí)行此方法,要實(shí)現(xiàn)一個(gè)攔截器,可以實(shí)現(xiàn) Spring 的 HandlerInterceptor 接口。?點(diǎn)擊Java項(xiàng)目分享

          實(shí)現(xiàn)

          實(shí)現(xiàn)步驟如下:

          1. 定義攔截器類(lèi) AppkeyInterceptor 類(lèi)并實(shí)現(xiàn) HandlerInterceptor 接口。
          2. 實(shí)現(xiàn)其 preHandle() 方法;
          3. 在 preHandle 方法內(nèi)通過(guò)注解和參數(shù)判斷是否需要攔截請(qǐng)求,攔截請(qǐng)求時(shí)接口返回 false
          4. 在自定義的 WebMvcConfigurerAdapter 類(lèi)內(nèi)注冊(cè)此攔截器;

          AppkeyInterceptor 類(lèi)如下:

          @Component
          public?class?WhitelistInterceptor?implements?HandlerInterceptor?{

          ????@Override
          ????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)?throws?Exception?{
          ????????Whitelist?whitelist?=?((HandlerMethod)?handler).getMethodAnnotation(Whitelist.class);
          ????????//?whitelist.values();?通過(guò)?request?獲取請(qǐng)求參數(shù),通過(guò)?whitelist?變量獲取注解參數(shù)
          ????????return?true;
          ????}

          ????@Override
          ????public?void?postHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler,?ModelAndView?modelAndView)?throws?Exception?{
          ??//?方法在Controller方法執(zhí)行結(jié)束后執(zhí)行
          ????}

          ????@Override
          ????public?void?afterCompletion(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler,?Exception?ex)?throws?Exception?{
          ??//?在view視圖渲染完成后執(zhí)行
          ????}
          }

          擴(kuò)展

          要啟用 攔截器還要顯式配置它啟用,這里我們使用 WebMvcConfigurerAdapter 對(duì)它進(jìn)行配置。需要注意,繼承它的的 MvcConfiguration 需要在 ComponentScan 路徑下。

          @Configuration
          public?class?MvcConfiguration?extends?WebMvcConfigurerAdapter?{
          ????@Override
          ????public?void?addInterceptors(InterceptorRegistry?registry)?{
          ????????registry.addInterceptor(new?WhitelistInterceptor()).addPathPatterns("/*").order(1);
          ????????//?這里可以配置攔截器啟用的?path?的順序,在有多個(gè)攔截器存在時(shí),任一攔截器返回?false?都會(huì)使后續(xù)的請(qǐng)求方法不再執(zhí)行
          ????}
          }

          還需要注意,攔截器執(zhí)行成功后響應(yīng)碼為 200,但響應(yīng)數(shù)據(jù)為空。

          當(dāng)使用攔截器實(shí)現(xiàn)功能后,領(lǐng)導(dǎo)終于祭出大招了:我們已經(jīng)有一個(gè) Auth 參數(shù)了,appkey 可以從 Auth 參數(shù)里取到,可以把在不在白名單作為 Auth 的一種方式,為什么不在 Auth 時(shí)校驗(yàn)?emmm… 吐血中。我是程序汪 號(hào)主

          ArgumentResolver

          參數(shù)解析器是 Spring 提供的用于解析自定義參數(shù)的工具,我們常用的 @RequestParam 注解就有它的影子,使用它,我們可以將參數(shù)在進(jìn)入Controller Action之前就組合成我們想要的樣子。Spring 會(huì)維護(hù)一個(gè) ResolverList, 在請(qǐng)求到達(dá)時(shí),Spring 發(fā)現(xiàn)有自定義類(lèi)型參數(shù)(非基本類(lèi)型), 會(huì)依次嘗試這些 Resolver,直到有一個(gè) Resolver 能解析需要的參數(shù)。要實(shí)現(xiàn)一個(gè)參數(shù)解析器,需要實(shí)現(xiàn) HandlerMethodArgumentResolver 接口。

          實(shí)現(xiàn)

          1. 定義自定義參數(shù)類(lèi)型 AuthParam,類(lèi)內(nèi)有 appkey 相關(guān)字段;
          2. 定義 AuthParamResolver 并實(shí)現(xiàn) HandlerMethodArgumentResolver 接口;
          3. 實(shí)現(xiàn) supportsParameter() 接口方法將 AuthParam 與 AuthParamResolver 適配起來(lái);
          4. 實(shí)現(xiàn) resolveArgument() 接口方法解析 reqest 對(duì)象生成 AuthParam 對(duì)象,并在此校驗(yàn) AuthParam ,確認(rèn) appkey 是否在白名單內(nèi);
          5. 在 Controller Action 方法上簽名內(nèi)添加 AuthParam 參數(shù)以啟用此 Resolver;

          實(shí)現(xiàn)的 AuthParamResolver 類(lèi)如下:點(diǎn)擊Java項(xiàng)目分享

          @Component
          public?class?AuthParamResolver?implements?HandlerMethodArgumentResolver?{

          ????@Override
          ????public?boolean?supportsParameter(MethodParameter?parameter)?{
          ????????return?parameter.getParameterType().equals(AuthParam.class);
          ????}

          ????@Override
          ????public?Object?resolveArgument(MethodParameter?parameter,?ModelAndViewContainer?mavContainer,?NativeWebRequest?webRequest,?WebDataBinderFactory?binderFactory)?throws?Exception?{
          ????????Whitelist?whitelist?=?parameter.getMethodAnnotation(Whitelist.class);
          ????????//?通過(guò)?webRequest?和?whitelist?校驗(yàn)白名單
          ????????return?new?AuthParam();
          ????}
          }

          擴(kuò)展

          當(dāng)然,使用參數(shù)解析器也需要單獨(dú)配置,我們同樣在 WebMvcConfigurerAdapter內(nèi)配置:

          @Configuration
          public?class?MvcConfiguration?extends?WebMvcConfigurerAdapter?{

          ????@Override
          ????public?void?addArgumentResolvers(List?argumentResolvers)?{
          ????????argumentResolvers.add(new?AuthParamResolver());
          ????}
          }

          這次實(shí)現(xiàn)完了,我還有些不放心,于是在網(wǎng)上查找是否還有其他方式可以實(shí)現(xiàn)此功能,發(fā)現(xiàn)常見(jiàn)的還有 Filter

          Filter

          Filter 并不是 Spring 提供的,它是在 Servlet 規(guī)范中定義的,是 Servlet 容器支持的。被 Filter 過(guò)濾的請(qǐng)求,不會(huì)派發(fā)到 Spring 容器中。它的實(shí)現(xiàn)也比較簡(jiǎn)單,實(shí)現(xiàn) javax.servlet.Filter接口即可。

          由于不在 Spring 容器中,F(xiàn)ilter 獲取不到 Spring 容器的資源,只能使用原生 Java 的 ServletRequest 和 ServletResponse 來(lái)獲取請(qǐng)求參數(shù)。點(diǎn)擊Java項(xiàng)目分享

          另外,在一個(gè) Filter 中要顯示調(diào)用 FilterChain 的 doFilter 方法,不然認(rèn)為請(qǐng)求被攔截。實(shí)現(xiàn)類(lèi)似:?我是程序汪?號(hào)主

          public?class?WhitelistFilter?implements?javax.servlet.Filter?{

          ????@Override
          ????public?void?init(FilterConfig?filterConfig)?throws?ServletException?{
          ??//?初始化后被調(diào)用一次
          ????}

          ????@Override
          ????public?void?doFilter(ServletRequest?request,?ServletResponse?response,?FilterChain?chain)?throws?IOException,?ServletException?{
          ?????//?判斷是否需要攔截
          ???????chain.doFilter(request,?response);?//?請(qǐng)求通過(guò)要顯示調(diào)用
          ????}

          ????@Override
          ????public?void?destroy()?{
          ?????//?被銷(xiāo)毀時(shí)調(diào)用一次
          ????}
          }

          擴(kuò)展

          Filter 也需要顯示配置:

          @Configuration
          public?class?FilterConfiguration?{

          ????@Bean
          ????public?FilterRegistrationBean?someFilterRegistration()?{
          ????????FilterRegistrationBean?registration?=?new?FilterRegistrationBean();
          ????????registration.setFilter(new?WhitelistFilter());
          ????????registration.addUrlPatterns("/*");
          ????????registration.setName("whitelistFilter");
          ????????registration.setOrder(1);?//?設(shè)置過(guò)濾器被調(diào)用的順序
          ????????return?registration;
          ????}
          }

          小結(jié)

          四種實(shí)現(xiàn)方式都有其適合的場(chǎng)景,那么它們之間的調(diào)用順序如何呢?

          Filter 是 Servlet 實(shí)現(xiàn)的,自然是最先被調(diào)用,后續(xù)被調(diào)用的是 Interceptor 被攔截了自然不需要后續(xù)再進(jìn)行處理,然后是 參數(shù)解析器,最后才是 切面的切點(diǎn)。我將四種方式在一個(gè)項(xiàng)目?jī)?nèi)全部實(shí)現(xiàn)后,輸出日志也證明了這個(gè)結(jié)論。

          程序汪資料鏈接

          程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理

          Java項(xiàng)目分享 ?最新整理全集,找項(xiàng)目不累啦 07版

          堪稱(chēng)神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門(mén)到實(shí)戰(zhàn)進(jìn)階

          臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開(kāi)放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開(kāi)放下載!

          字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開(kāi)放下載!


          歡迎添加程序汪個(gè)人微信 itwang009? 進(jìn)粉絲群或圍觀朋友


          瀏覽 28
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费观看欧美成人网站 | 香蕉视频黄在线观看 | 69视频在线播放 | 一级A片免费观看 | 国产精品福利在线观看 |