欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot使用自定义注解实现权限拦截的示例

程序员文章站 2024-04-01 17:03:04
本文介绍了springboot使用自定义注解实现权限拦截的示例,分享给大家,具体如下: handlerinterceptor(处理器拦截器) 常见使用场景...

本文介绍了springboot使用自定义注解实现权限拦截的示例,分享给大家,具体如下:

handlerinterceptor(处理器拦截器)

常见使用场景

  • 日志记录: 记录请求信息的日志, 以便进行信息监控, 信息统计, 计算pv(page view)等
  • 性能监控:
  • 权限检查:
  • 通用行为:

使用自定义注解实现权限拦截

首先handlerinterceptor了解

在handlerinterceptor中有三个方法:

public interface handlerinterceptor {

  // 在执行目标方法之前执行
  boolean prehandle(httpservletrequest request,httpservletresponse response, object handler)throws exception;

  // 执行目标方法之后执行
  void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview)throws exception;

  // 在请求已经返回之后执行
  void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex)throws exception;

}

在以上注释中已经写明执行顺序, 测试及测试结果如下:

public class testinterceptor implements handlerinterceptor {

  @override
  public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
    system.out.println("prehandler");
    return true;
  }

  @override
  public void posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) throws exception {
    system.out.println("posthandler");
  }

  @override
  public void aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex) throws exception {
    system.out.println("aftercompletion");
  }
}

结果:

prehandler
posthandler
aftercompletion

如何配置拦截器后面讲:

下面来讲讲这个拦截器是拦截的什么?

方法拦截器handlerinterceptor

我们使用springmvc都知道springmvc是基于方法的请求处理, 和struts2有明显区别(我是这么理解的), 并且controller是单例模式, 所有请求都是从dispatcherservlet来调用请求url对应的方法的(处理器映射器的功能), 那么它是一个方法拦截器, 如何知道呢?

@override
public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
  system.out.println(handler.getclass());
  return true;
}

执行结果:

class org.springframework.web.method.handlermethod

已经看到拦截器如何工作, 并且知道它是方法级别的拦截器, 那么接下来看看如何配置拦截器, 让它在服务器运行期间起作用

实现handlerinterceptor

实现handlerinterceptor接口或者继承handlerinterceptoradapter类
handlerinterceptoradapter适配器是对handlerinterceptor接口做了默认实现, 这种适配器模式, 是为了方便开发者只去想要复写方法, 其他方法采取默认措施.

上面的testinterceptor就是一个demo, 具体可以按需求在我们想要拦截的位置进行相应的逻辑处理

配置拦截器

从spring4.x开始, 就支持注解配置具体是使用@configuration注解标注一个普通类, 使该类成为配置类, 可以在该类中定义bean, 以及注入一些配置参数等.

首先, 我们要配置拦截器,就需要想springmvc的拦截链中取注入我们的拦截器, 这样才能请求进来时去拦截我们想要拦截的请求, 所以, 需要我们新建一个普通类, 使用@configuration注解标注在类名上, 并且让该类继承webmvcconfigureradapter,为什么不实现webmvcconfigurer接口呢? 因为方法太多,我们不需要复写其中所有方法...

下面让我们新建一个配置类, 来配置我们的拦截器

@configuration
public class interceptorconfig extends webmvcconfigureradapter {

  public void addinterceptors(interceptorregistry registry) {
    registry.addinterceptor(new testinterceptor()).addpathpatterns("/**");
  }
}

复写addinterceptors之后, 当我们的服务器启动时, 会自动调用这个方法, 参数怎么传我们都不用管(目前是java初级阶段, 没空研究spring深层原理), 我们只要知道这个方法一定会被调用就行了.

我们直接new一个自定义拦截器, 注册到整个拦截链中, 并且制定拦截路径, 这样当满足请求url拦截配置时, 我们的自定义拦截器就会执行相应的方法了.

拦截方法是非常灵的, 除了拦截配置的, 也可以拦截非匹配的, 也可以根据正则表达式拦截请求

至此, 我们的拦截器就完成了, 接下来自定义权限相关注解

自定义权限注解

定义一个@interface类

@target(elementtype.method)
@retention(retentionpolicy.runtime)
@documented
public @interface access {

  string[] value() default {};

  string[] authorities() default {};

  string[] roles() default {};

}

@target注解是标注这个类它可以标注的位置:

常用的元素类型(elementtype):

public enum elementtype {
  /** class, interface (including annotation type), or enum declaration */
  // type类型可以声明在类上或枚举上或者是注解上
  type,
  /** field declaration (includes enum constants) */
  // field声明在字段上
  field,
  /** method declaration */
  // 声明在方法上
  method,
  /** formal parameter declaration */
  // 声明在形参列表中
  parameter,
  /** constructor declaration */
  // 声明在构造方法上
  constructor,
  /** local variable declaration */
  // 声明在局部变量上
  local_variable,
  /** annotation type declaration */
  annotation_type,
  /** package declaration */
  package,
  /**
   * type parameter declaration
   *
   * @since 1.8
   */
  type_parameter,
  /**
   * use of a type
   *
   * @since 1.8
   */
  type_use
}

@retention注解表示的是本注解(标注这个注解的注解保留时期)

public enum retentionpolicy {
  /**
   * annotations are to be discarded by the compiler.
   */
  // 源代码时期
  source,
  /**
   * annotations are to be recorded in the class file by the compiler
   * but need not be retained by the vm at run time. this is the default
   * behavior.
   */
  // 字节码时期, 编译之后
  class,
  /**
   * annotations are to be recorded in the class file by the compiler and
   * retained by the vm at run time, so they may be read reflectively.
   *
   * @see java.lang.reflect.annotatedelement
   */
   // 运行时期, 也就是一直保留, 通常也都用这个
  runtime
}

@documented是否生成文档的标注, 也就是生成接口文档是, 是否生成注解文档

注解说完了, 下面需要到对应的controller的方法中取添加注解, 配置该方法允许的权限

在方法上配置权限

@restcontroller
public class hellocontroller {

  @requestmapping(value = "/admin", produces = mediatype.application_json_utf8_value, method = requestmethod.get)
  // 配置注解权限, 允许身份为admin, 或者说允许权限为admin的人访问
  @access(authorities = {"admin"})
  public string hello() {
    return "hello, admin";
  }
}

编写权限逻辑

// 自定义一个权限拦截器, 继承handlerinterceptoradapter类
public class authenticationinterceptor extends handlerinterceptoradapter {

  // 在调用方法之前执行拦截
  @override
  public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception {
    // 将handler强转为handlermethod, 前面已经证实这个handler就是handlermethod
    handlermethod handlermethod = (handlermethod) handler;
    // 从方法处理器中获取出要调用的方法
    method method = handlermethod.getmethod();
    // 获取出方法上的access注解
    access access = method.getannotation(access.class);
    if (access == null) {
    // 如果注解为null, 说明不需要拦截, 直接放过
      return true;
    }
    if (access.authorities().length > 0) {
      // 如果权限配置不为空, 则取出配置值
      string[] authorities = access.authorities();
      set<string> authset = new hashset<>();
      for (string authority : authorities) {
      // 将权限加入一个set集合中
        authset.add(authority);
      }
      // 这里我为了方便是直接参数传入权限, 在实际操作中应该是从参数中获取用户id
      // 到数据库权限表中查询用户拥有的权限集合, 与set集合中的权限进行对比完成权限校验
      string role = request.getparameter("role");
      if (stringutils.isnotblank(role)) {
        if (authset.contains(role)) {
        // 校验通过返回true, 否则拦截请求
          return true;
        }
      }
    }
    // 拦截之后应该返回公共结果, 这里没做处理
    return false;
  }

}

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。