一文搞定登录权限验证,轻松掌握过滤器、拦截器与AOP

发布于:2024-07-03 ⋅ 阅读:(15) ⋅ 点赞:(0)


前言

对于权限验证的方式,我们一般知道可以采用以下几种方式进行控制:

  1. 过滤器
  2. 拦截器
  3. AOP
  4. Spring Gateway
  5. SpringSecurity
  6. Shiro

对于一个简单的权限验证,也没有必要使用三方框架这么麻烦。这里我通过使用过滤器,拦截器、AOP来进行登录验证,来了解三者的区别。

1. 过滤器(Filter)

1.1 定义

过滤器是Servlet规范中定义的一种对象,用于在请求被处理之前或者响应被发送到客户端之前进行拦截和处理。

1.2 特点

  • 可以处理HTTP请求和响应,是Servlet规范的一部分,与特定的框架无关
  • 只能操作HttpServletRequest和HttpServletResponse对象,不能访问Spring容器中的Bean
  • 可以用于编码转换、字符集设置、日志记录、安全控制等。

1.3 应用场景

  • 访问控制(如身份验证、权限控制)
  • 日志记录和审计
  • 压缩响应数据
  • 图片水印处理等

跨站脚本攻击,SQL注入等,可以采取Filter的方式,Filter是拦截所有的请求,包扩静态资源。

1.4 使用步骤

1.4.1 自定义Filter

/**
 * <p>
 * 登录过滤器
 * </p>
 *
 * @author shiqi
 * @version 1.0.0
 * @createTime 2024-07-02
 */
public class LoginFilter  implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化过滤器
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse)  servletResponse;

        // 获取会话中的用户信息
        Object user = httpRequest.getSession().getAttribute("user");

        // 获取请求的URL
        String requestURI = httpRequest.getRequestURI();

        // 允许访问登录页面和静态资源
        if (user == null && !requestURI.endsWith("/login") && !requestURI.contains("/static/")) {
            // 用户未登录,且请求不是登录页面或静态资源,重定向到登录页面
            httpResponse.setContentType("application/json;charset=UTF-8");
            httpResponse.getWriter().write("{\"code\":401,\"msg\":\"未登录\"}");
            return ;
        }

        // 用户已登录或请求是登录页面,继续处理请求
        chain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        // 销毁过滤器
    }
}

1.4.2 编写配置类

/**
 * <p>
 * 配置过滤器的配置类。
 * </p>
 *
 * @author shiqi
 * @version 1.0.0
 * @createTime 2024-07-02
 */
@Configuration
public class FilterConfig {
    /**
     * 配置LoginFilter的注册bean。
     * 该方法创建一个FilterRegistrationBean实例,用于注册LoginFilter,并配置其拦截的URL模式。
     * @return FilterRegistrationBean<LoginFilter> 登录过滤器的注册bean,用于Spring Boot应用程序中。
     */
    @Bean
    public FilterRegistrationBean<LoginFilter> loginFilter() {
        FilterRegistrationBean<LoginFilter> registrationBean = new FilterRegistrationBean<>();

        // 设置LoginFilter实例
        registrationBean.setFilter(new LoginFilter());
        // 指定LoginFilter应拦截的所有URL模式
        registrationBean.addUrlPatterns("/*"); // 拦截所有请求

        return registrationBean;
    }
}

2.拦截器

2.1 定义

拦截器是Spring MVC框架提供的一种机制,用于在请求处理的各个阶段(如处理器执行之前、之后、视图渲染之前)进行预处理和后处理。

2.2 特点

  • 是Spring MVC框架的一部分,依赖于Spring容器。
  • 可以访问Spring容器中的Bean,对业务处理方法进行增强。
  • 主要用于业务处理的预处理和后处理,如日志记录、权限检查、事务管理等。

2.3应用场景

  • 登录验证和权限控制
  • 日志记录和性能监控
  • 事务管理和异常处理

2.4 使用步骤

2.4.1 自定义拦截器

/**
 * <p>
 * 登录拦截器
 * </p>
 *
 * @author shiqi
 * @version 1.0.0
 * @createTime 2024-07-02
 */
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理之前进行调用(Controller方法调用之前)
        // 返回true表示放行,返回false表示拦截
        Object user = request.getSession().getAttribute("user");
        if (user == null) {
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write("未登录");
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 请求处理之后调用,但在视图被渲染之前(Controller方法调用之后)
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 整个请求完成之后调用,用于清理资源等
    }
}

2.4.2 注册拦截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 添加拦截器到拦截器注册表中。
     *
     * 本方法的目的是为了配置一个特定的拦截器,即LoginInterceptor,以拦截所有符合指定路径模式的请求。
     * 通过这种方式,可以对特定的URL路径进行额外的权限检查或逻辑处理,而不需要修改每个控制器或方法。
     *
     * @param registry 拦截器注册表,用于添加新的拦截器和配置它们的拦截规则。
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加LoginInterceptor拦截器,并指定它应该拦截所有以"/index/**"为路径模式的请求。
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/index/**");
    }
}

3. AOP

3.1 定义

面向切面编程是一种编程范式,用于通过定义横切关注点(如日志、事务、安全性)来减少代码重复性并增强模块化。

3.2 特点

  • 是一种横切关注点的编程思想,与具体框架(如Servlet或Spring MVC)无关。
  • 可以用于跨多个类和对象实现通用功能,如事务管理、日志记录、缓存等。
  • 基于代理机制实现,在运行时动态地将切面织入到程序流程中。

3.3 应用场景

  • 事务管理(如声明式事务)
  • 日志记录(如方法调用日志)
  • 安全控制(如权限检查)
  • 性能监控和缓存管理

3.4 使用步骤

3.4.1 自定义注解

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {

    String[] roles() default {};
}

3.4.2 定义切面

@Aspect
@Component
public class LoginAspect{

    @Pointcut("@annotation(com.shiqi.securitydemo.aop.RequirePermission)")
    public void pointCut() {

    }

    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

        // 执行目标方法
        // 获取方法签名
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 获取方法
        Method method = signature.getMethod();
        // 获取方法上的注解
        RequirePermission permission = method.getAnnotation(RequirePermission.class);
        String[] roles = permission.roles();
        if (roles.length > 0){
            // 验证权限
            for (String role : roles) {
                if(!role.equals("admin")){
                    // 抛出异常
                    return "权限不足";
                }

            }

        }


        return pjp.proceed();
    }
}

3.4.3 使用注解

@RestController
@RequestMapping
public class IndexController {


    @RequestMapping("/index")
    public String index() {
        return "index";
    }


    /**
     * 处理管理员相关的请求。
     *
     * 该方法映射到管理员界面的URL路径。只有具备管理员角色的用户才能访问该页面。
     * 通过返回字符串"admin",指示Spring MVC将请求重定向到名为"admin"的视图或页面。
     *
     * @return 表示管理员页面的字符串
     */
    @RequestMapping("/admin")
    @RequirePermission(roles = {"guest"})
    public String admin(){
        return "admin";
    }
}

4. 总结

对比及应用场景总结

4. 1 过滤器 vs 拦截器

  • 过滤器是Servlet规范的一部分,独立于具体框架,主要用于HTTP请求和响应的预处理和后处理,如字符编码、安全控制等。
  • 拦截器是Spring MVC框架的一部分,依赖于Spring容器,可以访问Spring管理的Bean,用于业务处理方法的预处理和后处理,如权限验证、事务管理等。

4.2 拦截器 vs AOP

  • 拦截器是针对特定的请求路径或者Controller进行拦截和增强,属于局部性的增强。
  • AOP是一种更为广泛的横切关注点的编程思想,可以跨多个类和对象实现通用功能的增强,如事务管理、日志记录等。

4.3 过滤器 vs AOP

  • 过滤器主要用于HTTP请求和响应的处理,功能较为局限,主要在Servlet规范中使用。
  • AOP能够更加灵活地实现横切关注点的编程,不限于HTTP请求,可以应用于各种业务逻辑中,实现功能的解耦和复用。