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

网友投稿 405 2023-03-28


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

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

HandlerInterceptor(处理器拦截器)

常见使用场景

日志记录: 记录请求信息的日志, 以便进行信息监控, 信息统计, 计算PV(page View)等

性能监控:

权限检查:

通用行为:

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

首先HandlerInterceptor了解

在HandlerInterceptor中有三个方法:

public interface HandlerInterceptor {

// 在执行目标方法之前执行

boolean preHandle(HttpServletRequest request,tViDQpmHttpServletResponse 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 {}tViDQpm;

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 partViDQpmameter 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 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;

}

}

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:ArrayList在for循环中使用remove方法移除元素方法介绍
下一篇:SpringBoot整合ElasticSearch的示例代码
相关文章

 发表评论

暂时没有评论,来抢沙发吧~