In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
Today, I will talk to you about how to use AOP section in SpringBoot to achieve a permission check. Many people may not know much about it. In order to make you understand better, the editor has summarized the following content for you. I hope you can get something according to this article.
1 understand what AOP1.1 is AOP
AOP (Aspect Oriented Programming), aspect-oriented thinking, is one of the three core ideas of Spring (two out of two: IOC- control inversion, DI- dependency injection).
So why is AOP so important?
In our program, there are often some systematic requirements, such as permission verification, logging, statistics, etc., these codes will be scattered in various business logic, very redundant and not easy to maintain. For example, the following diagram:
It is obviously unacceptable to write as many duplicate checksum logging codes as there are business operations. Of course, with object-oriented thinking, we can extract these repetitive code and write them into public methods, just like this:
In this way, the problems of code redundancy and maintainability are solved, but it is also a bit cumbersome to manually call these public methods in turn in each business method. Is there a better way? Some, that is, AOP,AOP completely extracts permission verification, logging and other non-business codes, separates them from business codes, and looks for nodes to cut into business codes:
1.2 AOP system and concept
To simply understand, AOP actually does three kinds of things:
Where to cut in, that is, permission verification and other non-business operations are performed in which business code.
When to cut in, before or after the execution of the business code.
What to do after cutting in, such as permission verification, logging and so on.
Therefore, the system of AOP can be combed into the following figure:
Some concepts are explained in detail:
Pointcut: pointcut to decide where processing, such as permission verification, logging, and so on, should be cut into the business code (that is, weaving into the aspect). The pointcut can be divided into execution mode and annotation mode. The former can use path expressions to specify which classes are woven into the aspect, and the latter can specify which annotations modify the code to weave into the aspect.
Advice: processing, including timing and content. To deal with content is to do something, such as verifying permissions and logging. Processing time is when to execute the processing content, which is divided into pre-processing (before the execution of business code), post-processing (after the execution of business code) and so on.
Aspect: section, namely Pointcut and Advice.
Joint point: a connection point, which is a point at which a program executes. For example, the execution of a method or the handling of an exception. In Spring AOP, a join point always executes on behalf of a method.
Weaving: weaving is the process of processing content in a target object method through a dynamic proxy.
There is a picture on the Internet, which I think is very vivid. I post it here for everyone to see:
2 AOP instance
Real knowledge comes from practice, and then let's play with the code to implement AOP. The completed project has been uploaded to:
Https://github.com/ThinkMugz/aopDemo
To use AOP, you first need to introduce the dependency of AOP. Parameter check: write parameter check (validator) so that you won't be persuaded to quit.
Org.springframework.boot
Spring-boot-starter-aop
2.1 first instance
Next, let's look at a minimalist example: all get requests output the words "the advice of the get request is triggered" on the console before being called.
The specific implementation is as follows:
To create an AOP aspect class, simply add a @ Aspect annotation to the class. The @ Aspect annotation is used to describe an aspect class, which is required when defining an aspect class. The @ Component annotation gives the class to Spring to manage. Implement advice:package com.mu.demo.advice in this class
Import org.aspectj.lang.annotation.Aspect
Import org.aspectj.lang.annotation.Before
Import org.aspectj.lang.annotation.Pointcut
Import org.springframework.stereotype.Component
@ Aspect
@ Component
Public class LogAdvice {
/ / define a pointcut: all methods modified by GetMapping annotations are woven into advice
@ Pointcut ("@ annotation (org.springframework.web.bind.annotation.GetMapping)")
Private void logAdvicePointcut () {}
/ / Before indicates that logAdvice will execute before the target method executes
@ Before ("logAdvicePointcut ()")
Public void logAdvice () {
/ / this is just an example. You can write any processing logic.
System.out.println ("the advice of the get request triggered")
}
}
Create an interface class and internally create a get request: package com.mu.demo.controller
Import com.alibaba.fastjson.JSON
Import com.alibaba.fastjson.JSONObject
Import org.springframework.web.bind.annotation.*
@ RestController
@ RequestMapping (value = "/ aop")
Public class AopController {
@ GetMapping (value = "/ getTest")
Public JSONObject aopTest () {
Return JSON.parseObject ("{\" message\ ":\" SUCCESS\ ",\" code\ ": 200}")
}
@ PostMapping (value = "/ postTest")
Public JSONObject aopTest2 (@ RequestParam ("id") String id) {
Return JSON.parseObject ("{\" message\ ":\" SUCCESS\ ",\" code\ ": 200}")
}
}
After the project is started, request the http://localhost:8085/aop/getTest API:
Insert a picture description here
The http://localhost:8085/aop/postTest API is requested, and there is no output on the console, which proves that the pointcut is only for the methods modified by GetMapping.
2.2 second instance
Let's complicate the problem a little bit. The scenario in this example is:
Customize an annotation PermissionsAnnotation
Create a facet class, set the pointcut to intercept all methods marked with PermissionsAnnotation, intercept the parameters to the interface, and perform simple permission verification.
Mark PermissionsAnnotation on the test interface test of the test interface class
Specific implementation steps:
Use @ Target, @ Retention, @ Documented to customize an annotation: @ Target (ElementType.METHOD)
@ Retention (RetentionPolicy.RUNTIME)
@ Documented
Public @ interface PermissionAnnotation {
}
To create the first AOP aspect class, simply add a @ Aspect annotation to the class. The @ Aspect annotation is used to describe an aspect class, which is required when defining an aspect class. The @ Component annotation gives the class to Spring to manage. Implement the first step of permission verification logic in this class: package com.example.demo
Import com.alibaba.fastjson.JSON
Import com.alibaba.fastjson.JSONObject
Import org.aspectj.lang.ProceedingJoinPoint
Import org.aspectj.lang.annotation.Around
Import org.aspectj.lang.annotation.Aspect
Import org.aspectj.lang.annotation.Pointcut
Import org.springframework.core.annotation.Order
Import org.springframework.stereotype.Component
@ Aspect
@ Component
@ Order (1)
Public class PermissionFirstAdvice {
/ / define a section, and write the path of the custom comment in step 1 in parentheses
@ Pointcut ("@ annotation (com.mu.demo.annotation.PermissionAnnotation)")
Private void permissionCheck () {
}
@ Around ("permissionCheck ()")
Public Object permissionCheckFirst (ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println ("= first section =:" + System.currentTimeMillis ())
/ / get the request parameters. For more information, please see API class.
Object [] objects = joinPoint.getArgs ()
Long id = ((JSONObject) objects [0]) .getLong ("id")
String name = ((JSONObject) objects [0]) .getString ("name")
System.out.println ("id1- > >" + id)
System.out.println ("name1- > >" + name)
/ / if id is less than 0, an illegal id exception is thrown
If (id
< 0) { return JSON.parseObject("{\"message\":\"illegal id\",\"code\":403}"); } return joinPoint.proceed(); } } 创建接口类,并在目标方法上标注自定义注解 PermissionsAnnotation :package com.example.demo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping(value = "/permission") public class TestController { @RequestMapping(value = "/check", method = RequestMethod.POST) // 添加这个注解 @PermissionsAnnotation() public JSONObject getGroupList(@RequestBody JSONObject request) { return JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200}"); } } 在这里,我们先进行一个测试。首先,填好请求地址和header: 其次,构造正常的参数: 可以拿到正常的响应结果: 然后,构造一个异常参数,再次请求: 响应结果显示,切面类进行了判断,并返回相应结果: 有人会问,如果我一个接口想设置多个切面类进行校验怎么办?这些切面的执行顺序如何管理? 很简单,一个自定义的AOP注解可以对应多个切面类,这些切面类执行顺序由@Order注解管理,该注解后的数字越小,所在切面类越先执行。 下面在实例中进行演示: 创建第二个AOP切面类,在这个类里实现第二步权限校验: package com.example.demo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Component @Order(0) public class PermissionSecondAdvice { @Pointcut("@annotation(com.example.demo.PermissionsAnnotation)") private void permissionCheck() { } @Around("permissionCheck()") public Object permissionCheckSecond(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("===================第二个切面===================:" + System.currentTimeMillis()); //获取请求参数,详见接口类 Object[] objects = joinPoint.getArgs(); Long id = ((JSONObject) objects[0]).getLong("id"); String name = ((JSONObject) objects[0]).getString("name"); System.out.println("id->> "+ id)
System.out.println ("name- > >" + name)
/ / if name is not an administrator, an exception is thrown
If (! name.equals ("admin")) {
Return JSON.parseObject ("{\" message\ ":\" not admin\ ",\" code\ ": 403}")
}
Return joinPoint.proceed ()
}
}
Restart the project, continue testing, and construct a situation where both parameters are abnormal:
In response to the result, the second aspect class on the surface is executed in a higher order:
3 AOP related notes
In the above case, a lot of notes are used, which are explained in detail below.
3. 1 @ Pointcut
The @ Pointcut annotation is used to define an aspect, that is, the entrance to something concerned above, and the pointcut defines the event trigger time.
@ Aspect
@ Component
Public class LogAspectHandler {
/ * *
* define an aspect to intercept all methods under com.itcodai.course09.controller packages and subpackages
, /
@ Pointcut ("execution (* com.mutest.controller..*.* (..)")
Public void pointCut () {}
}
The @ Pointcut annotation specifies an aspect that defines what needs to be intercepted. Here are two common expressions: one is to use execution () and the other is to use annotation ().
More reference: SpringBoot content aggregation
Execution expression:
With execution (* * com.mutest.controller..*.* (..)) An expression is an example:
The location of the first * sign: indicates the return value type, and * indicates all types.
Package name: indicates the name of the package to be intercepted. The next two periods represent the current package and all the subpackages of the current package. In this case, it refers to the methods of all classes under the com.mutest.controller package and subpackages.
The location of the second * sign: indicates the class name, and * represents all classes.
* (..): this asterisk indicates the name of the method, * indicates all methods, followed by parentheses indicates the parameters of the method, and the two periods indicate any parameters.
Annotation () expression:
Annotation () defines a section for an annotation. For example, if we do a section for a method with @ PostMapping annotation, we can define the section as follows:
@ Pointcut ("@ annotation (org.springframework.web.bind.annotation.PostMapping)")
Public void annotationPointcut () {}
If you then use this section, you will cut in all methods where the annotation is @ PostMapping. This approach is ideal for scenarios where @ GetMapping, @ PostMapping, and @ DeleteMapping annotations have a variety of specific processing logic.
Also, as shown in the case above, the aspect is defined for custom annotations.
@ Pointcut ("@ annotation (com.example.demo.PermissionsAnnotation)")
Private void permissionCheck () {}
3.2 @ Around
The @ Around annotation is used to modify Around enhancement processing, which is very powerful, as shown in:
@ Around is free to choose the order in which the enhanced action and the target method are executed, that is, the target method can be executed before and after the enhanced action, or even during the process. The implementation of this feature is that the target method is executed only when the procedd () method of the ProceedingJoinPoint parameter is called.
@ Around can change the parameter value of the execution target method or the return value after the execution of the target method.
Around enhancement processing has the following characteristics:
When defining an Around enhancement method, the first parameter of the method must be of type ProceedingJoinPoint (at least one parameter). In the body of the enhancement processing method, calling the proceed method of ProceedingJoinPoint will execute the target method: this is the key that the @ Around enhancement processing can fully control when and how the target method executes; if the program does not call the proceed method of ProceedingJoinPoint, the target method will not execute.
When you call the proceed method of ProceedingJoinPoint, you can also pass in an Object [] object, and the values in this array are passed into the target method as arguments-- which is the key to how the Around enhancement processing method can change the parameter values of the target method. This means that if the length of the Object [] array passed in is not equal to the number of parameters required by the target method, or if the Object [] array element does not match the type of parameters required by the target method, the program will encounter an exception.
@ Around, while powerful, usually needs to be used in a thread-safe environment. Therefore, if you use ordinary Before, AfterReturning can solve the problem, there is no need to use Around. If you need to share some state data before and after the execution of the target method, you should consider using Around. In particular, when you need to use enhancement processing to block the execution of the target, or if you need to change the return value of the target method, you can only use Around enhancement processing.
Next, make some modifications to the previous example to observe the features of @ Around.
The custom annotation class remains unchanged. First, define the interface class:
Package com.example.demo
Import com.alibaba.fastjson.JSON
Import com.alibaba.fastjson.JSONObject
Import org.springframework.web.bind.annotation.*
@ RestController
@ RequestMapping (value = "/ permission")
Public class TestController {
@ RequestMapping (value = "/ check", method = RequestMethod.POST)
@ PermissionsAnnotation ()
Public JSONObject getGroupList (@ RequestBody JSONObject request) {
Return JSON.parseObject ("{\" message\ ":\" SUCCESS\ ",\" code\ ": 200,\" data\ ":" + request + "}")
}
}
Unique aspect class (there are two aspect classes in the previous case, and you only need to keep one here):
Package com.example.demo
Import com.alibaba.fastjson.JSONObject
Import org.aspectj.lang.ProceedingJoinPoint
Import org.aspectj.lang.annotation.Around
Import org.aspectj.lang.annotation.Aspect
Import org.aspectj.lang.annotation.Pointcut
Import org.springframework.core.annotation.Order
Import org.springframework.stereotype.Component
@ Aspect
@ Component
@ Order (1)
Public class PermissionAdvice {
@ Pointcut ("@ annotation (com.example.demo.PermissionsAnnotation)")
Private void permissionCheck () {
}
@ Around ("permissionCheck ()")
Public Object permissionCheck (ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println ("= start enhancement processing =")
/ / get the request parameters. For more information, please see API class.
Object [] objects = joinPoint.getArgs ()
Long id = ((JSONObject) objects [0]) .getLong ("id")
String name = ((JSONObject) objects [0]) .getString ("name")
System.out.println ("id1- > >" + id)
System.out.println ("name1- > >" + name)
/ / modify input parameters
JSONObject object = new JSONObject ()
Object.put ("id", 8)
Object.put ("name", "lisi")
Objects [0] = object
/ / pass in the modified parameters
Return joinPoint.proceed (objects)
}
}
Also use JMeter to call the interface, passing in parameters: {"id":-5, "name": "admin"}. The response result shows that @ Around intercepts the input parameter of the interface and returns the result in the aspect class.
3. 3 @ Before
The method specified in the @ Before annotation is executed before the section cuts into the target method. You can do some Log processing and statistics of some information, such as obtaining the user's request URL and the user's IP address, etc., which can be used when building a personal site. For example, the following code:
@ Aspect
@ Component
@ Slf4j
Public class LogAspectHandler {
/ * *
* execute the section method before the section method defined above
* @ param joinPoint jointPoint
, /
@ Before ("pointCut ()")
Public void doBefore (JoinPoint joinPoint) {
Log.info ("= doBefore method enters =")
/ / obtain signature
Signature signature = joinPoint.getSignature ()
/ / get the cut-in package name
String declaringTypeName = signature.getDeclaringTypeName ()
/ / get the name of the method to be executed
String funcName = signature.getName ()
Log.info ("method to be executed is: {}, belonging to {} package", funcName, declaringTypeName)
/ / it can also be used to record some information, such as getting the URL and IP of the request
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes ()
HttpServletRequest request = attributes.getRequest ()
/ / get the request URL
String url = request.getRequestURL () .toString ()
/ / get the request IP
String ip = request.getRemoteAddr ()
Log.info ("url requested by the user is: {}, ip address is: {}", url, ip)
}
}
The JointPoint object is useful for getting a signature, which allows you to get the requested package name, method name, including parameters (obtained through joinPoint.getArgs ()), and so on.
3.4 @ After
The @ After annotation corresponds to the @ Before annotation. The specified method is executed after the section is cut into the target method, or you can do some Log processing after the completion of a certain method.
@ Aspect
@ Component
@ Slf4j
Public class LogAspectHandler {
/ * *
* define an aspect to intercept all methods under the com.mutest.controller package
, /
@ Pointcut ("execution (* com.mutest.controller..*.* (..)")
Public void pointCut () {}
/ * *
* execute the section method after the section method defined above
* @ param joinPoint jointPoint
, /
@ After ("pointCut ()")
Public void doAfter (JoinPoint joinPoint) {
Log.info ("= doAfter method enters =")
Signature signature = joinPoint.getSignature ()
String method = signature.getName ()
Log.info ("method {} has been executed", method)
}
}
At this point, let's write a Controller test and check the execution result. Create a new AopController as follows:
@ RestController
@ RequestMapping ("/ aop")
Public class AopController {
@ GetMapping ("/ {name}")
Public String testAop (@ PathVariable String name) {
Return "Hello" + name
}
}
Start the project, type: localhost:8080/aop/csdn in the browser, and observe the output of the console:
The doBefore method has entered
The method to be executed is: testAop, which belongs to the com.itcodai.mutest.AopController package
The url requested by the user is as follows: http://localhost:8080/aop/name Magi IP address: 0rig 0rig 0rig 0lu 0lu 0lu 0l.
The doAfter method has entered
The method testAop has been executed
From the printed Log, you can see the logic and order of program execution, and you can intuitively grasp the actual role of @ Before and @ After annotations.
3. 5 @ AfterReturning
The @ AfterReturning annotation is similar to @ After, except that the @ AfterReturning annotation can be used to capture the return value after the execution of the cut-in method, and to enhance the business logic of the return value, such as:
@ Aspect
@ Component
@ Slf4j
Public class LogAspectHandler {
/ * *
* execute the section method defined above after it returns to capture or enhance the returned object
* @ param joinPoint joinPoint
* @ param result result
, /
@ AfterReturning (pointcut = "pointCut ()", returning = "result")
Public void doAfterReturning (JoinPoint joinPoint, Object result) {
Signature signature = joinPoint.getSignature ()
String classMethod = signature.getName ()
Log.info ("method {} is finished, return parameter is: {}", classMethod, result)
/ / specific return value enhancements can be made according to the business in the actual project
Log.info ("Business enhancements to return parameters: {}", result + "enhanced")
}
}
It is important to note that in the @ AfterReturning annotation, the value of the attribute returning must be consistent with the parameter, otherwise it will not be detected. The second input parameter in this method is the return value of the cut method, which can be enhanced in the doAfterReturning method and encapsulated according to business needs. Let's restart the service and test it again:
After the execution of the method testAop, the return parameter is: Hello CSDN
Business enhancements to return parameters: Hello CSDN enhancements
3.6 @ AfterThrowing
When an exception is thrown during the execution of the cut method, it will be executed in the @ AfterThrowing annotated method, where you can do some exception handling logic. Note that the value of the throwing property must be consistent with the parameter, otherwise an error will be reported. The second input parameter in this method is the thrown exception.
@ Aspect
@ Component
@ Slf4j
Public class LogAspectHandler {
/ * *
* execute the section method defined above when it throws an exception
* @ param joinPoint jointPoint
* @ param ex ex
, /
@ AfterThrowing (pointcut = "pointCut ()", throwing = "ex")
Public void afterThrowing (JoinPoint joinPoint, Throwable ex) {
Signature signature = joinPoint.getSignature ()
String method = signature.getName ()
/ / Logic for handling exceptions
Log.info ("execution method {} error, exception: {}", method, ex)
}
}
After reading the above, do you have any further understanding of how to implement a permission check in AOP section of SpringBoot? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.