Как получить области из токена OAuth в Spring boot SSO + zuul

Я пытаюсь создать простой шлюз API, используя Spring boot SSO + Zuul. Мне нужно преобразовать области OAuth в заголовки, которые в дальнейшем будут использоваться какой-либо другой серверной службой для выполнения RBAC на основе заголовков.

Я использую этот CustomOAuth2TokenRelayFilter, который в основном устанавливает заголовки перед отправкой на серверную часть. Моя проблема заключается в том, как получить области действия из текущего токена. Класс OAuth2AuthenticationDetails предоставляет значение маркера, но не предоставляет области.

Я не уверен в том, как получить прицелы там.

Ниже приведен пользовательский фильтр Zuul, который в основном взят из https://github.com/spring-cloud/spring-cloud-security/blob/master/spring-cloud-security/src/main/java/org/springframework/cloud/security/oauth2/proxy/OAuth2TokenRelayFilter.java

    import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Component;

@Component
    public class CustomOAuth2TokenRelayFilter extends ZuulFilter {

        private static Logger LOGGER = LoggerFactory.getLogger(CustomOAuth2TokenRelayFilter.class);

        private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
        private static final String TOKEN_TYPE = "TOKEN_TYPE";

        private OAuth2RestOperations restTemplate;


        public void setRestTemplate(OAuth2RestOperations restTemplate) {
            this.restTemplate = restTemplate;
        }


        @Override
        public int filterOrder() {
            return 1;
        }

        @Override
        public String filterType() {
            return "pre";
        }

        @Override
        public boolean shouldFilter() {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();

            if (auth instanceof OAuth2Authentication) {
                Object details = auth.getDetails();
                if (details instanceof OAuth2AuthenticationDetails) {
                    OAuth2AuthenticationDetails oauth = (OAuth2AuthenticationDetails) details;
                    RequestContext ctx = RequestContext.getCurrentContext();

                    LOGGER.debug ("role " + auth.getAuthorities());

                    LOGGER.debug("scope", ctx.get("scope")); // How do I obtain the scope ??


                    ctx.set(ACCESS_TOKEN, oauth.getTokenValue());
                    ctx.set(TOKEN_TYPE, oauth.getTokenType()==null ? "Bearer" : oauth.getTokenType());
                    return true;
                }
            }
            return false;
        }

        @Override
        public Object run() {
            RequestContext ctx = RequestContext.getCurrentContext();
            ctx.addZuulRequestHeader("x-pp-user", ctx.get(TOKEN_TYPE) + " " + getAccessToken(ctx));
            return null;
        }

        private String getAccessToken(RequestContext ctx) {
            String value = (String) ctx.get(ACCESS_TOKEN);
            if (restTemplate != null) {
                // In case it needs to be refreshed
                OAuth2Authentication auth = (OAuth2Authentication) SecurityContextHolder
                        .getContext().getAuthentication();
                if (restTemplate.getResource().getClientId()
                        .equals(auth.getOAuth2Request().getClientId())) {
                    try {
                        value = restTemplate.getAccessToken().getValue();
                    }
                    catch (Exception e) {
                        // Quite possibly a UserRedirectRequiredException, but the caller
                        // probably doesn't know how to handle it, otherwise they wouldn't be
                        // using this filter, so we rethrow as an authentication exception
                        throw new BadCredentialsException("Cannot obtain valid access token");
                    }
                }
            }
            return value;
        }

    }

person Maaz Khan    schedule 24.05.2016    source источник


Ответы (2)


Вы можете внедрить OAuth2ClientContext в свой фильтр и используйте oAuth2ClientContext.getAccessToken().getScope() для получения областей.

OAuth2ClientContext — это bean-компонент с областью действия сеанса, содержащий текущий токен доступа и сохраненное состояние.

Итак, если мы применим это к вашему примеру, это будет выглядеть так:

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Component;

@Component
public class CustomOAuth2TokenRelayFilter extends ZuulFilter {

    private static Logger LOGGER = LoggerFactory.getLogger(CustomOAuth2TokenRelayFilter.class);

    private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
    private static final String TOKEN_TYPE = "TOKEN_TYPE";

    private OAuth2RestOperations restTemplate;

    @Autowired
    private OAuth2ClientContext oAuth2ClientContext;

    public void setRestTemplate(OAuth2RestOperations restTemplate) {
        this.restTemplate = restTemplate;
    }


    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public boolean shouldFilter() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (auth instanceof OAuth2Authentication) {
            Object details = auth.getDetails();
            if (details instanceof OAuth2AuthenticationDetails) {
                OAuth2AuthenticationDetails oauth = (OAuth2AuthenticationDetails) details;
                RequestContext ctx = RequestContext.getCurrentContext();

                LOGGER.debug ("role " + auth.getAuthorities());

                LOGGER.debug("scope" + oAuth2ClientContext.getAccessToken().getScope());

                ctx.set(ACCESS_TOKEN, oauth.getTokenValue());
                ctx.set(TOKEN_TYPE, oauth.getTokenType()==null ? "Bearer" : oauth.getTokenType());
                return true;
            }
        }
        return false;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader("x-pp-user", ctx.get(TOKEN_TYPE) + " " + getAccessToken(ctx));
        return null;
    }

    private String getAccessToken(RequestContext ctx) {
        String value = (String) ctx.get(ACCESS_TOKEN);
        if (restTemplate != null) {
            // In case it needs to be refreshed
            OAuth2Authentication auth = (OAuth2Authentication) SecurityContextHolder
                    .getContext().getAuthentication();
            if (restTemplate.getResource().getClientId()
                    .equals(auth.getOAuth2Request().getClientId())) {
                try {
                    value = restTemplate.getAccessToken().getValue();
                }
                catch (Exception e) {
                    // Quite possibly a UserRedirectRequiredException, but the caller
                    // probably doesn't know how to handle it, otherwise they wouldn't be
                    // using this filter, so we rethrow as an authentication exception
                    throw new BadCredentialsException("Cannot obtain valid access token");
                }
            }
        }
        return value;
    }

}
person Riccardo Lippolis    schedule 24.05.2016
comment
К сожалению, возвращает ноль для меня. Пробовал автопроводку в контроллере. Использование предоставления учетных данных клиента в моем приложении - person sandkeks; 16.11.2016
comment
Кажется, работает как шарм. Однако есть 3 вопроса: в приведенном выше примере у вас есть экземпляр oAuth2ClientContext, почему все еще возникают проблемы с доступом к экземпляру Authentication? Возможно, у вас есть какие-либо документы о характере OAuth2ClientContext в рамках сеанса? Область запроса, вероятно, будет безопаснее с чем-то вроде прокси-сервера Zuul? - person demaniak; 23.03.2017
comment
@sandkeks Как вы пришли к этой проблеме? - person Dante May Code; 25.05.2017

Вы можете получить области из токена OAuth2 с помощью SecurityContextHolder и Аутентификация OAuth2

private static Set<String> getOAuthTokenScopes() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    OAuth2Authentication oAuth2Authentication;

    if (authentication instanceof OAuth2Authentication) {
        oAuth2Authentication = (OAuth2Authentication) authentication;
    } else {
        throw new IllegalStateException("Authentication not supported!");
    }

    return oAuth2Authentication.getOAuth2Request().getScope();
}
person sandkeks    schedule 16.11.2016
comment
Это сработало для меня, в отличие от oAuth2ClientContext , который возвращал null из метода getScopes(). Я смог просто добавить OAuth2Authentication authentication к моей сигнатуре метода @Controller, что сделало его намного проще. - person jax; 24.10.2017