【问题标题】:Is it possible to validate a JWT token created from an external OAuth2 Server in WSO2?是否可以验证从 WSO2 中的外部 OAuth2 服务器创建的 JWT 令牌?
【发布时间】:2018-12-11 01:19:10
【问题描述】:

我有一个为前端应用程序生成 JWT 令牌的 Spring Security OAuth2 服务器。

此令牌将通过 API 网关(WSO2 API 管理器)在对后端的调用中发送。

我希望在 WSO2 中注册后端 API,并能够验证外部生成的 JWT 令牌。

这可能吗?您能否提供一个 WSO2 APIM 不同位置的示例,这些位置需要配置为包含此逻辑?

注意:WSO2 永远不需要创建令牌,它始终是以前创建的,只需要验证它。

【问题讨论】:

  • 您想同时保留默认的 OAuth2 安全性和这个吗?或者只是用这个替换 OAuth 之一?
  • 用 JWT 验证替换 OAuth。

标签: oauth-2.0 wso2 jwt wso2-am


【解决方案1】:

经过大量试验和错误以及 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(由于非 oauth2 认证的 api 的一个众所周知的错误,后者需要删除):

应该是这样的:

<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. 如果您直接编辑您的api.xml 描述符,它将在您下次发布时被替换。要自动生成它,请编辑速度模板 (/repository/resources/api_templates/velocity_template.xml)。就我而言,我只希望它适用于某些应用程序,因此我使用标签 (jwt-auth) 来选择它们。

velocity_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>

【讨论】:

    【解决方案2】:

    确实,您可以编写一个自定义处理程序来自行进行身份验证(基本身份验证,jwt 承载)。很快就找到了。也许作为一种改进,您可以缓存经过验证的 jwt 令牌(或 jwt 哈希),因为验证可能需要一些时间和性能。

    作为默认解决方案(无需任何自定义),您可以使用JWT grant 将来自受信任 IdP 的令牌交换为内部 APIM 令牌。

    【讨论】:

    • 感谢您的建议,我去看看 JWT 的授权。
    • @codependent:请注意我没试过那个。我之前使用过 SAML 授权,jwt 应该可以使用当前的 apim 版本(2.6.0)
    猜你喜欢
    • 2016-08-04
    • 1970-01-01
    • 2022-06-18
    • 2019-10-08
    • 2016-02-03
    • 1970-01-01
    • 2020-06-30
    • 2016-12-06
    • 2020-05-25
    相关资源
    最近更新 更多