目錄和概述
概述
- 內容:攔截器的使用很簡單,定義一個自己的攔截器,向配置中添加一下就可以使用。為了方便,之后又引入了注解。本文就將用簡潔的代碼構建一個springboot的攔截器。
- 假設需求:訪問項目的controller是都要進行"token驗證",除了某些像登錄之類的方法。
- 項目結構:
- TokenInterceptor.java 自定義攔截器
- InterceptorConfig.java 添加攔截器進入項目
- NoNeedToken.java 自定義注解
- TestController.java 測試接口
目錄
1、自定義攔截器
在 TokenInterceptor.java 中輸入以下代碼,以下的代碼將生成一個在請求到達controller前進行攔截的攔截器import com.alibaba.fastjson.JSONObject;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
@Component
public class TokenInterceptor implements HandlerInterceptor {
// 假設現在的token有如下數據
List<String> tokenList = Arrays.asList("111", "222", "333");
// 這個方法是在訪問接口之前執行的,我們只需要在這里寫驗證登陸狀態的業務邏輯,就可以在用戶調用指定接口之前驗證登陸狀態了
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 設置返回為json格式,使用UTF-8
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
String token = request.getHeader("token");
PrintWriter out;
// 之后寫你的判斷邏輯:return true是通過攔截器,可以繼續訪問controller,return false是不通過
if (token == null || !tokenList.contains(token)) {
// 如果失敗了返回{state:"false", msg:"token is null or wrong"}
JSONObject res = new JSONObject();
res.put("state","false");
res.put("msg","token is null or wrong");
out = response.getWriter();
out.append(res.toString());
return false;
}
// 否則返回true 進入controller
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
2、載入攔截器
將以下代碼寫在InterceptorConfig.java中 ,使用addInterceptors方法將我們定義的攔截器添加進入項目中import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Resource
TokenInterceptor tokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 設置所有的路徑都要進行攔截,除了/test/login
registry.addInterceptor(tokenInterceptor).addPathPatterns("/**")
.excludePathPatterns("/test/login");
}
}
3、測試成功
將如下代碼到TestController中,用於測試攔截器效果import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
// canload用於檢測攔截器是否正常工作
@RequestMapping("/test")
public class TestController {
@RequestMapping(value = "/canload")
public Map<String, Object> canLoad(){
Map<String, Object> res = new HashMap<>();
res.put("state", "ok");
res.put("msg", "you can load");
return res;
}
// login模擬登錄時token返回
@RequestMapping(value = "/login")
public String login(){
return "111";
}
}
可以看到如下結果
①直接登錄成功
②直接訪問canload失敗
③使用postman,帶上token進行訪問成功
4、使用注解(Annotation)進行選擇
你也可以使用注解來代替第三步配置中的excludePathPatterns(), 來實現對於指定方法和controller的免token訪問,新建一個NoNeedToken.java注解,在攔截器里判斷注解存在時,直接return true放行NoNeedToken.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE}) //注解的范圍是類、接口、枚舉的方法上
@Retention(RetentionPolicy.RUNTIME)//被虛擬機保存,可用反射機制讀取
public @interface NoNeedToken {
}
響應的更改自定義攔截器的 preHandle方法,和攔截器配置
① 自定義攔截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
String token = request.getHeader("token");
PrintWriter out;
// 對於注解的判斷---------------------------------------------------
HandlerMethod handlerMethod = (HandlerMethod) handler;
if(handlerMethod.getMethodAnnotation(NoNeedToken.class)!=null || handlerMethod.getBeanType().isAnnotationPresent(NoNeedToken.class)){
// 如果自己擁有NoNeedToken標注或者所屬的class擁有NoNeedToken 就直接放行
return true;
}
//------------------------------------------------------------------
if (token == null || !tokenList.contains(token)) {
JSONObject res = new JSONObject();
res.put("state","false");
res.put("msg","token is null or wrong");
out = response.getWriter();
out.append(res.toString());
return false;
}
return true;
}
② 去除攔截器配置中的去除項
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
// .excludePathPatterns("/test/login");
}
③ 在測試的login方法上添加注釋
@RequestMapping(value = "/login")
@NoNeedToken
public String login(){
return "111";
}
修改完成之后效果和上面相同
5、常見問題
1.這個自定義注解的哪些@Target,@Retention,@Documented是什么意思
https://blog.csdn.net/zt15732625878/article/details/100061528,這篇文章寫的很詳細了
2.為什么我的注解加了,還是被攔截了
一般出現這種情況是類與方法的獲取注解方式不同,在本文中作者使用了下面的一句話來表述
handlerMethod.getMethodAnnotation(NoNeedToken.class)!=null || handlerMethod.getBeanType().isAnnotationPresent(NoNeedToken.class)
其中handlerMethod.getMethodAnnotation(NoNeedToken.class)!=null
是對於方法的
而handlerMethod.getBeanType().isAnnotationPresent(NoNeedToken.class)
是對於類的
結語:希望對大家有幫助