Можно ли проверить токен JWT, созданный на внешнем сервере OAuth2 в WSO2? - PullRequest
0 голосов
/ 08 декабря 2018

У меня есть сервер Spring Security OAuth2, который генерирует токены JWT для приложений веб-интерфейса.

Эти токены будут отправляться в вызовах к бэкэндам, проходящим через шлюз API (WSO2 API Manager).

Мне бы хотелось, чтобы бэкэнд-API были зарегистрированы в WSO2 и былиспособен проверить этот сгенерированный извне токен JWT.

Возможно ли это?Можете ли вы предоставить пример различных мест APIM WSO2, которые необходимо настроить для включения этой логики?

ВНИМАНИЕ: WSO2 никогда не потребуется создавать токен, он всегда был создан ранее, это нужно только для проверки.

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Действительно, вы можете написать собственный обработчик для аутентификации самостоятельно (обычная аутентификация, jwt bearer).Хорошая работа, найти это быстро.Возможно, в качестве улучшения вы можете кэшировать проверенный токен jwt (или хеш jwt), поскольку проверка может занять некоторое время и производительность.

В качестве решения по умолчанию (без какой-либо настройки) вы можете использовать JWT grant для обмена токена из доверенного IdP на внутренний токен APIM.

0 голосов
/ 10 декабря 2018

После долгих проб и ошибок и некоторой помощи от Stackoverflow (спасибо Bee) это мое рабочее решение.Я надеюсь, что это поможет другим, поскольку было действительно сложно заставить это работать:

1.Реализуйте JWTAuthHandler для проверки токенов JWT:

public class JwtAuthHandler extends AbstractHandler {

    private final PublicKeyFactory pkf = new PublicKeyFactory();
    private final JwtVerifier jwtVerifier = new JwtVerifier();

    @Override
    public boolean handleRequest(MessageContext messageContext) {

        try {
            final String jwtToken = getJwtTokenFromHeaders(messageContext).replace("Bearer ", "");

            SignedJWT signedJwt = SignedJWT.parse(jwtToken);
            final JSONObject payload = signedJwt.getPayload().toJSONObject();
            final JSONObject environment = (JSONObject)payload.get("environment");

            PublicKey publicKey = readPublicKey();

            JWSVerifier verifier = new RSASSAVerifier(((RSAPublicKey) publicKey));
            final boolean signatureVerification = signedJwt.verify(verifier)

            if (signatureVerification) {
                AuthenticationContext authContext = new AuthenticationContext();
                authContext.setAuthenticated(true);
                if (isProductionRequest(environment)) {
                    authContext.setKeyType(APIConstants.API_KEY_TYPE_PRODUCTION);
                } else {
                    authContext.setKeyType(APIConstants.API_KEY_TYPE_SANDBOX);
                }
                APISecurityUtils.setAuthenticationContext(messageContext, authContext, "Authorization");
            } else {
                LOG.debug("handleRequest() - Sending 401 Unauthorized");
                Utils.sendFault(messageContext, 401);
            }
            return signatureVerification;
        } catch (Exception e) {
            e.printStackTrace();
            Utils.sendFault(messageContext, 500);
            return false;
        }
    }

    @Override
    public boolean handleResponse(MessageContext messageContext) {
        return true;
    }

    private String getJwtTokenFromHeaders(MessageContext messageContext) {
        Map headers = (Map) ((Axis2MessageContext) messageContext).getAxis2MessageContext().
                getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
        return (String) headers.get("Authorization");
    }

    private boolean isProductionRequest(JSONObject environment) {
        return environment != null && environment.equals("pro");
    }

}

2.Переопределите определение API (/repository/deployment/server/synapse-configs/default/api/yourapi.xml), чтобы использовать обработчик jwt и удалить APIAuthenticationHandler и ThrottleHandler (последний необходимо удалить из-за хорошо известной ошибки для apis без аутентификации oauth2):

Должно быть что-то вроде этого:

<handlers>
  <handler class="com.codependent.JwtAuthHandler"/>
  <handler class="org.wso2.carbon.apimgt.gateway.handlers.common.APIMgtLatencyStatsHandler"/>
  <handler class="org.wso2.carbon.apimgt.gateway.handlers.security.CORSRequestHandler">
     <property name="apiImplementationType" value="ENDPOINT"/>
  </handler>
  <handler class="org.wso2.carbon.apimgt.gateway.handlers.analytics.APIMgtUsageHandler"/>
  <handler class="org.wso2.carbon.apimgt.gateway.handlers.analytics.APIMgtGoogleAnalyticsTrackingHandler">
     <property name="configKey" value="gov:/apimgt/statistics/ga-config.xml"/>
  </handler>
  <handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>


</handlers>

ВАЖНО:

  1. Бэкэнд обработки обычно получается (в обычных запросах OAuth2)из токена доступа OAuth2.Так как здесь мы заменили его, WSO2 не может определить, какую среду вызывать, поэтому он будет вызывать PRODUCTION по умолчанию.Чтобы обойти это, вставьте в JWT дополнительное поле в моем случае, которое поможет вам принять решение.Затем создайте AuthenticationContext с подходящей средой, как показано на рисунке.Вот и все!

  2. Если вы непосредственно отредактируете дескриптор yourapi.xml, он будет заменен при следующей публикации.Для автоматизации его генерации отредактируйте шаблон скорости (/repository/resources/api_templates/velocity_template.xml).В моем случае я хочу, чтобы он применялся только к некоторым приложениям, поэтому я использую тег (jwt-auth) для их выбора.

speed_template.xml:

<handlers xmlns="http://ws.apache.org/ns/synapse">
#if($apiObj.tags.contains("jwt-auth"))
    <handler class="com.codependent.JwtAuthHandler"/>
#end
#foreach($handler in $handlers)
    #if((($handler.className != "org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler") && 
         ($handler.className != "org.wso2.carbon.apimgt.gateway.handlers.throttling.ThrottleHandler")) || 
            !($apiObj.tags.contains("jwt-auth")))
    <handler xmlns="http://ws.apache.org/ns/synapse" class="$handler.className">
        #if($handler.hasProperties())
            #set ($map = $handler.getProperties() )
            #foreach($property in $map.entrySet())
        <property name="$!property.key" value="$!property.value"/>
            #end
        #end
    </handler>
    #end
#end
</handlers>
...