Spring Boot 通過AOP和自定義註解實現許可權控制的方法
阿新 • • 發佈:2020-01-07
本文介紹了Spring Boot 通過AOP和自定義註解實現許可權控制,分享給大家,具體如下:
原始碼:https://github.com/yulc-coding/java-note/tree/master/aop
思路
- 自定義許可權註解
- 在需要驗證的介面上加上註解,並設定具體許可權值
- 資料庫許可權表中加入對應介面需要的許可權
- 使用者登入時,獲取當前使用者的所有許可權列表放入Redis快取中
- 定義AOP,將切入點設定為自定義的許可權
- AOP中獲取介面註解的許可權值,和Redis中的資料校驗使用者是否存在該許可權,如果Redis中沒有,則從資料庫獲取使用者許可權列表,再校驗
pom檔案 引入AOP
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- AOP 切面--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
自定義註解 VisitPermission
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface VisitPermission { /** * 用於配置具體介面的許可權值 * 在資料庫中新增對應的記錄 * 使用者登入時,將使用者所有的許可權列表放入redis中 * 使用者訪問介面時,將對應介面的值和redis中的匹配看是否有訪問許可權 * 使用者退出登入時,清空redis中對應的許可權快取 */ String value() default ""; }
需要設定許可權的介面上加入註解 @VisitPermission(value)
@RestController @RequestMapping("/permission") public class PermissionController { /** * 配置許可權註解 @VisitPermission("permission-test") * 只用擁有該許可權的使用者才能訪問,否則提示非法操作 */ @VisitPermission("permission-test") @GetMapping("/test") public String test() { System.out.println("================== step 3: doing =================="); return "success"; } }
定義許可權AOP
- 設定切入點為@annotation(VisitPermission)
- 獲取請求中的token,校驗是否token是否過期或合法
- 獲取註解中的許可權值,校驗當前使用者是否有訪問許可權
- MongoDB 記錄訪問日誌(IP、引數、介面、耗時等)
@Aspect @Component public class PermissionAspect { /** * 切入點 * 切入點為包路徑下的:execution(public * org.ylc.note.aop.controller..*(..)): * org.ylc.note.aop.Controller包下任意類任意返回值的 public 的方法 * <p> * 切入點為註解的: @annotation(VisitPermission) * 存在 VisitPermission 註解的方法 */ @Pointcut("@annotation(org.ylc.note.aop.annotation.VisitPermission)") private void permission() { } /** * 目標方法呼叫之前執行 */ @Before("permission()") public void doBefore() { System.out.println("================== step 2: before =================="); } /** * 目標方法呼叫之後執行 */ @After("permission()") public void doAfter() { System.out.println("================== step 4: after =================="); } /** * 環繞 * 會將目標方法封裝起來 * 具體驗證業務資料 */ @Around("permission()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("================== step 1: around =================="); long startTime = System.currentTimeMillis(); /* * 獲取當前http請求中的token * 解析token : * 1、token是否存在 * 2、token格式是否正確 * 3、token是否已過期(解析資訊或者redis中是否存在) * */ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String token = request.getHeader("token"); if (StringUtils.isEmpty(token)) { throw new RuntimeException("非法請求,無效token"); } // 校驗token的業務邏輯 // ... /* * 獲取註解的值,並進行許可權驗證: * redis 中是否存在對應的許可權 * redis 中沒有則從資料庫中獲取許可權 * 資料空中沒有,拋異常,非法請求,沒有許可權 * */ Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod(); VisitPermission visitPermission = method.getAnnotation(VisitPermission.class); String value = visitPermission.value(); // 校驗許可權的業務邏輯 // List<Object> permissions = redis.get(permission) // db.getPermission // permissions.contains(value) // ... System.out.println(value); // 執行具體方法 Object result = proceedingJoinPoint.proceed(); long endTime = System.currentTimeMillis(); /* * 記錄相關執行結果 * 可以存入MongoDB 後期做資料分析 * */ // 列印請求 url System.out.println("URL : " + request.getRequestURL().toString()); // 列印 Http method System.out.println("HTTP Method : " + request.getMethod()); // 列印呼叫 controller 的全路徑以及執行方法 System.out.println("controller : " + proceedingJoinPoint.getSignature().getDeclaringTypeName()); // 呼叫方法 System.out.println("Method : " + proceedingJoinPoint.getSignature().getName()); // 執行耗時 System.out.println("cost-time : " + (endTime - startTime) + " ms"); return result; } }
單元測試
package org.ylc.note.aop; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.ylc.note.aop.controller.PermissionController; @SpringBootTest class AopApplicationTests { @Autowired private PermissionController permissionController; private MockMvc mvc; @BeforeEach void setupMockMvc() { mvc = MockMvcBuilders.standaloneSetup(permissionController).build(); } @Test void apiTest() throws Exception { MvcResult result = mvc.perform(MockMvcRequestBuilders.get("/permission/test") .accept(MediaType.APPLICATION_JSON) .header("token","9527")) .andReturn(); System.out.println("api test result : " + result.getResponse().getContentAsString()); } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。