经过大量试验和错误以及 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>
重要提示:
处理后端通常派生自 OAuth2 访问令牌(在正常的 OAuth2 请求中)。由于我们在这里替换了它,WSO2 无法确定调用哪个环境,因此它将默认调用 PRODUCTION。要解决此问题,请在您的 JWT 中插入一些额外的字段,在我的案例环境中,这有助于您做出决定。然后,使用如图所示的适当环境创建一个AuthenticationContext。就是这样!
如果您直接编辑您的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>