溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

SpringBoot快速設(shè)置攔截器并實(shí)現(xiàn)權(quán)限驗(yàn)證的方法

發(fā)布時(shí)間:2020-09-01 18:28:17 來(lái)源:腳本之家 閱讀:176 作者:唐影若凡 欄目:編程語(yǔ)言

一、概述

攔截器的使用場(chǎng)景越來(lái)越多,尤其是面向切片編程流行之后。那通常攔截器可以做什么呢?

之前我們?cè)贏gent介紹中,提到過(guò)統(tǒng)計(jì)函數(shù)的調(diào)用耗時(shí)。這個(gè)思路其實(shí)和AOP的環(huán)繞增強(qiáng)如出一轍。

那一般來(lái)說(shuō),場(chǎng)景如下:

  1. 函數(shù)增強(qiáng):比如對(duì)一個(gè)函數(shù)進(jìn)行參數(shù)檢查,或者結(jié)果過(guò)濾等。甚至可以對(duì)函數(shù)就行權(quán)限認(rèn)證。
  2. 性能監(jiān)控:統(tǒng)計(jì)函數(shù)性能。
  3. 日志打點(diǎn):比如在用戶登錄函數(shù)之前,打點(diǎn)統(tǒng)計(jì)PV等信息。

以及其他等等。

二、Spring的攔截器

無(wú)論是SpringMVC或者SpringBoot中,關(guān)于攔截器不得不提:
org.springframework.web.servlet.handler.HandlerInterceptorAdapter

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

  // 在目標(biāo)方法執(zhí)行前執(zhí)行
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return true;
  }

  // 在目標(biāo)方法執(zhí)行后執(zhí)行,但在請(qǐng)求返回前,我們?nèi)匀豢梢詫?duì) ModelAndView進(jìn)行修改
  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 
             throws Exception {}

  // 在請(qǐng)求已經(jīng)返回之后執(zhí)行
  @Override
  public void afterCompletion(
      HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
      throws Exception {}

  // 用來(lái)處理異步請(qǐng)求, 當(dāng)Controller中有異步請(qǐng)求方法的時(shí)候會(huì)觸發(fā)該方法
  @Override
  public void afterConcurrentHandlingStarted(
      HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {}
}

三、實(shí)現(xiàn)一個(gè)用于驗(yàn)證簡(jiǎn)單權(quán)限的攔截器

1、自定義一個(gè)權(quán)限注解 @Auth

@Inherited
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Auth {
  String user() default "";
}
  1. @Inherited:在使用此自定義注解時(shí),如果注解在類上面時(shí),子類會(huì)自動(dòng)繼承此注解,否則,子類不會(huì)繼承此注解。這里一定要記住,使用Inherited聲明出來(lái)的注解,只有在類上使用時(shí)才會(huì)有效,對(duì)方法,屬性等其他無(wú)效。
  2. @Target:表示此注解可以放置的位置。常見的位置有:TYPE=枚舉或注解上,F(xiàn)IELD=字段上,METHOD=方法上,PARAMETER=函數(shù)形參列表中,CONSTRUCTOR=構(gòu)造函數(shù)上,LOCAL_VARIABLE=局部變量上 等等其他位置。
  3. @Retention:此注解的生命周期。常見的有:SOURCE=源碼時(shí)期;CLASS=字節(jié)碼時(shí)期(已編譯);RUNTIME=運(yùn)行時(shí)期,通常是用這個(gè)的時(shí)候要多。
  4. @Documentd:生成注解文檔。

2、在Controller的方法上添加注解

上一步添加完注解后,之后要在你所使用的方法上添加相關(guān)注解,如下。

@RestController
@EnableAutoConfiguration
public class DemoController {

  @Auth(user = "admin")
  @RequestMapping(value = "/hello", method = RequestMethod.GET)
  public String sayHello() {
    return "hello world.";
  }
}

3、實(shí)現(xiàn)攔截器功能

需求:我們?cè)谟脩敉ㄟ^(guò)/hello這個(gè)URI訪問(wèn)時(shí),對(duì)其進(jìn)行驗(yàn)證,如果為admin則放行,否則拒絕,假設(shè)用戶的身份在URL參數(shù)中。

思路:因此我們要在執(zhí)行sayHello()之前,對(duì)用戶做出驗(yàn)證。如果其身份與注解中的身份相同,則放行。因此我們要在preHandle()中做文章。

難點(diǎn):我們?cè)趺茨玫紺ontroller 方法上的@Auth這個(gè)注解呢?看PreHandle()的三個(gè)參數(shù),貌似也沒(méi)有哪個(gè)可以提供Controller類中的注解。

其實(shí),第三個(gè)參數(shù)handler,一般情況下其類型為:org.springframework.web.method.HandlerMethod類型,而這里面含有注解的相關(guān)信息。

為什么這么說(shuō)呢?

在SpringBoot中,注解的默認(rèn)類型為函數(shù)級(jí),而在SpringMVC其默認(rèn)類型為Controller對(duì)象級(jí)別。

因此,如果在SpringMVC中需要在dispatcher-servlet.xml中配置:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>,這樣其類型才為HandlerMethod。

我們看下具體實(shí)現(xiàn)邏輯:

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle");
    if (!handler.getClass().isAssignableFrom(HandlerMethod.class)) {
      System.out.println("cat cast handler to HandlerMethod.class");
      return true;
    }
    // 獲取注解
    Auth auth = ((HandlerMethod) handler).getMethod().getAnnotation(Auth.class);
    if (auth == null) {
      System.out.println("cant find @Auth in this uri:" + request.getRequestURI());
      return true;
    }
    // 從參數(shù)中取出用戶身份并驗(yàn)證
    String admin = auth.user();
    if (!admin.equals(request.getParameter("user"))) {
      System.out.println("permission denied");
      response.setStatus(403);
      return false;
    }
    return true;
  }

其實(shí)實(shí)現(xiàn)邏輯就兩點(diǎn):從參數(shù)中取出身份,和注解中的進(jìn)行比對(duì)。

4、配置攔截器

那怎么讓剛才的這個(gè)攔截器生效呢?

這個(gè)時(shí)候,需要我們配置:WebMvcConfigurerAdapter

具體實(shí)現(xiàn)如下:

@Configuration
public class ConfigAdapter extends WebMvcConfigurerAdapter {
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/hello");
  }
}

注意:這里有兩點(diǎn)需要注意,一個(gè)是@Configuration這個(gè)注解,這樣才能讓SpringBoot服務(wù)發(fā)現(xiàn)這個(gè)配置;另一個(gè)是配置匹配項(xiàng),這里是對(duì)"/hello"這個(gè)進(jìn)行攔截。("/**"是對(duì)所有的訪問(wèn)攔截)

四、運(yùn)行

訪問(wèn) http://127.0.0.1:8080/hello?user=admin就可以看到結(jié)果啦。

本文中的代碼詳見:https://github.com/hawkingfoo/springboot-interceptor 

 以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI