
# 企业API使用
#### 场景简介
OneAccess提供第三方API的授权管理功能，API提供者将API配置到OneAccess之后，API消费者在使用API之前需要先到OneAccess获取鉴权Token，在使用API时携带该鉴权Token，API提供者根据鉴权Token判断是否可以提供服务，用以实现API的授权管理功能。
图1场景简介图   
![](https://support.huaweicloud.com/bestpractice-oneaccess/zh-cn_image_0000001615333568.png)
#### 前提条件
请确保您已拥有OneAccess管理门户的访问权限。
 #### 在OneAccess中添加企业应用
在OneAccess管理门户中添加企业应用，提供给API使用者获取鉴权Token信息。
1. 登录OneAccess管理门户。
2. 在导航栏中，单击"资源 \> 应用"。
3. 在企业应用页面，单击自建应用下的"添加自建应用"，设置Logo和名称，单击"保存"。
4. 获取的ClientId和ClientSecret。
   
   在应用信息页面单击应用图标，在应用详情页面获取ClientId和ClientSecret（此信息需要提供给API使用者）。
   ![](https://support.huaweicloud.com/bestpractice-oneaccess/public_sys-resources/note_3.0-zh-cn.png)
   - ClientSecret需单击"启用"生成。
   
   - ClientSecret是校验开发者身份的密码，具备高安全性，切勿将其直接提供给第三方开发者或直接存储在代码中。
   
   - 重置后的ClientSecret即时生效，所有使用原ClientSecret的接口将全部失效，请谨慎重置。
   
   - OneAccess不存储ClientSecret，当获取ClientSecret后，请妥善保管。
    
   
   
 
#### 在OneAccess中添加企业API
OneAccess管理员在OneAccess管理门户中添加企业需要的自定义API产品。
1. 登录OneAccess管理门户。
2. 在导航栏中，单击"资源 \> 企业API"。
3. 在企业API页面，单击自定义API产品下的"添加自定义API产品"。
4. 在"添加企业API"页面，上传产品LOGO，填写产品名称和描述，单击"确定"，自定义API产品添加完成，自定义API产品页面显示已添加的API产品。
5. 单击新建的自定义API产品，切换到"应用授权"页签，单击[在OneAccess中添加企业应用]中新建的应用后的"授权"，完成API对应用的授权使用。
6. 切换到"权限信息"页面，添加API权限信息。
 
#### 在OneAccess应用中授权相应API权限
在OneAccess的应用中，授权具体自定义API的权限。
1. 登录OneAccess管理门户。
2. 在导航栏中，单击"资源 \> 应用"。
3. 单击[在OneAccess中添加企业应用]中新建的应用，单击应用图标，进入通用信息页面。
4. 选择"API权限 "，在API权限页面，单击某一权限代码右侧"操作"列的"授权"则授权成功。
 
#### 在OneAccess中获取签名公钥和算法密钥
OneAccess颁发的鉴权Token是经过加密和签发的，需要获取签名公钥和算法密钥给API提供者进行解密。
1. 登录OneAccess管理门户。
2. 在导航栏中，选择"设置 \> 服务配置"，单击"API认证配置"，获取签名公钥和算法密钥，提供给API提供者。 
   ![](https://support.huaweicloud.com/bestpractice-oneaccess/zh-cn_image_0000001558168124.png)
   
   ![](https://support.huaweicloud.com/bestpractice-oneaccess/public_sys-resources/note_3.0-zh-cn.png)
   OneAccess不展示算法密钥，当重置算法密钥后，请妥善保管。
   
   
 
#### API使用者从OneAccess获取鉴权Token
API使用者调用OneAccess鉴权接口获取鉴权Token。
访问接口：https://访问域名/api/v2/tenant/token?grant_type=client_credentials。
PostMan调用示例：
![](https://support.huaweicloud.com/bestpractice-oneaccess/zh-cn_image_0000001877635265.png)
![](https://support.huaweicloud.com/bestpractice-oneaccess/public_sys-resources/note_3.0-zh-cn.png)
- 访问域名为用户访问域名。可在OneAccess实例详情页获取。
- 使用POST访问，使用Basic认证，Basic认证的用户名和密码为[4]中获取的ClientId和ClientSecret。
- 返回的id_token可以由API使用者使用对应API时传递给API提供者做身份认证和授权，其中包含签名信息以及对应的API权限信息。可以使用header来传递。建议使用标准的Authorization Header传递。
- 返回的id_token有有效期，有效期之内，此id_token可以重复使用，有效期的期限由应用中配置决定。
 
#### API提供者校验Token
API使用者调用API提供者的接口时，携带从OneAccess获取的id_token，API提供者在收到API使用者的消息时需要校验该Token，主要校验两个内容：
- Token的签名信息是否正确，保证Token是OneAccess颁发的。
- 校验Token中申明的权限是否包含当前访问当前的API。
下面是java示例代码：
```
import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.apache.commons.codec.binary.Base64;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers;
import org.jose4j.jwe.JsonWebEncryption;
import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
 
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * @author : bsong
 **/
public class JWTTest {
    public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
    public static final String END_CERT = "-----END CERTIFICATE-----";
 
    public static void main(String[] args) throws CertificateException, InvalidJwtException, JoseException {
        //id token由API调用者传递
        String idToken = "";
        // 算法密钥在OneAccess中设置
        String aesKey = "0123******************************************789abc";
        //证书在OneAccess管理门户配置中获取
        String certificate = "-----BEGIN CERTIFICATE-----\n" +
                "MIIC2jCCAcKgAwI..........................QEBCwUAMC4xLDAqBgNVBAMM\n" +
                "I2Jzb25nLmlkYWF..........................GUuY29tMB4XDTIyMDExNDA3\n" +
                "MDY1NVoXDTMyMDE..........................wwjYnNvbmcuaWRhYXMtdGVz\n" +
                "dC1hbHBoYS5iY2N..........................Ib3DQEBAQUAA4IBDwAwggEK\n" +
                "AoIBAQCJ7bfMCVX..........................GnE3W9uiSYk3WFkYFK8vh16\n" +
                "efVuvccAULE+xqi..........................652lsIBNOAC5YPy7J47z4iw\n" +
                "1GiAVYXxwyehgRe3..........................e0eJDKy6Ew5S+TUq72hqSD7\n" +
                "zrtQA3szqSK1pgFB..........................J8rMh9WiF2qUqzCdNRqkQRC\n" +
                "smGGj+PqD86otiif.........................0OPH5UOhR2OEve1cT9dgAlS\n" +
                "Vt1tKbE0l+iUTQqi..........................oZIhvcNAQELBQADggEBAEP8\n" +
                "EmkyoaWjngk3Tn5u..........................cJEDGTbuYO55wKap0BTetu6\n" +
                "cvGFxJYMQYefsx0..........................xn8N4ZgWvwgwDQVQx5WPgAT\n" +
                "QKunLWz30W4GYUE..........................QJZ7ift2sqoBLmkmjfcyqW0\n" +
                "jU1+7/e/ea5XAC3..........................DtVHqufwP4R/TALg1muaNyJ\n" +
                "f7obOcMHAb/OcbP..........................FSAwkVYsxSC9LEEUPhCONvX\n" +
                "KCWoeQoX/qkZH/nBvXU=\n" +
                "-----END CERTIFICATE-----";
 
        RSAPublicKey publicKey = getPublicKeyByCertificate(certificate);
        JsonWebKey jsonWebKey = getJsonWebKey(aesKey);
        JwtClaims jwtClaims = validateIDToken(publicKey, idToken);
        String apiPermission = jwtClaims.getClaimValue("api").toString();
        String permissionString = decryptionIDToken(jsonWebKey, apiPermission);
        System.out.println(permissionString);
        Map<String, List<String>> permissions = getPermissionsFromIdToken(permissionString);
        System.out.println(permissions);
    }
 
    public static Map<String, List<String>> getPermissionsFromIdToken(String permissionString) throws JoseException {
        Map<String, List<String>> result = new HashMap<>();
        Permission permission = JSON.parseObject(permissionString,Permission.class);
        permission.getAuz().stream().forEach(p ->{
            p.entrySet().forEach(e->{
                result.put(e.getKey(),e.getValue());
            });
        });
        return result;
    }
 
    @Data
    public static class Permission{
        List<Map<String, List<String>>> auth_method;
        List<Map<String, List<String>>> auz;
    }
 
    public static RSAPublicKey getPublicKeyByCertificate(String certificate) throws CertificateException {
        CertificateFactory fact = CertificateFactory.getInstance("X.509");
        byte[] decoded = Base64.decodeBase64(certificate.replace(BEGIN_CERT, "").replace(END_CERT, ""));
        InputStream input = new ByteArrayInputStream(decoded);
        X509Certificate cert = (X509Certificate) fact.generateCertificate(input);
        return (RSAPublicKey) cert.getPublicKey();
    }
 
    public static JsonWebKey getJsonWebKey(String key) throws JoseException {
        Map<String,Object> map = new HashMap<>();
        map.put("kty","oct");
        map.put("k",key);
        String jwkJson = JSON.toJSONString(map);
        return JsonWebKey.Factory.newJwk(jwkJson);
    }
 
    public static JwtClaims validateIDToken( RSAPublicKey publicKey,String idToken) throws InvalidJwtException {
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime()                 // JWT必须具有到期时间
                .setAllowedClockSkewInSeconds(300)           //允许在验证基于时间的声明时留有余地以解决时钟偏移
                .setRequireSubject()                        // JWT必须具有主题声明
                .setExpectedIssuer("Issuer")                //谁JWT需要已被发出
                .setExpectedAudience("Audience")            // JWT的目标对象
                .setVerificationKey(publicKey)
                .build();
        return jwtConsumer.processToClaims(idToken);
    }
 
    public static String decryptionIDToken(JsonWebKey jwk, String idToken) throws JoseException {
        JsonWebEncryption jsonWebEncryption = new JsonWebEncryption();
        jsonWebEncryption.setAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, KeyManagementAlgorithmIdentifiers.DIRECT));
        jsonWebEncryption.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256));
        jsonWebEncryption.setCompactSerialization(idToken);
        jsonWebEncryption.setKey(jwk.getKey());
        return jsonWebEncryption.getPlaintextString();
    }
}
```
