For this requirement, the first thing that comes to mind 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.
The steps are as follows:
Use @Aspect to declare the aspect class WhitelistAspect;
Add a pointcut whitelistPointcut() in the aspect class. In order to realize the flexible and assembly-able ability of this pointcut, we do not use execution to intercept all, but add an annotation @Whitelist, and the annotated method is The whitelist will be checked.
Use spring's AOP annotation @Before in the aspect class to declare a notification method checkWhitelist() to verify the whitelist before the Controller method is executed.
The pseudo code of the aspect class is as follows:
@Aspect public class WhitelistAspect { @Before(value = "whitelistPointcut() && @annotation(whitelist)") public void checkAppkeyWhitelist(JoinPoint joinPoint, Whitelist whitelist) { checkWhitelist(); // 可使用 joinPoint.getArgs() 获取Controller方法的参数 // 可以使用 whitelist 变量获取注解参数 } @Pointcut("@annotation(com.zhenbianshu.Whitelist)") public void whitelistPointCut() { } }
Add the @Whitelist annotation to the Controller method to implement the function.
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, If the list is verified by UID, you can add methods such as uid() to this annotation to implement custom verification.
In addition, spring's AOP also supports pointcut declaration methods such as execution (execution method), bean (execution method of Bean objects matching specific names), and @Around (executed in the execution of the target function) , @After (after method execution) and other notification methods.
So, the function has been implemented, but the leader is not satisfied =_=. The reason is that AOP is used too much in the project and is overused. He suggested that I change the method. Well, I just had to start it. Also pay attention to: Ma Yuan Technology Column, reply in the background: "Interview Guide" can be obtained, the latest version of high-definition PDF 3625 pages of interview questions for major Internet companies.
Spring’s Interceptor is also very suitable to implement 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 Spring's HandlerInterceptor interface.
The implementation steps are as follows:
Define the interceptor class AppkeyInterceptor class and implement the HandlerInterceptor interface.
Implement its preHandle() method;
Use annotations and parameters in the preHandle method to determine whether the request needs to be intercepted. When intercepting the request, the interface returns false;
Register this interceptor in the custom 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(); 通过 request 获取请求参数,通过 whitelist 变量获取注解参数 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 方法在Controller方法执行结束后执行 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在view视图渲染完成后执行 } }
To enable the interceptor, you also need to explicitly configure it. Here we use WebMvcConfigurerAdapter to configure it. It should be noted 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); // 这里可以配置拦截器启用的 path 的顺序,在有多个拦截器存在时,任一拦截器返回 false 都会使后续的请求方法不再执行 } }
Also note that after the interceptor is executed successfully, the response code is 200, but the response data is empty.
After using the interceptor to implement the function, the leader finally came up with a big move: we already have an Auth parameter, the appkey can be obtained from the Auth parameter, and the presence or absence of the whitelist can be used as a method of Auth. Why? Not validating during Auth? emmm... Vomiting blood.
The parameter parser is a tool provided by Spring for parsing custom parameters. Our commonly used @RequestParam annotation has its shadow. Using it, we can The parameters are combined 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 sequence until a Resolver can parse the required parameters. To implement a parameter resolver, you need to implement the HandlerMethodArgumentResolver interface.
Define custom parameter type AuthParam, there are appkey related fields in the class;
Define AuthParamResolver And implement the HandlerMethodArgumentResolver interface;
Implement the supportsParameter() interface method to adapt AuthParam to AuthParamResolver;
Implement the resolveArgument() interface method to resolve The reqest object generates an AuthParam object, and verifies the AuthParam here to confirm whether the appkey is in the whitelist;
Add the AuthParam parameter in the signature on the Controller Action method to enable this Resolver;
The implemented AuthParamResolver class 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); // 通过 webRequest 和 whitelist 校验白名单 return new AuthParam(); } }
Of course, using the parameter parser also needs to be configured separately, we also need to Configuration in WebMvcConfigurerAdapter:
@Configuration public class MvcConfiguration extends WebMvcConfigurerAdapter { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new AuthParamResolver()); } }
After the implementation was completed this time, I was still a little worried, so I searched online to see if there were other ways to achieve this function, and found that Filter is a common one.
Filter 并不是 Spring 提供的,它是在 Servlet 规范中定义的,是 Servlet 容器支持的。被 Filter 过滤的请求,不会派发到 Spring 容器中。它的实现也比较简单,实现 javax.servlet.Filter接口即可。
由于不在 Spring 容器中,Filter 获取不到 Spring 容器的资源,只能使用原生 Java 的 ServletRequest 和 ServletResponse 来获取请求参数。
另外,在一个 Filter 中要显示调用 FilterChain 的 doFilter 方法,不然认为请求被拦截。实现类似:
public class WhitelistFilter implements javax.servlet.Filter {
@Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化后被调用一次 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 判断是否需要拦截 chain.doFilter(request, response); // 请求通过要显示调用 } @Override public void destroy() { // 被销毁时调用一次 } }
Filter 也需要显示配置:
@Configuration public class FilterConfiguration { @Bean public FilterRegistrationBean someFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new WhitelistFilter()); registration.addUrlPatterns("/*"); registration.setName("whitelistFilter"); registration.setOrder(1); // 设置过滤器被调用的顺序 return registration; } }
The above is the detailed content of How Springboot implements universal Auth authentication. For more information, please follow other related articles on the PHP Chinese website!