問題描述
在做權限驗證的時候,我們經常會遇到這樣的情況:教師擁有多個學生,但是在處理學生信息的時候,教師只能操作自己班級的學生。所以,我們要做的就是,當教師嘗試處理別的班的學生的時候,拋出異常。
實體關系
用戶1:1教師
,教師m:n
班級,班級1:n
學生
實現思路
以findbyid
為例。因為從整體上看,用戶
和學生
是m:n
的關系,所以在調用這個接口的時候,獲取該學生的所有用戶
,然后跟當前登錄用戶
進行對比,如果不在其中,拋出異常。
利用切面,我們可以在findbyid
、update
、delete
方法上進行驗證。
注解
我們會在方法上添加注解,以表示對該方法進行權限驗證。
1
2
3
4
5
6
7
8
9
|
@target (elementtype.method) // 注解使用在方法上 @retention (retentionpolicy.runtime) // 運行時生效 public @interface authorityannotation { /** * 倉庫名 */ @required class repository(); } |
因為我們需要獲取出學生,但是并不限于學生,所以就要將倉庫repository
作為一個參數傳入。
實體
上面我們說過,需要獲取學生中的用戶,所以我們可以在實體中定義一個方法,獲取所有有權限的用戶:getbelongusers()
但是,我們知道,學生和用戶沒用直接的關系,而且為了復用,在對其他實體進行驗證的時候也能使用,可以考慮創建一個接口,讓需要驗證的實體去實現他。
這樣,我們可以在讓每個實體都集成這個接口,然后形成鏈式調用,這樣就解決了上面你的兩個問題。
1
2
3
|
public interface baseentity { list<user> getbelongtousers(); } |
教師:
1
2
3
4
5
6
7
8
9
10
|
@entity public class teacher implements yunzhientity, baseentity { ... @override public list<user> getbelongtousers() { list<user> userlist = new arraylist<>(); userlist.add( this .getuser()); return userlist; } } |
班級:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@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; } } |
學生:
1
2
3
4
5
6
7
8
|
@entity public class student implements baseentity { ... @override public list<user> getbelongtousers() { return this .getklass().getbelongtousers(); } } |
切面
有了實體后,我們就可以建立切面實現驗證功能了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@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對象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@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
獲取對象,然后獲取他的所屬用戶,再與當前登錄用戶進行比較。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
@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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@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); } } |
出現的問題
實現之后,進行單元測試的過程中出現了問題。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@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
。這樣在序列化的時候就會忽略它。
1
2
3
4
5
6
7
8
9
10
|
@entity public class student implements baseentity { ...... @jsonignore @override public list<user> getbelongtousers() { return this .getklass().getbelongtousers(); } } |
修改后的學生實體如上,其他實現了getbelongtousers
方法的,都需要做相同處理。
總結
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://segmentfault.com/a/1190000018442618