天天看點

Spring boot 項目(八)——自定義攔截器

Jwt簡介

1、Json web token (JWT), 是為了在網絡應用環境間傳遞聲明而執行的一種基于JSON的開放标準((RFC 7519).該token被設計為緊湊且安全的,特别适用于分布式站點的單點登入(SSO)場景。

2、JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便于從資源伺服器擷取資源,也可以增加一些額外的其它業務邏輯所必須的聲明資訊,該token也可直接被用于認證,也可被加密。

3、JWT生成的token是無狀态的,服務端不存儲,即一旦生成在有效期之前一直可用,無法銷毀。

4、如果需要重新整理token有效期或者提前失效需要借助緩存、資料庫或者redis來自己實作對應邏輯

操作流程

1、建立JwtUtil類

public class JwtUtil {

    //過期時間 30min
    private static final int EXPIRE_TIME = 30;
    //私鑰
    private static final String TOKEN_SECRET = "privateKey";
    private static final String USER_NAME = "name";

    /**
     *      簽發對象:這個使用者的id
     *      簽發時間:現在
     *      有效時間:30分鐘
     *      載荷内容:暫時設計為:這個人的名字
     *      加密密鑰:這個人的id加上一串字元串
     */
    public static String createToken(Long userId, String userName) {
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(Calendar.MINUTE,EXPIRE_TIME);
        Date expiresDate = nowTime.getTime();

        String token= JWT.create().withAudience(userId+"")   //簽發對象
                .withIssuedAt(new Date())    //發行時間
                .withExpiresAt(expiresDate)  //有效時間
                .withClaim(USER_NAME, userName)    //載荷,随便寫幾個都可以
                .sign(Algorithm.HMAC256(userId+TOKEN_SECRET));   //加密
        return token;
    }
    /**
     * 檢驗合法性,其中secret參數就應該傳入的是使用者的id
     */
    public static boolean verifyToken(String token, String secret) {
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret+TOKEN_SECRET)).build();
            verifier.verify(token);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 擷取簽發對象
     */
    public static String getAudience(String token) throws Exception {
        String audience = null;
        try {
            audience = JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeException j) {
            //這裡是token解析失敗

        }
        return audience;
    }
           

2、建立WebMvcConfig檔案

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注冊自定義攔截器
        registry.addInterceptor(loginInterceptor())
                .addPathPatterns("/**")
                // 那些路徑不攔截
                .excludePathPatterns("/login/**","/error"
                        ,"/swagger-ui.html/**","/swagger-resources/**");
    }
    @Bean
    public LoginInterceptor loginInterceptor(){
        return new LoginInterceptor();
    }
}
           

3、建立NoNeedToken注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoNeedToken {

}
           

4、建立LoginInterceptor檔案

public class LoginInterceptor implements HandlerInterceptor {

    protected Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private SysUserService sysUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");

        logger.info("已進入攔截器,目前通路位址為:" + request.getRequestURI());


        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        //檢查是否有NoNeedToken注釋,有則跳過認證
        if (method.isAnnotationPresent(NoNeedToken.class)) {
            return true;
        } else {
            if (token == null) {
                logger.warn("登入失效");
                return false;
            }
            String userId = JwtUtil.getAudience(token);
            SysUser user = sysUserService.getById(Long.valueOf(userId));
            if (user == null) {
                logger.warn("該使用者不存在!");
                return false;
            }
            boolean r = JwtUtil.verifyToken(token, userId);
            if (r){
                logger.info("驗證成功");
            }else{
                logger.info("驗證失敗");
                return false;
            }
            return true;
        }
    }
}
           

5、在controller層上不需要驗證token的方法上添加@NoNeedToken