我们相信:世界是美好的,你是我也是。 来玩一下解压小游戏吧!

切面aspectAOP编程中的重要概念,除了aspect外,还存在着其它相关概念。本文以介绍概念建立印象为主线,所以,仅仅说明aspect的基本使用方法。对比以往概念的话,这个aspect就是个无侵入式的自动埋点的编程方式。它是无侵入式的,没有它也可以运行,有了它就增加了相关埋点功能,非常绿色环保。

苏南大叔:SpringBoot开发,如何使用切面aspect文件做埋点? - springboot-aspect
SpringBoot开发,如何使用切面aspect文件做埋点?(图3-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10openjdk@23.0.2IntelliJ IDEA 2024.3.4.1maven@3.3.2spring boot@2.5.4java@17mysql@5.7.26

项目代码

在苏南大叔的SpringBoot系列文章中,在UserController这个接口里面,需要做两个验证,分别是:

这两个验证逻辑,都是写在UserController.java文件内部的。范例如下:

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;
    //...
    @PostMapping
    public ResponseEntity<?> createUser(@RequestBody User user, HttpServletRequest request) {
        if (!isLoggedIn(request)) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User not logged in");
        }
        try {
            validateCsrfToken(request);
            return ResponseEntity.ok(userService.createUser(user));
        } catch (RuntimeException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
        }
    }
    private void validateCsrfToken(HttpServletRequest request) {
        String csrfToken = request.getHeader("X-CSRF-TOKEN");
        String sessionCsrfToken = (String) request.getSession().getAttribute("CSRF_TOKEN");
        if (csrfToken == null || !csrfToken.equals(sessionCsrfToken)) {
            throw new RuntimeException("Invalid CSRF Token");
        }
    }

    private boolean isLoggedIn(HttpServletRequest request) {
        return request.getSession().getAttribute("user") != null;
    }
}
Java

当然,这两个判断,都与session有关。回顾一下csrf初始化数据写入的过程:

@PostMapping("/login")
public String login(@RequestParam("username") String username,
        @RequestParam("password") String password,
        HttpSession session,
        Model model) {
    System.out.println(username+","+password);
    if (isValidUser(username, password)) {
        session.setAttribute("user", username);
        return "redirect:/success";
    } else {
        model.addAttribute("error", "Invalid username or password");
        return "login";
    }
}

@GetMapping("/success")
public String showSuccessPage(HttpSession session, Model model) {
    if (session.getAttribute("user") == null) {
        return "redirect:/login";
    }
    model.addAttribute("user", session.getAttribute("user"));
    // HttpSession session = request.getSession();
    String csrfToken = UUID.randomUUID().toString();
    session.setAttribute("csrfToken", csrfToken);
    model.addAttribute("csrfToken", csrfToken);
    return "success";
}
Java

AOP面向切面编程

面向切面编程是一种编程范式,Aspect-Oriented Programming用于处理程序中分散的、交叉的关注点(cross-cutting concerns),比如日志、安全性、事务管理等。

在没有AOP的情况下,这些关注点通常会散布在整个代码库中,导致代码重复和维护困难。AOP通过引入"Aspect"(切面)这一概念,允许开发者将这些关注点从业务逻辑中分离出来,从而提高代码的模块性和可维护性。

在面向切面编程(AOP)中,切面(Aspect) 是一个模块化的单元,用于封装横切关注点(Cross-Cutting Concerns)。横切关注点是那些跨越多个模块或功能的通用关注点,如日志记录、事务管理、安全性和性能监控等。

pom.xml

为了本文代码的实现,需要安装spring-boot-starter-aop

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
XML

修改pom.xml后,执行mvn install

aspect切面

然而登陆及csrf这两个验证逻辑,是可以通过aspect切面独立出去的。参考代码:

src/main/java/com/example/demo/aspect/SecurityAspect.java:

package com.example.demo.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Aspect
@Component
public class SecurityAspect {

    private final HttpServletRequest request;
    private static final String CSRF_TOKEN = "csrfToken";

    public SecurityAspect(HttpServletRequest request) {
        this.request = request;
    }

    @Before("execution(* com.example.demo.controller.UserController.*(..))")
    public void checkCsrfToken() {
        String method = request.getMethod();
        if (RequestMethod.GET.name().equals(method) ||
            RequestMethod.HEAD.name().equals(method) ||
            RequestMethod.OPTIONS.name().equals(method) ||
            RequestMethod.TRACE.name().equals(method)) {
            return;
        }

        HttpSession session = request.getSession();
        String sessionCsrfToken = (String) session.getAttribute(CSRF_TOKEN);
        String requestCsrfToken = request.getHeader("X-CSRF-Token");

        if (sessionCsrfToken == null || !sessionCsrfToken.equals(requestCsrfToken)) {
            throw new RuntimeException("CSRF token 验证失败");
        }
        System.out.println("CSRF token 验证通过");
    }

    @Before("execution(* com.example.demo.controller.UserController.*(..))")
    public void checkIfLoggedIn() {
        HttpSession session = request.getSession();
        String user = (String) session.getAttribute("user");

        if (user == null || user.isEmpty()) {
            throw new RuntimeException("用户未登录");
        }
        System.out.println("用户已登录");
    }
}
Java

苏南大叔:SpringBoot开发,如何使用切面aspect文件做埋点? - aspect代码
SpringBoot开发,如何使用切面aspect文件做埋点?(图3-2)

aspect切面说明

【范围定义】
在这个例子中,aspect通过定义Before,实现了对其它文件里面定义的Controller的定制。在checkCsrfToken()中,因为只有对数据进行修改的接口才需要csrf验证,所以要对接口请求的方法进行筛选。这个筛选也可以定义在Before里面,但是,目前由于经验限制,只能做到过滤最常见的Get请求。暂留后续文字更新。

【搭积木效果】
如果把这个aspect文件移走的话,代码并不会报错。只是目标controller失去了防火效果而已。所以,搭积木的效果非常明显。这个是非侵入式的埋点,效果非常好,非常理想!

运行效果

效果如下所示:

苏南大叔:SpringBoot开发,如何使用切面aspect文件做埋点? - 运行效果
SpringBoot开发,如何使用切面aspect文件做埋点?(图3-3)

对于正常的用户来说,并不会看到这些错误提示。但是,这些错误提示也确实太繁杂了。所以,在这里埋个伏笔,下篇文章解决这个问题。

结语

苏南大叔的SpringBoot系列文章,到目前为止,应该可以应付大多数的开发需求了。虽然写的不是太详细,但是如果以功利为目的的话,这已经足够了。

更多苏南大叔的java文章,可以点击下面的连接:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   java