首頁 > 軟體

SpringBoot登入驗證token攔截器的實現

2022-07-07 14:03:52

使用者存取介面驗證,如果使用者沒有登入,則不讓他存取除登入外的任何介面。

實現思路:

1.前端登入,後端建立token(通過JWT這個依賴),返給前端

2.前端存取其他介面,傳遞token,後端判斷token存在以或失效

3.失效或不存在,則返回失效提示,前端根據介面返回的失效提示,讓其跳轉到登入介面

註解定義

定義2個註解,1個用於任何介面都能存取,另外一個用於需要登入才能存取

呼叫都通過註解

package com.example.etf.story.tools;
 
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 PassToken {
    boolean required() default true;
}

登入才能通過

package com.example.etf.story.tools;
 
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 UserLoginToken {
    boolean required() default true;
}

註解的作用說明

@Target代表此註解,能@到哪些程式碼上

@Target:註解的作用目標

  • @Target(ElementType.TYPE)——介面、類、列舉、註解
  • @Target(ElementType.FIELD)——欄位、列舉的常數
  • @Target(ElementType.METHOD)——方法
  • @Target(ElementType.PARAMETER)——方法引數
  • @Target(ElementType.CONSTRUCTOR) ——建構函式
  • @Target(ElementType.LOCAL_VARIABLE)——區域性變數
  • @Target(ElementType.ANNOTATION_TYPE)——註解
  • @Target(ElementType.PACKAGE)——包

@Retention:註解的保留位置

  • RetentionPolicy.SOURCE:這種型別的Annotations只在原始碼級別保留,編譯時就會被忽略,在class位元組碼檔案中不包含。
  • RetentionPolicy.CLASS:這種型別的Annotations編譯時被保留,預設的保留策略,在class檔案中存在,但JVM將會忽略,執行時無法獲得。
  • RetentionPolicy.RUNTIME:這種型別的Annotations將被JVM保留,所以他們能在執行時被JVM或其他使用反射機制的程式碼所讀取和使用。
  • @Document:說明該註解將被包含在javadoc
  • @Inherited:說明子類可以繼承父類別中的該註解

token生成與驗證

傳送門

然後springBoot攔截器驗證token

攔截器定義

攔截器設定定義

攔截器攔截,除了登入和傳送簡訊,不攔截,其他都攔截

package com.example.etf.story.tools;
 
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 MyMvcConfig implements WebMvcConfigurer {
    @Resource
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //註冊自己的攔截器,並設定攔截的請求路徑
        //addPathPatterns為攔截此請求路徑的請求
        //excludePathPatterns為不攔截此路徑的請求
        registry.addInterceptor(loginInterceptor).addPathPatterns("/story/*").excludePathPatterns("/story/sendSMS")
                .excludePathPatterns("/story/signOrRegister");
    }
}

攔截的時候,呼叫的方法,給誰通過

其中service查詢資料庫,有沒有使用者,的方法要自己寫

攔截器的方法執行類

package com.example.etf.story.tools;
 
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
 
import com.example.etf.story.dao.R;
import com.example.etf.story.paramer.UserInfoParam;
import com.example.etf.story.service.TestClientService;
import com.example.etf.story.service.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
 
@Slf4j
@Component
public class LoginInterceptor extends R implements HandlerInterceptor {
    /**
     * 目標方法執行前
     * 該方法在控制器處理請求方法前執行,其返回值表示是否中斷後續操作
     * 返回 true 表示繼續向下執行,返回 false 表示中斷後續操作
     *
     * @return
     */
    @Resource
    private TestClientService testClientService;
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");// 從 http 請求頭中取出 token
        // 如果不是對映到方法直接通過
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
 
        //檢查方法是否有passtoken註解,有則跳過認證,直接通過
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //檢查有沒有需要使用者許可權的註解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 執行認證
                if (token == null) {
                    throw new RuntimeException("無token,請重新登入");
                }
                // 獲取 token 中的 user id
                String phone;
                try {
                    phone = JWT.decode(token).getClaim("phone").asString();
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("token不正確,請不要通過非法手段建立token");
                }
                //查詢資料庫,看看是否存在此使用者,方法要自己寫
                UserInfoParam userInfoParam = testClientService.selectUserByPhone(phone);
                if (userInfoParam == null) {
                    throw new RuntimeException("使用者不存在,請重新登入");
                }
 
                // 驗證 token
                if (TokenUtils.verify(token)) {
                    return true;
                } else {
                    throw new RuntimeException("token過期或不正確,請重新登入");
                }
 
            }
        }
          throw new RuntimeException("沒有許可權註解一律不通過");
    }
 
 
 
 
        /**
         * 目標方法執行後
         * 該方法在控制器處理請求方法呼叫之後、解析檢視之前執行
         * 可以通過此方法對請求域中的模型和檢視做進一步修改
         */
        @Override
        public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView
        modelAndView) throws Exception {
            System.out.println("postHandle執行{}");
 
        }
        /**
         * 頁面渲染後
         * 該方法在檢視渲染結束後執行
         * 可以通過此方法實現資源清理、記錄紀錄檔資訊等工作
         */
        @Override
        public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception
        ex) throws Exception {
            System.out.println("afterCompletion執行異常");
 
        }
 
}

註解使用

在controller層加入註解進行測試

返回值-全域性異常類定義

加入全域性,異常類,這樣當異常,會返回你所指定的異常

package com.example.etf.story.tools;
 
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
 
 
@ControllerAdvice
public class GloablExceptionHandler {
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e) {
        String msg = e.getMessage();
        if (msg == null || msg.equals("")) {
            msg = "伺服器出錯";
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("message", msg);
        jsonObject.put("status",500)
        return jsonObject;
    }
}

各種測試

不傳token

成功

製造可行的假token

我們測試一下加token後的

 因為資料庫裡,我沒有插入,所以不存在,我們在隨便寫個token

偽造token測試

我們在試試

程式設計師使用:方法不加註解,測試

程式設計師使用:加上,呼叫通過,註解

 我們試試,加上通過註解

拓展:從請求中獲取token

我們在試試從中獲取token

參考文章:

SpringBoot整合JWT實現token驗證 - 簡書

springboot對請求的介面實現token攔截以及引數校驗_kotomeli的部落格-CSDN部落格_springboot攔截請求引數

到此這篇關於SpringBoot登入驗證token攔截器的實現的文章就介紹到這了,更多相關SpringBoot token攔截器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com