This paper introduces four ways to implement general auth in spring boot, including traditional AOP, interceptor, parameter parser and filter, and provides the corresponding example code. Finally, it briefly summarizes their execution sequence.
|Foreword
Recently, I have been inundated by endless business demands and have no time to breathe. Finally, I received a job that can let me break through the code comfort zone. The process of solving it was very tortuous. For a time, I doubted my life. However, the gains were also great. The code was not obvious, but I felt that I had erased the veil that java, Tomcat and Spring had been blocking my eyes. Their understanding has reached a new level.
I haven't output it for a long time, so I chose one aspect to summarize, hoping to learn more about other things in the process of combing. Due to the prosperous ecology of Java, there are a large number of articles dedicated to each module below. So I chose another angle, starting from practical problems, to connect these scattered knowledge. You can see it as a summary. For the extreme details of each module, you can turn to the official documents or see other blogs on the network.
The requirements are simple and clear. They are not the same as the flirtatious requirements proposed by the products: a general appkey white list verification function is added to our web framework. I hope it has better scalability.
This web framework is implemented by the Department pioneer based on Spring boot. It is between the business and the Spring framework. It does some business oriented general functions, such as log output, function switch, general parameter parsing, etc. It is usually transparent to the business. Recently, it has been busy doing a good job in requirements and code writing, and has never even noticed its existence.
|Traditional AOP
For this requirement, the first thought is of course the AOP interface provided by spring boot. You only need to add a pointcut before the Controller method, and then process the pointcut.
realization
Its use steps are as follows:
- Use @Aspect to declare the facet class WhitelistAspect;
- Add a pointcut whitelistPointcut() in the facet class. In order to realize the ability of flexible assembly of this pointcut, we do not use execution to intercept all, but add an annotation @Whitelist, and the annotated methods will verify the Whitelist.
- In the aspect class, use spring's AOP annotation @Before to declare a notification method checkWhitelist() to verify the whitelist Before the Controller method is executed.
The facet pseudo code is as follows:
@Aspect public class WhitelistAspect { @Before(value = "whitelistPointcut() && @annotation(whitelist)") public void checkAppkeyWhitelist(JoinPoint joinPoint, Whitelist whitelist) { checkWhitelist(); // Joinpoint Getargs() get the parameters of the Controller method // You can use the whitelist variable to get annotation parameters } @Pointcut("@annotation(com.zhenbianshu.Whitelist)") public void whitelistPointCut() { } }
Add on Controller method @Whitelist annotations implement functionality.
extend
In this example, annotations are used to declare pointcuts, and I have implemented annotation parameters to declare the whitelist to be verified. If other whitelists need to be added later, such as UID verification, uid() and other methods can be added to this annotation to realize user-defined verification.
In addition, spring's AOP also supports pointcut declaration methods such as execution (execution method), Bean (execution method of Bean object matching a specific name) and notification methods such as @Around (executed during the execution of the target function), @After (After the execution of the method).
Thus, the function has been realized, but the leader is not satisfied = \u=, The reason is that AOP is used too much in the project. I suggest that I change the way. Well, I have to start.
| Interceptor
Spring's interceptor is also very suitable for this function. As the name suggests, the interceptor is used to determine whether to execute this method through some parameters before the Action in the Controller is executed. To implement an interceptor, you can implement the HandlerInterceptor interface of spring.
realization
The implementation steps are as follows:
- Define the interceptor class AppkeyInterceptor class and implement the HandlerInterceptor interface.
- Implement its preHandle() method;
- Judge whether the request needs to be intercepted through annotations and parameters in the preHandle method. The interface returns false when intercepting the request;
- Register this interceptor in the customized WebMvcConfigurerAdapter class;
The AppkeyInterceptor class is as follows:
@Component public class WhitelistInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Whitelist whitelist = ((HandlerMethod) handler).getMethodAnnotation(Whitelist.class); // whitelist.values(); Obtain request parameters through request and annotation parameters through whitelist variable return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // Method is executed after the Controller method is executed } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Execute after the view view rendering is completed } }
extend
To enable the interceptor, you need to explicitly configure it. Here we use the WebMvcConfigurerAdapter to configure it. Note that the MvcConfiguration that inherits it needs to be in the ComponentScan path.
@Configuration public class MvcConfiguration extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new WhitelistInterceptor()).addPathPatterns("/*").order(1); // Here, you can configure the order of path s enabled by interceptors. When multiple interceptors exist, any interceptor that returns false will cause subsequent request methods not to be executed } }
It should also be noted that after the interceptor is successfully executed, the response code is 200, but the response data is empty. The Spring Boot basic tutorial and sample source code can be learned here: https://github.com/javastacks/javastack , very complete.
After using the interceptor to realize the function, the leaders finally made a big move: we already have an Auth parameter. The appkey can be obtained from the Auth parameter. It can be used as a way of Auth if it is not in the white list. Why not verify it during Auth? emmm... Vomiting blood.
| ArgumentResolver
The parameter parser is a tool provided by Spring to parse custom parameters. The @RequestParam annotation we commonly use has its shadow. Using it, we can combine parameters into what we want before entering the Controller Action.
Spring will maintain a ResolverList. When the request arrives, spring finds that there are custom type parameters (non basic types), and will try these resolvers in turn until a Resolver can resolve the required parameters. To implement a parameter parser, you need to implement the HandlerMethodArgumentResolver interface.
realization
- Define the user-defined parameter type AuthParam, and there are appkey related fields in the class;
- Define AuthParamResolver and implement the HandlerMethodArgumentResolver interface;
- Implement the supportsParameter() interface method to adapt AuthParam and AuthParamResolver;
- Implement the resolveArgument() interface method to parse the reqest object to generate the AuthParam object, and verify the AuthParam here to confirm whether the appkey is in the white list;
- Add AuthParam parameter to the signature on the Controller Action method to enable this Resolver;
The AuthParamResolver class implemented is as follows:
@Component public class AuthParamResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(AuthParam.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Whitelist whitelist = parameter.getMethodAnnotation(Whitelist.class); // Verify the whitelist through webRequest and whitelist return new AuthParam(); } }
extend
Of course, the parameter parser also needs to be configured separately. We also configure it in the WebMvcConfigurerAdapter:
@Configuration public class MvcConfiguration extends WebMvcConfigurerAdapter { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new AuthParamResolver()); } }
After the implementation this time, I was still a little worried, so I searched the Internet to see if there were other ways to realize this function. I found that the common one was Filter.
| Filter
Filter is not provided by Spring. It is defined in Servlet specification and supported by Servlet container. Requests filtered by the filter will not be sent to the Spring container. Its implementation is also relatively simple. It implements javax Servlet. The filter interface is OK.
Because it is not in the Spring container, the Filter cannot obtain the resources of the Spring container. It can only use the ServletRequest and ServletResponse of native Java to obtain the request parameters.
In addition, the doFilter method that calls FilterChain should be displayed in a Filter. Otherwise, the request is considered to be intercepted. Implementation similar:
public class WhitelistFilter implements javax.servlet.Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // Called once after initialization } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Judge whether interception is required chain.doFilter(request, response); // Request via call to display } @Override public void destroy() { // Called once when destroyed } }
extend
Filter also needs to display configuration:
@Configuration public class FilterConfiguration { @Bean public FilterRegistrationBean someFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new WhitelistFilter()); registration.addUrlPatterns("/*"); registration.setName("whitelistFilter"); registration.setOrder(1); // Set the order in which the filter is called return registration; } }
|Summary
Each of the four implementation methods has its own suitable scenario. What is the calling order between them?
Filter is implemented by Servlet. Naturally, it is called first. The Interceptor that is called later is intercepted. Naturally, it does not need to be processed later. Then, it is the parameter parser. Finally, it is the pointcut of the facet.