Spring Boot 通過AOP和自定義註解實現許可權控制
阿新 • • 發佈:2020-06-24
相逢便是 緣 ,路過點個 贊 ^.^
原始碼: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());
}
}
複製程式碼