В статье представлены четыре способа реализации общей аутентификации в Spring Boot, включая традиционный AOP, перехватчики, анализаторы параметров и фильтры, а также приведены соответствующие примеры кода. Наконец, для справки и изучения кратко изложен порядок их выполнения.
| Предисловие
"В последнее время я был перегружен бесконечными деловыми потребностями, и мне некогда было дышать. Я наконец получил работу, которая позволила мне вырваться из зоны комфорта кода. Процесс ее решения был очень извилистым. Очевидно, но я чувствую, что я стер слой завесы, которую Java, Tomcat и Spring закрывали мои глаза. Достигнут новый уровень понимания их.
Давно не выкладывал, поэтому выбираю один аспект для подведения итогов, надеясь узнать что-то еще в процессе разбора. Благодаря благополучной экологии Java, каждому из следующих модулей посвящено большое количество статей. Поэтому я выбрал другой ракурс, начиная с практических задач, и, соединив эти разрозненные знания воедино, можно посмотреть на них как на резюме. Для максимально подробного ознакомления с каждым модулем вы можете обратиться к официальной документации или прочитать другие блоги в Интернете.
Требования просты и понятны и совсем не совпадают с кокетливыми требованиями, предъявляемыми к продуктам: добавить в наш веб-фреймворк функцию проверки белого списка ключей приложения 通用
и надеяться, что его масштабируемость станет лучше.
Этот веб-фреймворк реализован пионерами отдела на основе Spring-Boot. Он находится между бизнесом и фреймворком Spring. Он выполняет некоторые общие функции, ориентированные на бизнес, такие как вывод журнала, переключение функций и общие параметры. анализ. Обычно это прозрачно для бизнеса.В последнее время я был занят составлением требований и хорошим написанием кода, и даже не заметил его существования.
|Метод 1. Традиционный АОП
При выполнении этого требования первое, что приходит на ум, это, конечно же, интерфейс AOP, предоставляемый Spring Boot. Вам нужно только добавить точку отсечения перед методом Controller, а затем обработать точку отсечения.
выполнить
Этапы его использования следующие:
-
Используйте
@Aspect
для объявления класса аспектаWhitelistAspect
; -
Добавьте точку отсечения в класс аспекта
whitelistPointcut()
. Чтобы реализовать гибкие и настраиваемые возможности этой точки отсечения, вместо использованияexecution
всех перехватов добавьте аннотацию@Whitelist
, и аннотированный метод будет проверять белый список. -
Используйте аннотации AOP Spring в классе аспектов, чтобы объявить
@Before
метод уведомленияcheckWhitelist()
для проверки белого списка перед выполнением метода контроллера.
Псевдокод фасетного класса выглядит следующим образом:
@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() {
}
}
Добавьте аннотации к методу Controller @Whitelist
для реализации функции.
расширять
В этом примере аннотации используются для объявления pointcut, и я реализовал белый список для проверки с помощью параметров аннотации. Если вам позже понадобится добавить другие белые списки, например UID для проверки, вы можете добавить эту аннотацию и другие методы для uid()
реализации пользовательская проверка.
Кроме того, Spring AOP также поддерживает execution(执行方法) 、bean(匹配特定名称的 Bean 对象的执行方法)
равные методы объявления pointcut и @Around(在目标函数执行中执行) 、@After(方法执行后)
равные методы уведомления.
Таким образом функция реализована, но руководитель не доволен. Причина в том, что АОП в проекте используется слишком много, и он перегружен. Предлагаю другой путь. Ну, мне пришлось это начать.
| Способ 2: использование перехватчика
Перехватчик Spring (Interceptor) также очень подходит для реализации этой функции. Как следует из названия, перехватчик используется для определения того, следует ли выполнять этот метод с помощью некоторых параметров перед выполнением действия в контроллере.Чтобы реализовать перехватчик, вы можете реализовать интерфейс Spring HandlerInterceptor
.
выполнить
Этапы реализации следующие:
-
Определите класс перехватчика
AppkeyInterceptor
и реализуйте интерфейс HandlerInterceptor. -
реализовать свой
preHandle()
метод; -
В методе preHandle судите, нужно ли перехватывать запрос, посредством аннотаций и параметров, и интерфейс возвращает результат при перехвате запроса
false
; -
WebMvcConfigurerAdapter
Зарегистрируйте этот перехватчик в пользовательском классе;
AppkeyInterceptor
Класс выглядит следующим образом:
@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视图渲染完成后执行
}
}
расширять
Чтобы включить перехватчик, необходимо явно настроить его для включения.Здесь мы используем WebMvcConfigurerAdapter
его для настройки. Следует отметить, что те, кто его наследуют, MvcConfiguration
должны находиться по пути ComponentScan.
@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WhitelistInterceptor()).addPathPatterns("/*").order(1);
// 这里可以配置拦截器启用的 path 的顺序,在有多个拦截器存在时,任一拦截器返回 false 都会使后续的请求方法不再执行
}
}
Следует также отметить, что после успешного выполнения перехватчика код ответа равен 200
, но данные ответа пусты.
После использования перехватчика для реализации функции лидер, наконец, сделал большой шаг: у нас уже есть параметр Auth, ключ приложения можно получить из параметра Auth, и если он находится в белом списке, его можно использовать как способ аутентификации. почему бы и нет, когда проверка подлинности? эммм... рвота кровью.
| Способ 3: используйте ArgumentResolver
Анализатор параметров — это инструмент, предоставляемый Spring для анализа пользовательских параметров. Наши часто используемые @RequestParam
аннотации имеют свою тень. Используя его, мы можем объединить параметры в то, что мы хотим, прежде чем войти в действие контроллера.
Spring будет поддерживать один ResolverList
. Когда запрос поступит, Spring обнаружит, что существуют параметры пользовательского типа (небазовые типы), и будет пробовать эти Resolvers по очереди, пока Resolver не сможет разрешить требуемые параметры. Чтобы реализовать анализатор параметров, вам необходимо реализовать HandlerMethodArgumentResolver
интерфейс.
выполнить
-
Определите тип пользовательского параметра
AuthParam
, и в классе появятся поля, связанные с ключом приложения; -
Определите
AuthParamResolver
и реализуйте интерфейс HandlerMethodArgumentResolver; -
Реализуйте
supportsParameter()
метод интерфейса для адаптации AuthParam и AuthParamResolver; -
Реализуйте
resolveArgument()
метод интерфейса для анализа объекта запроса для создания объекта AuthParam и проверьте AuthParam здесь, чтобы подтвердить, находится ли ключ приложения в белом списке; -
Добавьте параметр AuthParam в подпись метода действия контроллера, чтобы включить этот преобразователь;
Реализованный класс AuthParamResolver выглядит следующим образом:
@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();
}
}
расширять
Разумеется, использование парсера параметров тоже нужно настраивать отдельно, и мы его тоже настраиваем WebMvcConfigurerAdapter
внутренне :
@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthParamResolver());
}
}
После реализации на этот раз я все еще немного волнуюсь, поэтому поискал в Интернете, есть ли другие способы реализации этой функции, и обнаружил, что есть и другие распространенные Filter
.
| Способ 4: использовать фильтр
Фильтр не предоставляется Spring, он определен в спецификации сервлета и поддерживается контейнером сервлета. Запросы, отфильтрованные Filter, не будут отправляться в контейнер Spring. Его реализация также относительно проста, javax.servlet.Filter
достаточно реализовать интерфейс.
Поскольку его нет в контейнере Spring, Filter не может получить ресурсы контейнера Spring и может использовать только собственные Java ServletRequest и ServletResponse для получения параметров запроса.
Кроме того, в Filter метод doFilter FilterChain должен вызываться явно, иначе запрос считается перехваченным. Реализуйте что-то вроде:
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() {
// 被销毁时调用一次
}
}
расширять
Фильтр также должен отображать конфигурацию:
@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;
}
}
| Резюме
У четырех реализаций есть подходящие сценарии, так какова последовательность вызовов между ними?
Фильтр реализуется сервлетом.Естественно, он вызывается первым, а перехватчик вызывается позже.Естественно, его не нужно обрабатывать потом, потом парсер параметров и, наконец, точку отсечения аспекта.