Spring Boot中使用注解拦截器实现通用校验器和基于角色的权限注解

news/2024/9/21 3:42:00/

通过使用Spring Boot的注解和拦截器,我们可以优雅地实现通用校验器和灵活的权限控制。本文将以电商交易系统为案例,详细讲解如何在Spring Boot中实现支持角色入参的权限校验器,以及如何通过注解拦截器实现通用校验器,提供高拓展性和可维护性的解决方案。

1. 背景介绍

在电商交易系统中,不同的用户角色(如普通用户、商家、管理员)拥有不同的操作权限。例如:

  • 普通用户:可以浏览商品、下单购买。
  • 商家用户:可以上架商品、管理库存。
  • 管理员:可以管理用户、审核商家、处理投诉。

为了确保系统的安全性和业务逻辑的正确性,我们需要对用户的操作进行校验和权限控制。传统的方法可能会在每个接口中添加重复的校验和权限判断代码,既不优雅也不利于维护。为了解决这个问题,我们可以使用Spring Boot的注解和拦截器机制,实现通用校验器和基于角色的权限控制。

2. Maven依赖

首先,我们需要在项目的pom.xml中添加必要的依赖:

<dependencies><!-- Spring Boot Starter Web,用于构建Web应用 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot Starter AOP,用于支持切面编程 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- 其他常用依赖,如数据库驱动 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
</dependencies>

引入这些依赖后,我们可以使用Spring Boot的Web和AOP功能来实现我们的需求。

3. 项目结构设计

为了清晰地展示项目的实现,我们可以将项目结构设计如下:

src/main/java/com/example/ecommerce
|-- annotation  // 自定义注解
|   |-- ValidateUser.java       // 通用校验注解
|   |-- RequiresRoles.java      // 支持角色入参的权限注解
|
|-- aspect       // 拦截器和注解处理器
|   |-- ValidationAspect.java   // 校验拦截器
|   |-- AuthorizationAspect.java // 权限拦截器
|
|-- controller   // 控制器层
|   |-- UserController.java
|   |-- ProductController.java
|   |-- OrderController.java
|
|-- service      // 服务层
|   |-- UserService.java
|   |-- ProductService.java
|   |-- OrderService.java
|
|-- model        // 实体类
|   |-- User.java
|   |-- Product.java
|   |-- Order.java
|
|-- util         // 工具类
|   |-- SessionUtil.java        // 会话管理
|
|-- exception    // 自定义异常
|   |-- AuthorizationException.java
|   |-- ValidationException.java

接下来,我们将详细介绍如何实现通用校验器和支持角色入参的权限注解。

4. 自定义注解实现通用校验器

4.1 定义通用校验注解

首先,我们需要定义一个通用的用户校验注解@ValidateUser,用于校验用户的登录状态和信息完整性。

package com.example.ecommerce.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateUser {String message() default "用户校验失败";
}

该注解可以标记在需要校验用户的控制器方法上,通过AOP在方法执行前进行拦截和校验。

4.2 实现校验拦截器

接下来,实现ValidationAspect类,对使用@ValidateUser注解的方法进行拦截。

package com.example.ecommerce.aspect;import com.example.ecommerce.annotation.ValidateUser;
import com.example.ecommerce.exception.ValidationException;
import com.example.ecommerce.model.User;
import com.example.ecommerce.util.SessionUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect
@Component
public class ValidationAspect {@Pointcut("@annotation(com.example.ecommerce.annotation.ValidateUser)")public void validateUserPointcut() {}@Before("validateUserPointcut()")public void doBefore(JoinPoint joinPoint) throws Throwable {User user = SessionUtil.getCurrentUser();// 校验用户是否已登录if (user == null) {throw new ValidationException("用户未登录");}// 校验用户信息是否完整if (user.getUsername() == null || user.getEmail() == null) {throw new ValidationException("用户信息不完整");}}
}

在这里,我们通过SessionUtil.getCurrentUser()获取当前用户(实际应用中可能从Session或Token中获取)。如果用户未登录或信息不完整,抛出自定义的ValidationException异常。

5. 基于角色的权限注解实现

5.1 定义支持角色入参的权限注解

为了实现更加灵活的权限控制,我们需要定义一个支持角色入参的权限注解@RequiresRoles

package com.example.ecommerce.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequiresRoles {/*** 需要的角色列表*/String[] roles();String message() default "无权限访问";
}

该注解可以接收一个角色数组roles,表示只有满足这些角色之一的用户才能访问标记的方法。

5.2 实现权限拦截器

接下来,实现AuthorizationAspect类,对使用@RequiresRoles注解的方法进行拦截和权限校验。

package com.example.ecommerce.aspect;import com.example.ecommerce.annotation.RequiresRoles;
import com.example.ecommerce.exception.AuthorizationException;
import com.example.ecommerce.model.User;
import com.example.ecommerce.util.SessionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Arrays;@Aspect
@Component
public class AuthorizationAspect {@Pointcut("@annotation(com.example.ecommerce.annotation.RequiresRoles)")public void requiresRolesPointcut() {}@Around("requiresRolesPointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {User user = SessionUtil.getCurrentUser();// 校验用户是否已登录if (user == null) {throw new AuthorizationException("用户未登录");}// 获取方法上的注解MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);// 获取需要的角色列表String[] roles = requiresRoles.roles();// 校验用户角色if (Arrays.stream(roles).noneMatch(role -> role.equals(user.getRole()))) {throw new AuthorizationException("无访问权限");}// 权限校验通过,执行方法return joinPoint.proceed();}
}

在这个拦截器中,我们:

  • 获取当前用户,并校验是否已登录。
  • 获取方法上的@RequiresRoles注解,提取需要的角色列表。
  • 校验当前用户的角色是否在需要的角色列表中。
  • 如果权限校验通过,执行目标方法;否则,抛出AuthorizationException异常。

6. 电商交易系统示例

接下来,我们将结合电商交易系统的实际场景,展示如何应用上述的通用校验器和基于角色的权限注解。

6.1 用户管理模块

首先,定义User实体类和SessionUtil工具类。

6.1.1 用户实体类
package com.example.ecommerce.model;public class User {private String username;private String email;private String role; // 用户角色,如 "user", "seller", "admin"// 构造方法、Getter和Setter方法
}
6.1.2 会话管理工具类
package com.example.ecommerce.util;import com.example.ecommerce.model.User;public class SessionUtil {// 模拟获取当前用户的方法public static User getCurrentUser() {// 实际应用中应从Session或Token中获取用户信息return new User("张三", "zhangsan@example.com", "user");}
}

6.2 商品管理模块

在商品管理模块中,不同的操作需要不同的权限。例如:

  • 普通用户:可以查看商品列表。
  • 商家用户:可以添加和修改商品。
  • 管理员:可以删除任何商品。
6.2.1 商品实体类
package com.example.ecommerce.model;public class Product {private Long id;private String name;private Double price;private String description;// 构造方法、Getter和Setter方法
}
6.2.2 商品控制器
package com.example.ecommerce.controller;import com.example.ecommerce.annotation.RequiresRoles;
import com.example.ecommerce.model.Product;
import com.example.ecommerce.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/product")
public class ProductController {@Autowiredprivate ProductService productService;// 所有用户都可以访问@GetMapping("/list")public List<Product> listProducts() {return productService.getAllProducts();}// 商家和管理员可以添加商品@RequiresRoles(roles = {"seller", "admin"})@PostMapping("/add")public String addProduct(@RequestBody Product product) {productService.addProduct(product);return "商品添加成功";}// 商家和管理员可以修改商品@RequiresRoles(roles = {"seller", "admin"})@PutMapping("/update")public String updateProduct(@RequestBody Product product) {productService.updateProduct(product);return "商品更新成功";}// 只有管理员可以删除商品@RequiresRoles(roles = {"admin"})@DeleteMapping("/delete/{id}")public String deleteProduct(@PathVariable Long id) {productService.deleteProduct(id);return "商品删除成功";}
}
6.2.3 商品服务层
package com.example.ecommerce.service;import com.example.ecommerce.model.Product;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class ProductService {// 模拟商品数据库private List<Product> productList;public List<Product> getAllProducts() {// 返回所有商品return productList;}public void addProduct(Product product) {// 添加商品到数据库productList.add(product);}public void updateProduct(Product product) {// 更新商品信息}public void deleteProduct(Long id) {// 从数据库删除商品}
}

6.3 订单管理模块

在订单管理模块中,用户下单需要校验登录状态和用户信息完整性。

6.3.1 订单实体类
package com.example.ecommerce.model;public class Order {private Long id;private Long productId;private Integer quantity;private String status;// 构造方法、Getter和Setter方法
}
6.3.2 订单控制器
package com.example.ecommerce.controller;import com.example.ecommerce.annotation.ValidateUser;
import com.example.ecommerce.model.Order;
import com.example.ecommerce.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate OrderService orderService;// 提交订单,需要校验用户@ValidateUser@PostMapping("/submit")public String submitOrder(@RequestBody Order order) {orderService.submitOrder(order);return "订单提交成功";}// 查询订单,需要登录@ValidateUser@GetMapping("/list")public List<Order> listOrders() {return orderService.getOrdersByUser();}
}
6.3.3 订单服务层
package com.example.ecommerce.service;import com.example.ecommerce.model.Order;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class OrderService {// 模拟订单数据库private List<Order> orderList;public void submitOrder(Order order) {// 提交订单逻辑orderList.add(order);}public List<Order> getOrdersByUser() {// 获取当前用户的订单列表return orderList;}
}

7. 拓展与总结

7.1 拓展思路

  1. 细化权限控制:可以进一步细化权限,例如增加@RequiresPermissions注解,基于具体的操作权限而非角色。

    public @interface RequiresPermissions {String[] permissions();String message() default "无操作权限";
    }
    
  2. 动态权限管理:将权限信息存储在数据库或配置中心,支持动态更新,避免硬编码角色和权限。

  3. 多重校验机制:结合参数校验、业务校验,构建更加完善的校验体系。

  4. 统一异常处理:使用@ControllerAdvice@ExceptionHandler统一处理校验和权限异常,提高代码的可维护性。

    @RestControllerAdvice
    public class GlobalExceptionHandler {@ExceptionHandler(ValidationException.class)public ResponseEntity<String> handleValidationException(ValidationException ex) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());}@ExceptionHandler(AuthorizationException.class)public ResponseEntity<String> handleAuthorizationException(AuthorizationException ex) {return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ex.getMessage());}
    }
    

为了简化代码并结合@RequiresRoles@ValidateUser的功能,我们可以创建一个新的注解@SecureAction,同时支持用户校验和基于角色的权限控制。通过这个注解,我们可以在一个地方实现对用户的校验和角色权限的判断,避免多次注解的重复使用。

定义新的注解@SecureAction

@SecureAction注解将结合@ValidateUser@RequiresRoles的功能,校验用户的登录状态并判断其角色是否符合要求。我们可以通过注解的参数传递需要的角色列表。

package com.example.ecommerce.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SecureAction {/*** 需要的角色列表,如果为空则只校验用户登录状态*/String[] roles() default {};String message() default "无权限或用户未登录";
}

在这个注解中,roles参数可选,如果不传递角色则只校验用户的登录状态,传递角色时则会校验用户是否具有指定的角色。

实现拦截器

SecureActionAspect拦截器将同时处理用户校验和角色校验。

package com.example.ecommerce.aspect;import com.example.ecommerce.annotation.SecureAction;
import com.example.ecommerce.exception.AuthorizationException;
import com.example.ecommerce.exception.ValidationException;
import com.example.ecommerce.model.User;
import com.example.ecommerce.util.SessionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Arrays;@Aspect
@Component
public class SecureActionAspect {@Pointcut("@annotation(com.example.ecommerce.annotation.SecureAction)")public void secureActionPointcut() {}@Around("secureActionPointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {User user = SessionUtil.getCurrentUser();// 校验用户是否已登录if (user == null) {throw new ValidationException("用户未登录");}// 获取方法上的注解MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();SecureAction secureAction = method.getAnnotation(SecureAction.class);// 获取需要的角色列表String[] roles = secureAction.roles();// 如果定义了角色,则进行角色校验if (roles.length > 0 && Arrays.stream(roles).noneMatch(role -> role.equals(user.getRole()))) {throw new AuthorizationException("无访问权限");}// 用户校验和角色校验通过,执行方法return joinPoint.proceed();}
}

示例:如何使用@SecureAction注解

在控制器方法上使用@SecureAction注解来同时实现用户登录状态和角色的校验:

package com.example.ecommerce.controller;import com.example.ecommerce.annotation.SecureAction;
import com.example.ecommerce.model.Product;
import com.example.ecommerce.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/product")
public class ProductController {@Autowiredprivate ProductService productService;// 所有用户都可以查看商品@GetMapping("/list")public List<Product> listProducts() {return productService.getAllProducts();}// 只有商家和管理员可以添加商品@SecureAction(roles = {"seller", "admin"})@PostMapping("/add")public String addProduct(@RequestBody Product product) {productService.addProduct(product);return "商品添加成功";}// 只有管理员可以删除商品@SecureAction(roles = {"admin"})@DeleteMapping("/delete/{id}")public String deleteProduct(@PathVariable Long id) {productService.deleteProduct(id);return "商品删除成功";}
}

7.2 总结

通过本文的介绍,我们学习了如何在Spring Boot中使用自定义注解和拦截器,实现通用的用户校验器和支持角色入参的权限注解。这样的设计具有以下优点:

  • 高可复用性:将校验和权限逻辑抽象为注解和拦截器,避免代码重复。
  • 高可维护性:当需要修改校验或权限逻辑时,只需修改拦截器代码,无需逐个修改业务代码。
  • 高拓展性:可以根据需求灵活添加新的校验规则或权限控制。
  • 增强代码可读性:业务代码中通过注解直观地表达了需要的校验和权限要求。

在实际项目中,合理地使用注解和拦截器,可以大大提高开发效率和代码质量。希望本文的内容对您有所帮助,能够在项目实践中灵活运用。


http://www.ppmy.cn/news/1528215.html

相关文章

企业专用智能云盘 | 帮助企业便捷管控企业文档 | 天锐绿盘云文档安全管理系统

由于当前多数企业内部的办公文件普遍散落于各员工电脑中&#xff0c;导致存在诸多潜在的文档使用风险。为优化团队协作效率&#xff0c;天 锐 绿盘是一款集文档统一管理、高效协同于一体的企业云盘&#xff0c;帮助企业解决文档管理中的诸多难题。 【地址&#xff1a;点击了解天…

2024永久激活版 Studio One 6 Pro for mac 音乐创作编辑软件 完美兼容

Studio One 6是一款功能强大的音乐制作软件&#xff0c;由PreSonus公司开发。它提供了全面的音频录制、编辑、混音和母带处理工具&#xff0c;适用于音乐制作人、音频工程师和创作人员。 Studio One 6拥有直观的用户界面&#xff0c;使用户能够快速而流畅地进行音乐创作。它采…

Elasticsearch集群的运维与管理

【1】安装启动ES 集群 &#xff08;1.1&#xff09;集群架构规划 OS  ES versionIpnode.nameRolecluster.namees basedirCentOS Linux release 7.8.2003 (Core)elasticsearch-7.14.1 192.168.175.132:9200 cluster&#xff1a;192.168.175.132:9301 node_1 node.mastertrue …

海外问卷调查:选择静态IP还是动态IP?

在全球化的市场研究中&#xff0c;海外问卷调查是一种重要的数据收集手段。然而&#xff0c;选择合适的IP类型对于确保调查的效率、质量和合法性至关重要。本文将探讨在进行海外问卷调查时&#xff0c;静态IP与动态IP的优劣&#xff0c;并提供决策指导。 一、IP类型基础 在深入…

单片机实现内存管理的C语言实现

在嵌入式系统&#xff08;如单片机&#xff09;中&#xff0c;内存资源非常有限&#xff0c;因此需要高效的内存管理机制。在这种情况下&#xff0c;可能无法使用标准的动态内存管理函数&#xff08;如 malloc 和 free&#xff09;。因此&#xff0c;通常需要设计一个自定义的内…

【java面试每日五题之基础篇一】(仅个人理解)

1. 怎么理解面向对象编程&#xff08;Object Oriented Programming&#xff0c;OOP&#xff09; 面向对象编程是一种编程范式&#xff0c;核心思想是将真实世界中的事物都抽象为对象&#xff0c;通过与代码中的对象进行交互从而实现各种需求&#xff0c;对于OOP中关键概念的理解…

基于python flask的高血压疾病预测分析与可视化系统的设计与实现,使用随机森林、决策树、逻辑回归、xgboost等机器学习库预测

研究背景 随着现代社会的快速发展&#xff0c;生活方式的改变和人口老龄化的加剧&#xff0c;心血管疾病&#xff0c;尤其是高血压&#xff0c;已成为全球范围内的重大公共健康问题。高血压是一种常见的慢性疾病&#xff0c;其主要特征是动脉血压持续升高。长期不控制的高血压…

【云原生监控】Prometheus监控系统

Prometheus监控系统 文章目录 Prometheus监控系统资源列表基础环境一、部署Prometheus服务1.1、解压1.2、配置systemctl启动1.3、监听端口1.4、访问Prometheus仪表盘 二、部署Node-Exporter2.1、解压2.2、配置systemctl启动2.3、监听端口2.4、访问node-exporter仪表盘 三、配置…