SpringBoot配置拦截器并实现权限验证

本文中的代码详见:https://github.com/hawkingfoo/springboot-interceptor

一、概述

拦截器的使用场景越来越多,尤其是面向切片编程流行之后。那通常拦截器可以做什么呢? 
之前我们在Agent介绍中,提到过统计函数的调用耗时。这个思路其实和AOP的环绕增强如出一辙。

使用场景如下:

  • 函数增强:比如对一个函数进行参数检查,或者结果过滤等。甚至可以对函数就行权限认证。
  • 性能监控:统计函数性能。
  • 日志打点:比如在用户登录函数之前,打点统计PV等信息。以及其他等等。

二、Spring的拦截器

无论是SpringMVC或者SpringBoot中,关于拦截器不得不提: 
org.springframework.web.servlet.handler.HandlerInterceptorAdapter

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {

    // 在目标方法执行前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    // 在目标方法执行后执行,但在请求返回前,我们仍然可以对 ModelAndView进行修改
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 
                          throws Exception {}

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

    // 用来处理异步请求, 当Controller中有异步请求方法的时候会触发该方法
    @Override
    public void afterConcurrentHandlingStarted(
            HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {}
}

三、实现一个用于验证简单权限的拦截器

1. 自定义一个权限注解 @Auth

@Inherited
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Auth {
    String user() default "";
}

@Inherited:在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解,否则,子类不会继承此注解。这里一定要记住,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。
@Target:表示此注解可以放置的位置。常见的位置有:TYPE=枚举或注解上,FIELD=字段上,METHOD=方法上,PARAMETER=函数形参列表中,CONSTRUCTOR=构造函数上,LOCAL_VARIABLE=局部变量上 等等其他位置。
@Retention:此注解的生命周期。常见的有:SOURCE=源码时期;CLASS=字节码时期(已编译);RUNTIME=运行时期,通常是用这个的时候要多。
@Documentd:生成注解文档。

2. 在Controller的方法上添加注解
上一步添加完注解后,之后要在你所使用的方法上添加相关注解,如下。

@RestController
@EnableAutoConfiguration
public class DemoController {

    @Auth(user = "admin")
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String sayHello() {
        return "hello world.";
    }
}

3. 实现拦截器功能
需求:我们在用户通过/hello这个URI访问时,对其进行验证,如果为admin则放行,否则拒绝,假设用户的身份在URL参数中。 
思路:因此我们要在执行sayHello()之前,对用户做出验证。如果其身份与注解中的身份相同,则放行。因此我们要在preHandle()中做文章。 
难点:我们怎么拿到Controller 方法上的@Auth这个注解呢?看PreHandle()的三个参数,貌似也没有哪个可以提供Controller类中的注解。

其实,第三个参数handler,一般情况下其类型为:org.springframework.web.method.HandlerMethod类型,而这里面含有注解的相关信息。

为什么这么说呢? 
在SpringBoot中,注解的默认类型为函数级,而在SpringMVC其默认类型为Controller对象级别。 
因此,如果在SpringMVC中需要在dispatcher-servlet.xml中配置: 
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>,这样其类型才为HandlerMethod。我们看下具体实现逻辑:   

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        if (!handler.getClass().isAssignableFrom(HandlerMethod.class)) {
            System.out.println("cat cast handler to HandlerMethod.class");
            return true;
        }
        // 获取注解
        Auth auth = ((HandlerMethod) handler).getMethod().getAnnotation(Auth.class);
        if (auth == null) {
            System.out.println("cant find @Auth in this uri:" + request.getRequestURI());
            return true;
        }
        // 从参数中取出用户身份并验证
        String admin = auth.user();
        if (!admin.equals(request.getParameter("user"))) {
            System.out.println("permission denied");
            response.setStatus(403);
            return false;
        }
        return true;
    }

其实实现逻辑就两点:从参数中取出身份,和注解中的进行比对。

4. 配置拦截器
那怎么让刚才的这个拦截器生效呢? 
这个时候,需要我们配置:WebMvcConfigurerAdapter 
具体实现如下:

@Configuration
public class ConfigAdapter extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/hello");
    }
}

注意:这里有两点需要注意,一个是@Configuration这个注解,这样才能让SpringBoot服务发现这个配置;另一个是配置匹配项,这里是对”/hello”这个进行拦截。(”/**”是对所有的访问拦截)

四、运行
访问 http://127.0.0.1:8080/hello?user=admin就可以看到结果啦。

  • 0
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值