如何编写Azure API管理策略以允许作用域或角色



我想为一组使用作用域的AD用户和一组使用应用程序角色的守护程序应用程序限制API。但是,只有在两个声明都存在的情况下,以下Azure APIM策略才会进行检查。如何重写以下策略以允许JWT令牌中存在作用域或应用程序角色:

<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
<required-claims>
<claim name="scp" match="any">
<value>API.Request.Get</value>
</claim>
<claim name="roles" match="any">
<value>API.Request.Get</value>                   
</claim>
</required-claims>
</validate-jwt>

我的想法是将choose whenjwt-validate结合起来,这是我的策略,它可以选择验证scp或角色,但我不知道为什么它不能正确验证值,我真的不是apim专家。

<inbound>
<base />
<set-variable name="isScp" value="@{
string isScp = "false";
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length > 0)
{
string[] authHeaderParts = authHeader.Split(' ');
if (authHeaderParts?.Length == 2 && authHeaderParts[0].Equals("Bearer", StringComparison.InvariantCultureIgnoreCase))
{
Jwt jwt;
if (authHeaderParts[1].TryParseJwt(out jwt))
{
if(jwt.Claims.GetValueOrDefault("scp", "null") != "null"){
isScp = "true";
}
}
}
}
return isScp;
}" />
<choose>
<when condition="@(context.Variables.GetValueOrDefault("isScp") == "false")">
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Server is unavailable for roles">
<required-claims>
<claim name="roles" match="any">
<value>User.ReadWrite.All</value>
</claim>
</required-claims>
</validate-jwt>
</when>
<otherwise>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Server is unavailable for scp">
<required-claims>
<claim name="scp" match="any" separator=" ">
<value>User.ReadWrite.All</value>
</claim>
</required-claims>
</validate-jwt>
</otherwise>
</choose>
</inbound>

顺便说一句,验证也可以写在代码中,而不使用validate-jwt,即:

<inbound>
<base />
<set-variable name="pass" value="@{
bool isContainScp = false;
bool isContainRoles = false;
string pass = "false";
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length > 0)
{
string[] authHeaderParts = authHeader.Split(' ');
if (authHeaderParts?.Length == 2 && authHeaderParts[0].Equals("Bearer", StringComparison.InvariantCultureIgnoreCase))
{
Jwt jwt;
if (authHeaderParts[1].TryParseJwt(out jwt))
{
string tempScp = jwt.Claims.GetValueOrDefault("scp", "null");
if(tempScp != "null"){
isContainScp = tempScp.Contains("User.ReadWrite.All");
}else{
//write logic here
isContainRoles = true;
}
}
}
}
if(isContainScp || isContainRoles){
pass = "true";
}
return pass;
}" />
<choose>
<when condition="@(context.Variables.GetValueOrDefault("pass") == "false")">
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized hhh" />
</return-response>
</when>
<otherwise />
</choose>
</inbound>

感谢Tiny Wang给我一个样品。authHeader已包含令牌。因此,没有必要进一步拆分它来获得令牌。这里是更新的一个:

<inbound>
<base />
<set-variable name="clientType" value="@{
bool isAuthorized = false;
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");

if (authHeader?.Length == 0) {
return "No Authorization Header";
}
Jwt jwt;
if (!authHeader.TryParseJwt(out jwt)) {
return "Parse JWT Token failed";
}
bool isPublicClient = jwt.Claims.GetValueOrDefault("azpacr", "null").Equals("0");
bool isClientSecret = jwt.Claims.GetValueOrDefault("azpacr", "null").Equals("1");
bool isClientCertificate = jwt.Claims.GetValueOrDefault("azpacr", "null").Equals("2");
if (isPublicClient) {
return "Public Client";
} else if (isClientSecret) {
return "Client Secret";
} else if (isClientCertificate) {
return "Client Certificate";
}
return "Unauthorized";
}" />
<choose>
<when condition="@(context.Variables.GetValueOrDefault("clientType") == "Public Client")">
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized by APIM policy." require-expiration-time="false">
<openid-config url="https://login.microsoftonline.com/YourTenantHere/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>Your audience here</audience>
</audiences>
<issuers>
<issuer>https://login.microsoftonline.com/YourTenantHere/v2.0</issuer> 
</issuers>
<required-claims>
<claim name="scp" match="any">
<value>API.Request.Get</value>
</claim>
</required-claims>
</validate-jwt>
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
</return-response>
</when>
<when condition="@(context.Variables.GetValueOrDefault("clientType") == "Client Secret")">
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized by APIM policy." require-expiration-time="false">
<openid-config url="https://login.microsoftonline.com/your tenant here/v2.0/.well-known/openid-configuration" /> 
<audiences>
<audience>Your Audience Here</audience> 
</audiences>
<issuers>
<issuer>https://login.microsoftonline.com/Your tenant here/v2.0</issuer> 
</issuers>
<required-claims>
<claim name="roles" match="any">
<value>App.Request.Get</value>
</claim>
</required-claims>
</validate-jwt>
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
</return-response>
</when>
<otherwise>
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized hhh" />
</return-response>
</otherwise>
</choose>
</inbound>

实现JWT角色声明的主要途径是在验证JWT策略中添加"输出令牌变量名称"prperty。这里有一个例子(这里需要的声明是"电子邮件",但想法相同(:

<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." require-scheme="Bearer" output-token-variable-name="jwt">
<openid-config url="https://csmsorg.b2clogin.com/csmsorg.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_susi_reset_v2" />
<audiences>
<audience>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx</audience>
</audiences>
<issuers>
<issuer>https://xxxxxx.b2clogin.com/e748014c-129c-4d13-accc-e5e1bb7c38eb/v2.0/</issuer>
</issuers>
<required-claims>
<claim name="tfp" match="any">
<value>B2C_1_susi_reset_v2</value>
<value>B2C_1__SUSI</value>
</claim>
</required-claims>
</validate-jwt>        
<choose>
<when condition="@(!((Jwt)context.Variables["jwt"]).Claims["emails"].Contains("xxxxxx@gmail.com"))">
<return-response>
<set-status code="403" reason="Forbidden" />
</return-response>
</when>
</choose>
</inbound>

最新更新