Введение: эта статья является завершением серии "Проектирование и реализация аутентификации, аутентификации и управления разрешениями API в микросервисной архитектуре". Первые три статьи объясняют процесс и основные детали аутентификации, аутентификации и управления разрешениями API. Эта статья относительно длинная, и эта серия завершается.Основное содержание включает в себя конечные точки вне процесса авторизации и аутентификации иSpring Security
Опыт наступания на яму в фильтрующей части. Добро пожаловать в эту серию статей.
1. Предыдущий обзор
Во-первых, давайте, как обычно, повторим предыдущую статью. во-первыхПроектирование и реализация аутентификации, аутентификации и управления разрешениями API в микросервисной архитектуре (1)Представлены предыстория проекта, техническое исследование и окончательный выбор. Статья 2Проектирование и реализация аутентификации, аутентификации и управления разрешениями API в микросервисной архитектуре (2)Рисует краткую блок-схему входа и проверки и фокусируется на конкретной реализации проверки подлинности пользователя и выдачи токена. Статья 3Разработка и реализация аутентификации, аутентификации и управления разрешениями API в микросервисной архитектуре (3)Сначала в нем представлена конфигурация сервера ресурсов и задействованные классы конфигурации, а затем основное внимание уделяется аутентификации на уровне токенов и API.
В этой статье будут рассмотрены оставшиеся две встроенные конечные точки: выход из системы и токен обновления. Обработка конечной точки токена выхода такая же, какSpring Security
Некоторые из «/logout», предоставленные по умолчанию, несколько отличаются, не только очищая информацию в SpringSecurityContextHolder, но также добавляя очистку токена хранилища. Другая конечная точка токена обновления на самом деле является тем же API, что и предыдущая авторизация запроса, но Grant_type в параметре отличается.
В дополнение к вышеупомянутым двум встроенным конечным точкам, следующее будет сосредоточено на следующихSpring Security
фильтр. Первоначально предполагалось, что проверка разрешений на операции на уровне API будет осуществляться черезSpring Security
Реализация фильтра , специально здесь изучалась и наступила на яму.
Наконец, это резюме этой серии, в котором обсуждаются существующие недостатки и последующие работы.
2. Другие конечные точки
2.1 конечная точка выхода из системы
Конечная точка выхода из системы, встроенная в систему аутентификации, упоминалась в первой статье./logout
, если вспомнить конфигурацию третьего сервера ресурсов, то про/logout
Конфигурация должна быть знакома.
//...
.and().logout()
.logoutUrl("/logout")
.clearAuthentication(true)
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
.addLogoutHandler(customLogoutHandler());
Основные функции вышеуказанной конфигурации:
- Установите URL-адрес выхода
- Очистить информацию для аутентификации
- Установите метод обработки для успешного выхода
- Установить пользовательскую обработку выхода из системы
Конечно вLogoutConfigurer
В , здесь автор перечисляет необходимые для проекта элементы конфигурации. Эти элементы конфигурации вращаются вокругLogoutFilter
фильтр. КстатиSpring Security
фильтр. он использовалspringSecurityFillterChian
В качестве входа для фильтрации безопасности различные фильтры расположены в следующем порядке:
- SecurityContextPersistenceFilter: относится к информации контекста безопасности SecurityContext.
- HeaderWriterFilter: добавьте заголовок в ответ http
- CsrfFilter: предотвращение атак csrf, включено по умолчанию.
- LogoutFilter: фильтр, обрабатывающий выход из системы.
- UsernamePasswordAuthenticationFilter: Фильтр проверки подлинности формы
- RequestCacheAwareFilter: кэшировать запросы запросов
- SecurityContextHolderAwareRequestFilter: этот фильтр оборачивает ServletRequest один раз, делая запрос более богатым API.
- AnonymousAuthenticationFilter: фильтр анонимной идентификации.
- SessionManagementFilter: фильтр, связанный с сеансом, обычно используемый для предотвращения атак защиты с фиксацией сеанса и для ограничения количества нескольких сеансов, открытых одним и тем же пользователем.
- ExceptionTranslationFilter: фильтр обработки исключений
- FilterSecurityInterceptor: ключевой фильтр для безопасности веб-приложений.
Различные фильтры просто отмечены их функциями, и некоторые из них выделены в следующем разделе. Фильтр выхода находится вверху списка, давайте посмотримLogoutFilter
Диаграмма классов UML.
Диаграмма классов такая же, как и в нашей предыдущей конфигурации.HttpSecurity
созданныйLogoutConfigurer
, мы настроили здесьLogoutConfigurer
некоторые свойства. в то же времяLogoutConfigurer
созданный из этих свойствLogoutFilter
.
LogoutConfigurer
Конфигурация, первый и второй пункты не нуждаются в подробном объяснении, один — установить конечную точку, а другой — очистить информацию аутентификации.
В-третьих, настройте обработку успешного выхода из системы. Поскольку интерфейс и серверная часть проекта разделены, клиенту нужно знать статус интерфейса API только в случае успешного выполнения, и ему не нужно возвращать определенную страницу или продолжать передавать запросы. Поэтому конфигурация по умолчанию здесьHttpStatusReturningLogoutSuccessHandler
, в случае успеха возвращается код состояния 200.
Для конфигурации четвертой точки настройте метод обработки выхода из системы. нужна помощь здесьTokenStore
, работать с токеном.TokenStore
Как упоминалось в конфигурации предыдущей статьи, используется JdbcTokenStore. Сначала проверьте правильность запроса, если он действителен, выполните его и последовательно удалите.refreshToken
иexistingAccessToken
.
public class CustomLogoutHandler implements LogoutHandler {
//...
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
//确定注入了tokenStore
Assert.notNull(tokenStore, "tokenStore must be set");
//获取头部的认证信息
String token = request.getHeader("Authorization");
Assert.hasText(token, "token must be set");
//校验token是否符合JwtBearer格式
if (isJwtBearerToken(token)) {
token = token.substring(6);
OAuth2AccessToken existingAccessToken = tokenStore.readAccessToken(token);
OAuth2RefreshToken refreshToken;
if (existingAccessToken != null) {
if (existingAccessToken.getRefreshToken() != null) {
LOGGER.info("remove refreshToken!", existingAccessToken.getRefreshToken());
refreshToken = existingAccessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
}
LOGGER.info("remove existingAccessToken!", existingAccessToken);
tokenStore.removeAccessToken(existingAccessToken);
}
return;
} else {
throw new BadClientCredentialsException();
}
}
//...
}
Выполните следующий запрос:
method: get
url: http://localhost:9000/logout
header:
{
Authorization: Basic ZnJvbnRlbmQ6ZnJvbnRlbmQ=
}
Если выход выполнен успешно, будет возвращено значение 200, а токен и SecurityContextHolder будут очищены.
2.2 Обновить конечную точку
Как упоминалось в первой статье, поскольку срок действия токена, как правило, не очень велик, и обновлениеЦикл токена, как правило, будет очень долгим. Чтобы не влиять на работу пользователя, вы можете использовать обновлениеtoken для динамического обновления токена. Обновление токена в основном связано сRefreshTokenGranter
Связанный,CompositeTokenGranter
Управляйте списком, каждый тип гранта соответствует конкретному реальному авторизатору, а грантодателю, соответствующему refresh_token, являетсяRefreshTokenGranter
, а внутри лица, предоставляющего, GrantType используется для определения того, является ли это соответствующим типом предоставления. Выполните следующий запрос:
method: post
url: http://localhost:12000/oauth/token?grant_type=refresh_token&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJYLUtFRVRTLVVzZXJJZCI6ImQ2NDQ4YzI0LTNjNGMtNGI4MC04MzcyLWMyZDYxODY4ZjhjNiIsInVzZXJfbmFtZSI6ImtlZXRzIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImJhZDcyYjE5LWQ5ZjMtNDkwMi1hZmZhLTA0MzBlN2RiNzllZCIsImV4cCI6MTUxMDk5NjU1NiwianRpIjoiYWE0MWY1MjctODE3YS00N2UyLWFhOTgtZjNlMDZmNmY0NTZlIiwiY2xpZW50X2lkIjoiZnJvbnRlbmQifQ.mICT1-lxOAqOU9M-Ud7wZBb4tTux6OQWouQJ2nn1DeE
header:
{
Authorization: Basic ZnJvbnRlbmQ6ZnJvbnRlbmQ=
}
Когда refresh_token правильный, ответ, который он возвращает, такой же, как и обычный ответ от /oauth/token. Конкретный код может относиться к пояснению второй части.
3. Spring Security
фильтр
В предыдущем разделе мы представили детали реализации двух встроенных конечных точек, а также упомянулиHttpSecurity
Фильтр, потому что реализация конечной точки выхода осуществляется через роль фильтра. Основные фильтры:
- FilterSecurityInterceptor
- UsernamePasswordAuthenticationFilter
- SecurityContextPersistenceFilter
- ExceptionTranslationFilter
В этом разделе речь пойдет оUsernamePasswordAuthenticationFilter
иFilterSecurityInterceptor
.
3.1 UsernamePasswordAuthenticationFilter
Автор только начал читать статью про фильтры, ибоUsernamePasswordAuthenticationFilter
Есть много статей об этом. Если вы просто введете Spring-Security, это неизбежно будет таким же, как/login
Конечные точки знакомы. SpringSecurity требует, чтобы наша страница входа в форму отправляла запрос на URL-адрес /login в режиме POST, а имена параметров, для которых требуется имя пользователя и пароль, должны быть именем пользователя и паролем. В противном случае он не будет работать должным образом. Причина в том, что когда мы вызываем метод formLogin объекта HttpSecurity, он в итоге регистрирует для нас фильтрUsernamePasswordAuthenticationFilter
. Взгляните на исходный код этого фильтра.
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
//用户名、密码
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
//post请求/login
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
//实现抽象类AbstractAuthenticationProcessingFilter的抽象方法,尝试验证
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
//···
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
//···
return this.getAuthenticationManager().authenticate(authRequest);
}
}
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
//...
//调用requiresAuthentication,判断请求是否需要authentication,如果需要则调用attemptAuthentication
//有三种结果可能返回:
//1.Authentication对象
//2. AuthenticationException
//3. Authentication对象为空
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//不需要校验,继续传递
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
Authentication authResult;
try {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
//...
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
//实际执行的authentication,继承类必须实现该抽象方法
public abstract Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException,
ServletException;
//成功authentication的默认行为
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
//...
}
//失败authentication的默认行为
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
//...
}
...
//设置AuthenticationManager
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
...
}
UsernamePasswordAuthenticationFilter
из-за наследстваAbstractAuthenticationProcessingFilter
Имеет только функцию фильтра.AbstractAuthenticationProcessingFilter
Требуется установить authenticationManager, класс реализации authenticationManager фактически будет обрабатывать аутентификацию запроса.AbstractAuthenticationProcessingFilter
Будет перехватывать запросы, соответствующие правилам фильтрации, и пытаться выполнить аутентификацию. Подклассы должны реализовать метод tryAuthentication, который выполняет определенную аутентификацию.
Обработка после аутентификации аналогична предыдущей процедуре выхода из системы. Если аутентификация прошла успешно, возвращенный объект Authentication будет сохранен в SecurityContext, и будет вызван SuccessHandler.Вы также можете установить указанный URL-адрес и указать пользовательский SuccessHandler. В случае сбоя аутентификации клиенту по умолчанию будет возвращен код 401. Вы также можете задать URL-адрес и указать собственный обработчик FailureHandler.
на основеUsernamePasswordAuthenticationFilter
индивидуальныеAuthenticationFilte
Есть еще много случаев, вот рекомендуемая запись в блогеSpring Security (5) — практическая реализация IP_Login, написано более подробно.
3.2 FilterSecurityInterceptor
FilterSecurityInterceptor
Это более сложный и основной фильтр в цепочке фильтров, который в основном отвечает за авторизацию безопасности веб-приложений. Первый взгляд на обычайFilterSecurityInterceptor
конфигурация.
@Override
public void configure(HttpSecurity http) throws Exception {
...
//添加CustomSecurityFilter,过滤器的顺序放在FilterSecurityInterceptor
http.antMatcher("/oauth/check_token").addFilterAt(customSecurityFilter(), FilterSecurityInterceptor.class);
}
//提供实例化的自定义过滤器
@Bean
public CustomSecurityFilter customSecurityFilter() {
return new CustomSecurityFilter();
}
Как видно из приведенной выше конфигурации, вFilterSecurityInterceptor
Местоположение зарегистрированоCustomSecurityFilter
, для соответствия/oauth/check_token
, называется вход в фильтр. На картинке нижеFilterSecurityInterceptor
Диаграмма классов , которая также добавляетCustomSecurityFilter
Читателям удобно сравнивать с классом реализуемого им интерфейса.
CustomSecurityFilter
это имитацияFilterSecurityInterceptor
осуществлять, наследоватьAbstractSecurityInterceptor
и реализоватьFilter
интерфейс. Весь процесс требует зависимостейAuthenticationManager
,AccessDecisionManager
иFilterInvocationSecurityMetadataSource
.AuthenticationManager
Это менеджер аутентификации, который реализует вход аутентификации пользователя;AccessDecisionManager
Это решающий фактор доступа, который определяет, достаточно ли у пользователя разрешений для доступа к определенному ресурсу;FilterInvocationSecurityMetadataSource
Это определение исходных данных ресурса, то есть оно определяет, с помощью каких ролей можно получить доступ к ресурсу.
Как вы можете видеть на диаграмме классов над пользовательскимCustomSecurityFilter
также понялAccessDecisionManager
иFilterInvocationSecurityMetadataSource
. соответственноSecureResourceFilterInvocationDefinitionSource
иSecurityAccessDecisionManager
. Основная конфигурация анализируется ниже.
//通过一个实现的filter,对HTTP资源进行安全处理
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
//被filter chain真实调用的方法,通过invoke代理
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
//代理的方法
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//...省略
}
}
Приведенный выше кодFilterSecurityInterceptor
В реализации конкретные детали реализации не указаны. Здесь мы сосредоточимся на объяснении пользовательской реализации.
public class CustomSecurityFilter extends AbstractSecurityInterceptor implements Filter {
@Autowired
SecureResourceFilterInvocationDefinitionSource invocationSource;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private SecurityAccessDecisionManager decisionManager;
//设置父类中的属性
@PostConstruct
public void init() {
super.setAccessDecisionManager(decisionManager);
super.setAuthenticationManager(authenticationManager);
}
//主要的过滤方法,与原来的一致
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//logger.info("doFilter in Security ");
//构造一个FilterInvocation,封装request, response, chain
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
//beforeInvocation会调用SecureResourceDataSource中的逻辑,类似于aop中的before
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
//完成后续工作,类似于aop中的after
super.afterInvocation(token, null);
}
}
//...
//资源源数据定义,设置为自定义的SecureResourceFilterInvocationDefinitionSource
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return invocationSource;
}
}
настроенный вышеCustomSecurityFilter
, что является тем же процессом, что и наше предыдущее объяснение. Три интерфейса, от которых в основном зависели, реализовали внедрение в реализации. Взгляните на метод beforeInvocation родительского класса, в котором отсутствуют некоторые несущественные фрагменты кода.
protected InterceptorStatusToken beforeInvocation(Object object) {
//根据SecurityMetadataSource获取配置的权限属性
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
//...
//判断是否需要对认证实体重新认证,默认为否
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
try {
//决策管理器开始决定是否授权,如果授权失败,直接抛出AccessDeniedException
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
throw accessDeniedException;
}
}
Как видно из приведенного выше кода, первым шагом является получение настроенного атрибута разрешения в соответствии с SecurityMetadataSource, а accessDecisionManager будет использовать информацию списка разрешений. Затем решите, нужно ли повторно аутентифицировать объект аутентификации, по умолчанию нет. Второй шаг заключается в том, что менеджер принятия решений начинает решать, авторизоваться или нет, и если авторизация не удалась, сразу создается исключение AccessDeniedException.
(1) Получите настроенный атрибут разрешения
public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
private PathMatcher matcher;
//map保存配置的URL对应的权限集
private static Map<String, Collection<ConfigAttribute>> map = new HashMap<>();
//根据传入的对象URL进行循环
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
logger.info("getAttributes");
//应该做instanceof
FilterInvocation filterInvocation = (FilterInvocation) o;
//String method = filterInvocation.getHttpRequest().getMethod();
String requestURI = filterInvocation.getRequestUrl();
//循环资源路径,当访问的Url和资源路径url匹配时,返回该Url所需要的权限
for (Iterator<Map.Entry<String, Collection<ConfigAttribute>>> iterator = map.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<String, Collection<ConfigAttribute>> entry = iterator.next();
String url = entry.getKey();
if (matcher.match(url, requestURI)) {
return map.get(requestURI);
}
}
return null;
}
//...
//设置权限集,即上述的map
@Override
public void afterPropertiesSet() throws Exception {
logger.info("afterPropertiesSet");
//用来匹配访问资源路径
this.matcher = new AntPathMatcher();
//可以有多个权限
Collection<ConfigAttribute> atts = new ArrayList<>();
ConfigAttribute c1 = new SecurityConfig("ROLE_ADMIN");
atts.add(c1);
map.put("/oauth/check_token", atts);
}
}
Выше приведены конкретные детали реализации getAttributes(), которая извлекает запрошенный URL-адрес для соответствия заранее установленным ограниченным ресурсам и, наконец, возвращает необходимые разрешения и роли. Когда система запустится, она прочитает настроенный набор карт и сопоставит перехваченные запросы. Комментарии в коде более подробные, поэтому я не буду здесь много говорить.
(2) Менеджер по принятию решений
public class SecurityAccessDecisionManager implements AccessDecisionManager {
//...
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
logger.info("decide url and permission");
//集合为空
if (collection == null) {
return;
}
Iterator<ConfigAttribute> ite = collection.iterator();
//判断用户所拥有的权限,是否符合对应的Url权限,如果实现了UserDetailsService,则用户权限是loadUserByUsername返回用户所对应的权限
while (ite.hasNext()) {
ConfigAttribute ca = ite.next();
String needRole = ca.getAttribute();
for (GrantedAuthority ga : authentication.getAuthorities()) {
logger.info("GrantedAuthority: {}", ga);
if (needRole.equals(ga.getAuthority())) {
return;
}
}
}
logger.error("AccessDecisionManager: no right!");
throw new AccessDeniedException("no right!");
}
//...
}
Вышеприведенный код является реализацией диспетчера решений, и его логика относительно проста: он сопоставляет разрешения запроса с разрешениями, требуемыми набором ограниченных ресурсов, и возвращает, если они есть, в противном случае выдает исключение без правильных разрешений. По умолчанию предусмотрено три типа менеджеров решений, а именно на основе утверждения, на основе консенсуса и на основе единогласия.Пространство ограничено, и мы не будем его здесь расширять.
Кроме того, он имеет полномочия через ранее настроенные методы аутентификации, включая аутентификацию по паролю и аутентификацию клиента. Мы предварительно настроили в сервере авторизацииwithClientDetails
, поэтому разрешения, полученные с помощью внешней аутентификации, являются полномочиями, которые мы предварительно настроили в базе данных.
4. Резюме
Основная функция системы Auth — авторизационная аутентификация и аутентификация. После того, как проект будет микросервисным, исходное отдельное приложение, основанное на аутентификации и аутентификации HttpSession, не сможет удовлетворить потребности микросервисной архитектуры. Каждому микросервису необходимо аутентифицировать доступ, а каждому микроприложению необходимо идентифицировать текущего пользователя доступа и его разрешения, особенно при наличии нескольких клиентов, в том числе веб-, мобильных и т. д., аутентификация в рамках монолитной архитектуры приложения Правильный путь не особо подходит. Как базовый общедоступный сервис, сервис разрешений также должен быть микросервисным.
В дизайне автора служба Auth выполняет авторизацию и аутентификацию, с одной стороны, и выполняет проверку легитимности личности и проверки разрешений на уровне API на основе токенов, с другой стороны. Для запроса службы шлюз вызовет службу аутентификации, чтобы проверить действительность токена. В то же время, судя по общей ситуации текущего проекта, есть некоторые унаследованные сервисы, которым не хватает времени и сил для немедленной трансформации микросервисов, и их нужно продолжать запускать. Чтобы адаптироваться к текущей новой архитектуре, принятое решение состоит в том, чтобы выполнить аутентификацию авторизации операции на уровне API в рабочем API этих устаревших служб в службе аутентификации. Контекстная информация, необходимая для проверки разрешений на операции на уровне API, должна быть объединена с бизнесом и согласована с клиентом.Соответствующая информация должна быть получена в токене и передана службе аутентификации, но информация для проверки контекста в заголовке должна быть сведена к минимуму.
На этот раз автор анализирует большую часть кода и исходного кода, задействованного в разработке системы аутентификации, а что касается не упомянутого содержания и деталей, то читатели могут расширить их самостоятельно.
5. Недостатки и доработка
5.1 Слабые стороны
-
Унифицированность проверки разрешений на операции на уровне API
(1) Для проверки разрешения на работу на уровне API необходимо построить соответствующую контекстную информацию при вызове на шлюзе. Контекстная информация в основном зависит от полезной нагрузки в токене.Если слишком много информации приводит к тому, что токен становится слишком длинным, длина заголовка запроса каждого клиента становится больше.
(2). Не все операционные интерфейсы могут быть охвачены. Эта проблема более серьезная. В соответствии с набором контекста очень вероятно, что разрешения многих интерфейсов не могут быть идентифицированы. Конечным результатом является сбой проверки разрешений на операции на уровне API. У вас нет разрешения на доступ к интерфейсу, но вы не сможете получить доступ через него, потому что контекст, задействованный в интерфейсе, вообще не может быть полностью получен. На текущем этапе нашего проекта определенный минимальный набор контекстов едва ли можно охватить, но это действительно не оптимистично в отношении интерфейса службы, который будет расширен позже.
(3) Каждый интерфейс каждого сервиса регистрирует необходимые разрешения в сервисе Auth, что слишком хлопотно, а сервису Auth необходимо поддерживать такую информацию дополнительно.
-
Узкое место в пропускной способности системы, вызванное вызовом службы аутентификации на шлюзе
(1). На самом деле это очень легко понять. Будучи общедоступной базовой службой, служба аутентификации требует аутентификации для большинства сервисных интерфейсов, а служба аутентификации должна быть сложной.
(2) Шлюз вызывает службу аутентификации, блокирует вызов и выполняет дальнейшую обработку только тогда, когда служба аутентификации возвращает результат проверки. Несмотря на то, что служба аутентификации может быть развернута в нескольких экземплярах, после большого параллелизма становится очевидным узкое место, которое может привести к недоступности всей системы.
5.2 Последующая работа
- С точки зрения дизайна всей системы разрешения на операции на уровне API будут разбросаны по интерфейсам каждой службы на более позднем этапе, и каждый интерфейс отвечает за необходимые ему разрешения и удостоверения. Spring Security также поддерживает проверку разрешений на уровне интерфейса.Причина принятия этого подхода заключается в совместимости с новыми службами и устаревшими службами, в основном для устаревших служб.Новые службы распределяются по каждому интерфейсу.
- После того, как разрешения на операции на уровне API будут распределены по каждому интерфейсу службы, ответ службы проверки подлинности может быть соответствующим образом улучшен. Шлюз может вовремя переслать или отклонить запрос.
- Контекстная информация, необходимая для разрешений на работу на уровне API, действительно сложна для каждого интерфейса, мы потратили время на управление разрешениями, соответствующими сотням операционных интерфейсов мобильных сервисов, что очень раздражает. !
Адрес источника этой статьи:
Гитхаб:GitHub.com/Доступный ETS2012/A…
Облако кода:git ee.com/can-ets/au-th-…
Подписывайтесь на свежие статьи, приглашаю обратить внимание на мой публичный номер
Ссылаться на
- Настроить вход через форму
- Анализ исходного кода Spring Security3 — анализ FilterSecurityInterceptor
- Core Security Filters
- Spring Security (4) — анализ исходного кода основного фильтра
Связанное Чтение
Проектирование и реализация аутентификации, аутентификации и управления разрешениями API в микросервисной архитектуре (1)
Проектирование и реализация аутентификации, аутентификации и управления разрешениями API в микросервисной архитектуре (2)
Разработка и реализация аутентификации, аутентификации и управления разрешениями API в микросервисной архитектуре (3)