token的處理和統一異常處理


統一token處理

3.1 問題分析

3.1.1 問題

token 為了測試方便增大存活時長,但是之后token能獲去但總是不對,因為數據越界,變為-1,導致本來想增大存活時長,但卻早早就使得token過期了

每一個控制方法中都需要解析token , 獲取當前用戶id , 代碼重復度比較高

  • 重復性的登錄驗證

  • 繁瑣的token獲取及解析

3.1.2 解決方案

基於ThreadLocal + 攔截器的形式統一處理

 

 

 

攔截器(Interceptor)

  • 是一種動態攔截方法調用的機制;

  • 類似於Servlet 開發中的過濾器Filter,用於對處理器進行前置處理和后置處理。

 

 

 

ThreadLocal

  • 線程內部的存儲類,賦予了線程存儲數據的能力。

  • 線程內調用的方法都可以從ThreadLocal中獲取同一個對象。

  • 多個線程中ThreadLocal數據相互隔離

Threadlocal使用方法很簡單

ThreadLocal<T> threadLocal = new ThreadLocal<T>();
threadLocal.set() //將數據綁定到當前線程
threadLocal.get() //從當前線程中獲取數據

代碼實現

 定義攔截器

 

定義攔截器,在前置攔截方法preHandle中解析token並驗證有效性,如果失效返回狀態碼401。如果有效,解析User對象,存入ThreadLocal中

package com.i.server.interceptor;

/**
 * @author Administrator
 */
public class TokenInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //1、獲取請求頭
        String token = request.getHeader("Authorization");

        //2、使用工具類,判斷token是否有效
        boolean verifyToken = JwtUtils.verifyToken(token);
        //3、如果token失效,返回狀態碼401,攔截
        if(!verifyToken) {
            response.setStatus(401);
            return false;
        }
        //4、如果token正常可用,放行
        //解析token,獲取id和手機號碼,
        Claims claims = JwtUtils.getClaims(token);
        String mobile = (String) claims.get("mobile");
        Integer id = (Integer) claims.get("id");
        
        //構造User對象,存入Threadlocal
        User user = new User();
        user.setId(Long.valueOf(id));
        user.setMobile(mobile);

        UserHolder.set(user);

        return true;
    }
    
    //清空 解決ThreadLocal中數據越存越多導致內存溢出問題
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.remove();
    }
}

注冊攔截器

攔截器需要注冊到MVC容器中

/**
 * @author Administrator
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login", "/user/loginVerification");
    }
}

ThreadLocal工具類

定義ThreadLocal工具類,僅需要調用set方法即可將數據存入ThreadLocal中

package com.i.server.interceptor;

import com.i.model.domain.User;

/**
 * @author Administrator
 */
public class UserHolder {

    private static ThreadLocal<User> tl = new ThreadLocal<User>();

    /**
     * 保存數據到線程
     */
    public static void set(User user) {
        tl.set(user);
    }

    /**
     * 獲取線程中的用戶信息
     */
    public static User get() {
        return tl.get();
    }


    /**
     * 從當前線程,獲取用戶對象的id
     */
    public static Long getUserId() {
        if (tl.get() == null) {
            return null;
        }
        return tl.get().getId();
    }

    /**
     * 從當前線程,獲取用戶對象的手機號碼
     */
    public static String getMobile() {
        if (tl.get() == null) {
            return null;
        }
        return tl.get().getMobile();
    }

     /**
     * 移除線程中數據
     */
    public static void remove() {
        tl.remove();
    }
}

修改控制器方法

修改控制器方法, 所有需要用到userId都可以直接從線程中獲取Long userId = UserHolder.getUserId();

@PostMapping(path = "/loginReginfo")
public ResponseEntity loginReginfo(@RequestBody UserInfo userInfo) {
    //1. 校驗token
    Long userId = UserHolder.getUserId();
    //2. 保存用戶信息
    userInfo.setId(userId);
    userInfoService.save(userInfo);
    //3. 返回數據
    return ResponseEntity.ok(null);
}

 

 

統一異常處理

軟件開發過程中,不可避免的是需要處理各種異常,常見的形式就是逐層向上拋出,web層進行處理。使用try {...} catch {...}很方便就能對異常做到業務處理

  1. 冗余代碼多,影響代碼可讀性

  2. 異常處理和業務代碼耦合

 SpringMVC提供了一套解決全局異常的處理方案,可以在代碼無侵入的前提下完成異常處理。遵循逐層拋出,異常處理器統一處理的思路

(面向切面編程 aop)

 

 

項目中可能存在不可預知的各種異常,如:空指針,數組越界等。針對這類異常,可以直接在異常處理器中統一處理;

還有一類是可預知的錯誤,如圖片不合法,驗證碼錯誤等等。這類錯誤也可以理解為業務異常,可以通過自定義異常類來處理;

業務異常對象

需求:自定義統一異常處理器,完成異常統一處理

 1定義異常處理類ExceptionAdvice

 2 編寫方法,構造異常錯誤信息

 3 通過@ControllerAdvice和@ExceptionHandler配置

為了方便操作,將一些常見的業務錯誤封裝到ErrorResult對象中

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ErrorResult {

    private String errCode = "999999";
    private String errMessage;

    public static ErrorResult error() {
        return ErrorResult.builder().errCode("999999").errMessage("系統異常稍后再試").build();
    }

    public static ErrorResult fail() {
        return ErrorResult.builder().errCode("000001").errMessage("發送驗證碼失敗").build();
    }

    public static ErrorResult loginError() {
        return ErrorResult.builder().errCode("000002").errMessage("驗證碼失效").build();
    }

    public static ErrorResult faceError() {
        return ErrorResult.builder().errCode("000003").errMessage("圖片非人像,請重新上傳!").build();
    }

    public static ErrorResult mobileError() {
        return ErrorResult.builder().errCode("000004").errMessage("手機號碼已注冊").build();
    }

    public static ErrorResult contentError() {
        return ErrorResult.builder().errCode("000005").errMessage("動態內容為空").build();
    }

    public static ErrorResult likeError() {
        return ErrorResult.builder().errCode("000006").errMessage("用戶已點贊").build();
    }

    public static ErrorResult disLikeError() {
        return ErrorResult.builder().errCode("000007").errMessage("用戶未點贊").build();
    }

    public static ErrorResult loveError() {
        return ErrorResult.builder().errCode("000008").errMessage("用戶已喜歡").build();
    }

    public static ErrorResult disloveError() {
        return ErrorResult.builder().errCode("000009").errMessage("用戶未喜歡").build();
    }
}

業務異常類

自定義業務異常類,針對業務錯誤之間拋出業務異常即可

/**
 * 自定義異常類
 */
@Data
public class BusinessException extends RuntimeException {

    private ErrorResult errorResult;

    public BusinessException(ErrorResult errorResult) {
        super(errorResult.getErrMessage());
        this.errorResult = errorResult;
    }
}

異常處理器

/**
 * 自定義統一異常處理
 *  1、通過注解,聲明異常處理類
 *  2、編寫方法,在方法內部處理異常,構造響應數據
 *  3、方法上編寫注解,指定此方法可以處理的異常類型
 */
@ControllerAdvice
public class ExceptionAdvice {

    //處理業務異常
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity handlerException(BusinessException be) {
        be.printStackTrace();
        ErrorResult errorResult = be.getErrorResult();
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
    }

    //處理不可預知的異常
    @ExceptionHandler(Exception.class)
    public ResponseEntity handlerException1(Exception be) {
        be.printStackTrace();
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ErrorResult.error());
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM