注解实现接口拦截功能

本文介绍了在Spring Boot项目中通过注解实现接口拦截功能,包括两种方式:一种是自定义拦截器并过滤不需要token验证的接口;另一种是使用注解`UserToken`和`PassToken`来区分接口是否需要token验证。同时,通过`CurrentUser`注解,可以直接获取当前用户的详细信息,简化开发流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注解实现接口拦截功能

方式一

我们在Spring boot项目中时候,一般会采用token作为身份验证,这样也方便前端做免登录功能

  1. 一般方式:我们会自定义一个拦截器,继承WebMvcConfigurer类,重写addInterceptors方法,效果如下:
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 不需要拦截校验的url
        String[] clientUserApiArrays = {
                Constants.CLIENT_URI_V1 + "/users/token",
                Constants.CLIENT_URI_V1 + "/users/we_chat_login"
        };
        registry.addInterceptor(clientUserApiInterceptor).addPathPatterns("/api/v*/client/**").excludePathPatterns(clientUserApiArrays);
    }
}

将不用token验证的接口地址过滤掉,这样也是很不错的做法

Github地址

  1. 第二种方式

采用注解的方式

  1. 先看下在代码中的应用,看看是不是你要的效果,不需要验证默认是不用加的
@ApiOperation(value = "获取推荐商户列表", notes = "获取推荐商户列表")
    @GetMapping("/getMerchantList")
    @UserToken
    public BaseResult<List<MerchantVO>> getMerchantList() {
        return BaseResult.success(iMerchantService.getMerchantList());
    }

  1. 先定义两个注解用来区分需要token验证和不需要token验证
PassToken注解(对方法使用)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

UserToken注解(对方法使用)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserToken {
    boolean required() default true;

}
  1. 我们对刚才继承WebMvcConfigurer类改造一下
@Configuration
public class UserApiInterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns(Constants.APP_URI_V1 + "/**");
                // 对所有的接口进行拦截,判断是否包含有上面的验证注解
    }

	// UserApiInterceptor拦截器
    @Bean
    public UserApiInterceptor authenticationInterceptor() {
        return new UserApiInterceptor();
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        // 注册@CurrentUser注解的实现类
        resolvers.add(new CurrentUserHandlerMethodArgResolver());
    }
}

UserApiInterceptor类(用于拦截区分带UserToken的注解或者PassToken的注解)

@Slf4j
@Component
public class UserApiInterceptor implements HandlerInterceptor {

    @Autowired
    IUserService iUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        log.info("请求地址[{}], 请求参数[{}]", request.getRequestURI(), JSONObject.toJSON(request.getParameterMap()));
        String token = request.getHeader(JwtTokenUtil.HEADER_STRING);

        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        // 检查是否有passToken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        // 检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserToken.class)) {
            UserToken userToken = method.getAnnotation(UserToken.class);
            if (userToken.required()) {
                // 执行认证
                if (StringUtils.isEmpty(token)) {
                    throw new TokenValidException(300, "token校验失败!未获取到token");
                }
                Boolean valid = JwtTokenUtil.validateJWTToken(token);
                if (!valid) {
                    throw new TokenValidException(300, "token校验失败!请检查");
                }
                User user = iUserService.getById(JwtTokenUtil.getJwtUserId(request));
                if (user == null) {
                    throw new BusinessException(300, "用户信息为空");
                }
                UserInfoVO userInfoVO = new UserInfoVO();
                BeanUtils.copyProperties(user, userInfoVO);
                //我们将解析的用户结果先放入session中
                request.getSession().setAttribute("currentUser",userInfoVO);
            }
        }
        return true;
    }

}

CurrentUserHandlerMethodArgResolver类(用于获取CurrentUser注解对象的值,后面会介绍)

@Slf4j
@Component
public class CurrentUserHandlerMethodArgResolver implements HandlerMethodArgumentResolver {

    /**
     * 判断是否支持使用@CurrentUser注解的参数
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        // 如果该参数注解有@CurrentUser且参数类型是User
        return methodParameter.getParameterAnnotation(CurrentUser.class) != null && methodParameter.getParameterType() == UserInfoVO.class;
    }

    /**
     * 注入参数值
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        // 取得HttpServletRequest
        HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
        // 取出session中的User
        UserInfoVO userInfoVO = (UserInfoVO) request.getSession().getAttribute("currentUser");
        log.info("currentUser id:{}", userInfoVO.getId());
        return userInfoVO;
    }

}
  1. 这时候便可以使用这个上面PassToken和UserToken注解了(有些代码工具缺失,最后有源码包可供参考)

    @ApiOperation(value = "获取推荐商户列表", notes = "获取推荐商户列表")
        @GetMapping("/getMerchantList")
        @UserToken
        public BaseResult<List<MerchantVO>> getMerchantList() {
            return BaseResult.success(iMerchantService.getMerchantList());
        }
    
  2. 我们来看下CurrentUser的注解的用途

    项目中我们一般通过token验证来验证某个用户是否是登录状态,我们只验证登录状态,不去获取这个用户的信息吗,一般情况下,我们也可以通过Request这种方式,将token(id)传入每个方法中,然后用该token获取获取id,在通过id获取一些信息,这样也是可以的,这边我们tokenCurrentUser注解,直接将用户信息拿到,供后面开发使用,即加即用,先看效果:

@ApiOperation(value = "获取轮播图列表", notes = "获取轮播图列表接口")
    @GetMapping("/getBannerList")
    @UserToken
    public BaseResult<List<BannerVO>> getBannerList(@CurrentUser UserInfoVO userInfoVO) {
        // 测试CurrentUser注解
        log.info("UserInfoVo" + userInfoVO.toString());
        return BaseResult.success(iBannerService.getBannerList());
    }

打印结果:

2020-12-29 16:41:13.788  INFO 12632 --- [nio-8081-exec-3] com.llayjun.millet.api.BannerController  : UserInfoVoUserInfoVO{id='1319550471008100353', name='1', sex=0, mobile='1', passWord='1'}

CurrentUser注解类(对对象使用)

@Target({ElementType.PARAMETER}) // Annotation所修饰的对象范围:方法参数
@Retention(RetentionPolicy.RUNTIME) // Annotation被保留时间:运行时保留(有效)
@Documented
public @interface CurrentUser {

}
  1. 源代码

Github地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值