SpringBoot 使用拦截器实现自定义注解

2019年10月10日21:30:36 评论 753

在SpringBoot中,我们可以使用自定义注解来实现很多功能,比如权限控制,一般只需要四步就可以实现自定义注解。今天,我们就来简单总结一下SpringBoot实现自定义注解的方法。

业务场景:现在我需要对部分用户进行拦截,通过注解的方式,假如有这么一个注解@WithOutLogin,只要他作用在方法上,那么请求都不需要带token,反之,如果请求没有带token,那么请求方法将会失败。

一. 自定义注解

定义一个@interface类。

package com.exam.common.security;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WithOutLogin {
}

@Target:用于描述注解的使用范围。
ElementType.TYPE:类、接口(包括注解类型) 或enum声明。ElementType.METHOD:方法。
@Retention:注解的生命周期,用于表示该注解会在什么时期保留。
RetentionPolicy.RUNTIME:运行时保留,这样就可以通过反射获得了。
@Documented:表示该注解会被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

二. 编写拦截器类

注解定义好了,接下来使用Springboot拦截器,创建类继承HandlerInterceptor,并实现preHandle、postHandle、afterCompletion方法。然后在preHandle方法中编写业务逻辑,实现注解的业务功能。

package com.exam.common.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class PermissionInterceptor implements HandlerInterceptor {

    //在请求已经返回之后执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) throws Exception {
    }

    //执行目标方法之后执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object hadnler, ModelAndView ex) throws Exception {
    }

    //在执行目标方法之前执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object hadnler) throws Exception {
        //判断是否方法级别的
        if (hadnler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) hadnler;
            WithOutLogin withOutLogin = handlerMethod.getMethodAnnotation(WithOutLogin.class);
            //有注解,则不验证token
            if (withOutLogin != null) {
                return true;
            }
        }

        String token = request.getHeader("X-Auth-Token");
        if (StringUtils.isBlank(token)) {
            log.error("未登录 userId is :{}, token  is null.", token);
            return false;
        }
        return true; 
    } 
}

三. 拦截器类注册

创建一个普通类,让该类继承WebMvcConfigurerAdapter,并且使用@Configuration注解标注在类名上,使其成为一个配置类。然后重写addInterceptors方法,当服务器启动时,会自动调用此方法。

package com.exam.common.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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 PermissionConfigure extends WebMvcConfigurerAdapter {
    @Autowired
    private PermissionInterceptor permissionInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(permissionInterceptor).addPathPatterns("/user/**");
        super.addInterceptors(registry);
    }
}

四. 编写controller类

在Controller中的业务方法上选择是否标注@WithOutLogin注解,如果有,那么无需登录就可以执行该方法。如果没有此注解,就需要登录后才可以执行。

package com.exam.api.rest;

import com.exam.common.security.WithOutLogin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController extends RestBaseController {

    @WithOutLogin
    @PutMapping(value = "/findPassword")
    public String smsLogin(HttpServletRequest request){
        return "找回密码不需要登录";
    }

    @PostMapping(value = "/modifyMobile")
    public String modifyMobile(ModifyMobileRequest entity, HttpServletRequest request){
        return "修改手机号需要登录";
    }
}

至此,我们启动服务器,访问接口,就能看到效果了,这里不再做示范。

本文已通过「原本」原创作品认证,未经作者授权请勿直接转载,负责将依法追究其法律责任。
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: