好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Spring实现拥有者权限验证的方法示例

问题描述

在做权限验证的时候,我们经常会遇到这样的情况:教师拥有多个学生,但是在处理学生信息的时候,教师只能操作自己班级的学生。所以,我们要做的就是,当教师尝试处理别的班的学生的时候,抛出异常。

实体关系

用户 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测试数据/a/1190000018442618

查看更多关于Spring实现拥有者权限验证的方法示例的详细内容...

  阅读:21次