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

发布于 2019-10-10

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

在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 "修改手机号需要登录";
    }
}

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

喜欢 3
奋楫笃行,臻于至善!

相关文章

使用 Mycat 中间件搭建 MySQL 高可用实现分库分表及读写分离

Mycat 是一款基于阿里开源产品Cobar而研发的开源数据库分库分表中间件(基于Java语言开发),可以用来方便地搭建面向企业应用开发的大数据库集群,支持事务、ACID等特性,其核心是基于代理方案实...
阅读全文

通用架构模式和通用架构服务

架构模式是在给定上下文的软件架构中,针对常发生问题的一种通用、复用的解决方案。架构模式类似于软件设计模式,但是范畴更广。一个好的软件产品往往需要有良好的架构思想和架构服务来支撑整个软件的生命周期,本文...
阅读全文

Java 的可重入锁和不可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。Java中Reentra...
阅读全文

Redis 的两种持久化方式及使用场景分析

Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘。当下次Redis重启时,利用持久化文件实现数据恢...
阅读全文

redis 高可用主从,哨兵,集群解决方案

Redis因为其高性能和易用性在我们后端的服务中发挥了巨大的作用,并且很多重要功能的实现都会依赖redis。除了常用的缓存,还有队列,发布订阅等重要用处。所以redis的服务高可用就显得尤为关键。这里...
阅读全文

Redis 缓存穿透、缓存击穿、缓存雪崩的区别及解决方案

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很...
阅读全文

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注