1. 程式人生 > >SpringBoot之HandlerInterceptor攔截器的使用 ——(二)自定義註解

SpringBoot之HandlerInterceptor攔截器的使用 ——(二)自定義註解

在上一篇部落格已經介紹了HandlerInterceptor的基本用法這裡就不重複了詳見:SpringBoot之HandlerInterceptor攔截器的使用 ——(一)

功能簡介

攔截所有添加了我們自定義的註解的方法,並將userId和userMobile放入HttpServletRequest,之後通過對應的註解取值。

包格式

所需要的類

首先我們來先定義三個註解

根據需求其實UserId和UserMobile可以不要,不影響攔截器的使用

package com.xxx.core.annotation;

import javax.ws.rs.NameBinding;
import
java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(value = RetentionPolicy.RUNTIME) @NameBinding public @interface UserAuthenticate { /** * 是否需要校驗訪問許可權 預設不校驗 * * @return
*/
boolean permission() default false; }
package com.xxx.core.annotation;

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UserId {
}
package com.xxx.core.annotation;

import java.lang.annotation.*;

@Target
(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface UserMobile { }

常量類

package com.xxx.core.handler;

public class HeaderCons {

    /**
     * 使用者ID
     */
    public static final String USER_ID = "H-User-Id";

    /**
     * 使用者手機號
     */
    public static final String USER_MOBILE = "H-User-Mobile";
}

攔截器

package com.xxx.core.filter;

import com.xxx.exception.FastRuntimeException;
import com.xxx.core.annotation.UserAuthenticate;
import com.xxx.core.handler.HeaderCons;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Objects;


public class TestFilter extends HandlerInterceptorAdapter {
    private final Logger logger = LoggerFactory.getLogger(TestFilter.class);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        logger.info("request請求地址path[{}] uri[{}]", request.getServletPath(),request.getRequestURI());
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        UserAuthenticate userAuthenticate = method.getAnnotation(UserAuthenticate.class);
        //如果沒有加註解則userAuthenticate為null
        if (Objects.nonNull(userAuthenticate)) {
            Long userId= getUserId(request);
            //userAuthenticate.permission()取出permission判斷是否需要校驗許可權
            if (userId == null || (userAuthenticate.permission() && !checkAuth(userId,request.getRequestURI()))){
                throw new FastRuntimeException(20001,"No access");
            }
        }
        return true;
    }

    /**
     * 根據token獲取使用者ID
     * @param request
     * @return
     */
    private Long getUserId(HttpServletRequest request){
        //新增業務邏輯根據token獲取使用者UserId
        request.getHeader("H-User-Token");
        Long userId = 1L;
        String userMobile = "18888888888";
        request.setAttribute(HeaderCons.USER_ID,userId);
        request.setAttribute(HeaderCons.USER_MOBILE,userMobile);
        return userId;
    }

    /**
     * 校驗使用者訪問許可權
     * @param userId
     * @param requestURI
     * @return
     */
    private boolean checkAuth(Long userId,String requestURI){
        //新增業務邏輯根據UserId獲取使用者的許可權組然後校驗訪問許可權
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {}

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {}
}
package com.xxx.core;

import com.welab.fund.core.filter.TestFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebAppConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 可新增多個,這裡選擇攔截所有請求地址,進入後判斷是否有加註解即可
            registry.addInterceptor(new TestFilter()).addPathPatterns("/**");
    }
}

如果不需要使用UserId和UserMobile這兩個註解到這裡已經結束了。不過為了方便業務層的使用直接獲取使用者的id、mobile等資訊我這裡就加上了

新增如下類即可取出我們在攔截器中set進去的值

package com.xxx.core.handler;

import com.xxx.core.annotation.UserId;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;


public class UserIdMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(UserId.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        return servletRequest.getAttribute(HeaderCons.USER_ID);
    }
}
package com.xxx.core.handler;

import com.xxx.core.annotation.UserMobile;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;


public class UserMobileMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(UserMobile.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        return servletRequest.getAttribute(HeaderCons.USER_MOBILE);
    }
}

以上類是根據你定義的註解來建設的取出放在request裡面的值,如果有多個就再加就行了

package com.xxx.core.filter;


import com.xxx.core.annotation.UserId;
import com.xxx.core.annotation.UserMobile;
import com.xxx.core.handler.UserIdMethodArgumentResolver;
import com.xxx.core.handler.UserMobileMethodArgumentResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.List;


@Configuration
public class FilterAutoConfiguration {


    @Configuration
    @ConditionalOnWebApplication
    @ConditionalOnClass({UserId.class, UserMobile.class})//多個用逗號隔開
    protected static class ArgumentResolverAutoConfiguration extends WebMvcConfigurerAdapter {
        protected ArgumentResolverAutoConfiguration() {
        }

        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        //可新增多個
            argumentResolvers.add(new UserIdMethodArgumentResolver());
            argumentResolvers.add(new UserMobileMethodArgumentResolver());
        }
    }
}

大功告成 接下來我們看看如何使用



package com.xxx.controller;


import com.xxx.common.response.Response;
import com.xxx.common.Urls;
import com.xxx.core.annotation.UserAuthenticate;
import com.xxx.core.annotation.UserId;
import com.xxx.core.annotation.UserMobile;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Validated
@RestController
public class TestAuthController {
    @UserAuthenticate
    @GetMapping(value = Urls.Test.TEST)
    public Response testAuth(@UserId Long userId,@UserMobile String userMobile) {
        System.out.println("userId : "+ userId + "  userMobile :" + userMobile);
        return new Response();
    }
}

瀏覽器輸入地址 後臺列印
userId : 1 userMobile :18888888888

下一篇帶來如何解決獲取request中body內容後,導致字元流關閉,後續controller無法獲取的問題