您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關怎么在Spring中實現擁有者權限驗證,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
問題描述
在做權限驗證的時候,我們經常會遇到這樣的情況:教師擁有多個學生,但是在處理學生信息的時候,教師只能操作自己班級的學生。所以,我們要做的就是,當教師嘗試處理別的班的學生的時候,拋出異常。
實體關系
用戶1:1教師
,教師m:n
班級,班級1:n
學生
實現思路
以findById
為例。因為從整體上看,用戶
和學生
是m:n
的關系,所以在調用這個接口的時候,獲取該學生的所有用戶
,然后跟當前登錄用戶
進行對比,如果不在其中,拋出異常。
利用切面,我們可以在findById
、update
、delete
方法上進行驗證。
注解
我們會在方法上添加注解,以表示對該方法進行權限驗證。
@Target(ElementType.METHOD) // 注解使用在方法上 @Retention(RetentionPolicy.RUNTIME) // 運行時生效 public @interface AuthorityAnnotation { /** * 倉庫名 */ @Required Class repository(); }
因為我們需要獲取出學生,但是并不限于學生,所以就要將倉庫repository
作為一個參數傳入。
實體
上面我們說過,需要獲取學生中的用戶,所以我們可以在實體中定義一個方法,獲取所有有權限的用戶:getBelongUsers()
但是,我們知道,學生和用戶沒用直接的關系,而且為了復用,在對其他實體進行驗證的時候也能使用,可以考慮創建一個接口,讓需要驗證的實體去實現他。
這樣,我們可以在讓每個實體都集成這個接口,然后形成鏈式調用,這樣就解決了上面你的兩個問題。
public interface BaseEntity { List<User> getBelongToUsers(); }
教師:
@Entity public class Teacher implements YunzhiEntity, BaseEntity { ... @Override public List<User> getBelongToUsers() { List<User> userList = new ArrayList<>(); userList.add(this.getUser()); return userList; } }
班級:
@Entity public class Klass implements BaseEntity { ... @Override public List<User> getBelongToUsers() { List<User> userList = new ArrayList<>(); for (Teacher teacher: this.getTeacherList()) { userList.addAll(teacher.getBelongToUsers()); } return userList; } }
學生:
@Entity public class Student implements BaseEntity { ... @Override public List<User> getBelongToUsers() { return this.getKlass().getBelongToUsers(); } }
切面
有了實體后,我們就可以建立切面實現驗證功能了。
@Aspect @Component public class OwnerAuthorityAspect { private static final Logger logger = LoggerFactory.getLogger(OwnerAuthorityAspect.class.getName()); /** * 使用注解,并第一個參數為id */ @Pointcut("@annotation(com.yunzhiclub.alice.annotation.AuthorityAnnotation) && args(id,..) && @annotation(authorityAnnotation)") public void doAccessCheck(Long id, AuthorityAnnotation authorityAnnotation) { } @Before("doAccessCheck(id, authorityAnnotation)") public void before(Long id, AuthorityAnnotation authorityAnnotation) { }
首先,我們要獲取到待操作對象
。但是在獲取對象之前,我們必須獲取到repository
。
這里我們利用applicationContext
來獲取倉庫bean
,然后再利用獲取到的bean,生成repository對象。
@Aspect @Component public class OwnerAuthorityAspect implements ApplicationContextAware { private ApplicationContext applicationContext = null; // 初始化上下文 ...... @Before("doAccessCheck(id, authorityAnnotation)") public void before(Long id, AuthorityAnnotation authorityAnnotation) { logger.debug("獲取注解上的repository, 并通過applicationContext來獲取bean"); Class<?> repositoryClass = authorityAnnotation.repository(); Object object = applicationContext.getBean(repositoryClass); logger.debug("將Bean轉換為CrudRepository"); CrudRepository<BaseEntity, Object> crudRepository = (CrudRepository<BaseEntity, Object>)object; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
該類實現了ApplicationContextAware
接口,通過setApplicationContext
函數獲取到了applicationContext
。
接下來,就是利用repository
獲取對象,然后獲取他的所屬用戶,再與當前登錄用戶進行比較。
@Before("doAccessCheck(id, authorityAnnotation)") public void before(Long id, AuthorityAnnotation authorityAnnotation) { logger.debug("獲取注解上的repository, 并通過applicationContext來獲取bean"); Class<?> repositoryClass = authorityAnnotation.repository(); Object object = applicationContext.getBean(repositoryClass); logger.debug("將Bean轉換為CrudRepository"); CrudRepository<BaseEntity, Object> crudRepository = (CrudRepository<BaseEntity, Object>)object; logger.debug("獲取實體對象"); Optional<BaseEntity> baseEntityOptional = crudRepository.findById(id); if(!baseEntityOptional.isPresent()) { throw new RuntimeException("對不起,未找到相關的記錄"); } BaseEntity baseEntity = baseEntityOptional.get(); logger.debug("獲取登錄用戶以及擁有者,并進行比對"); List<User> belongToTUsers = baseEntity.getBelongToUsers(); User currentLoginUser = userService.getCurrentLoginUser(); Boolean havePermission = false; if (currentLoginUser != null && belongToTUsers.size() != 0) { for (User user: belongToTUsers) { if (user.getId().equals(currentLoginUser.getId())) { havePermission = true; break; } } if (!havePermission) { throw new RuntimeException("權限不允許"); } } }
使用
在控制器的方法上使用注解:@AuthorityAnnotation
,傳入repository。
@RestController @RequestMapping("/student") public class StudentController { private final StudentService studentService; // 學生 @Autowired public StudentController(StudentService studentService) { this.studentService = studentService; } /** * 通過id獲取學生 * * @param id * @return */ @AuthorityAnnotation(repository = StudentRepository.class) @GetMapping("/{id}") @JsonView(StudentJsonView.get.class) public Student findById(@PathVariable Long id) { return studentService.findById(id); } }
出現的問題
實現之后,進行單元測試的過程中出現了問題。
@Test public void update() throws Exception { logger.info("獲取一個保存學生"); Student student = studentService.getOneSaveStudent(); Long id = student.getId(); logger.info("獲取一個更新學生"); Student newStudent = studentService.getOneUnSaveStudent(); String jsonString = JSONObject.toJSONString(newStudent); logger.info("發送更新請求"); this.mockMvc .perform(put(baseUrl + "/" + id) .cookie(this.cookie) .content(jsonString) .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()); }
400的錯誤,說明參數錯誤,參數傳的是實體,看下傳了什么:
我們看到,這個字段并不是我們實體中的字段,但是為什么序列化的時候出現了這個字段呢?
原因是這樣的,我們在實體中定義了一個getBelongToUsers
函數,然后JSONobject
在進行序列化的時候會根據實體中的getter
方法,獲取get
后面的為key
,也就是將belongToUsers
看做了字段。
所以就出現了上面傳實體字段多出的情況,從而引發了400的錯誤。
解決
我們不想JSONobject
在序列化的時候處理getBelongToUsers
,就需要聲明一下,這里用到了注解:@JsonIgnore
。這樣在序列化的時候就會忽略它。
@Entity public class Student implements BaseEntity { ...... @JsonIgnore @Override public List<User> getBelongToUsers() { return this.getKlass().getBelongToUsers(); } }
看完上述內容,你們對怎么在Spring中實現擁有者權限驗證有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。