AI 网关支持全局认证和消费者鉴权,确保只有授权请求才能访问服务。本文介绍消费者在AI 网关中如何通过认证和鉴权确保安全访问资源。
背景信息
相比全局认证鉴权适用于统一登录认证等ToC场景,API开启消费者认证,适用于授权API给合作伙伴等ToB场景。
对比项 | 全局认证鉴权 | 消费者鉴权 |
适用场景 | 统一登录认证等ToC场景。 | 授权API给合作伙伴等ToB场景。 |
核心差异 | 开启认证的同时也开启鉴权。 | 开启认证后,需要额外做鉴权配置。 |
配置入口 | 。 |
|
认证方式配置(以JWT认证为例) |
|
|
鉴权方式配置 | 创建配置时填写黑名单或白名单的域名和路径(Path)列表。
|
|
注意事项
当用户在消费者认证中开启认证后,认证策略会即时生效,如果此时API已发布,但是又未给API配置消费者和授权规则,则默认会拒绝所有访问请求。
使用消费者认证鉴权
JWT认证
JWT认证流程概览:
客户端向AI 网关发起认证请求,请求中一般会携带终端用户的用户名和密码。
网关将请求直接转发给后端服务。
后端服务读取请求中的验证信息(比如用户名、密码)进行验证,验证通过后使用私钥生成标准的Token,返回给网关。
网关将携带Token的应答返回给客户端,客户端需要将这个Token缓存到本地。
客户端向AI 网关发送业务请求,请求中携带Token。
网关使用用户设定的公钥对请求中的Token进行验证,验证通过后,将请求透传给后端服务。
后端服务进行业务处理后应答。
网关将业务应答返回给客户端。
下文主要介绍生成Token、客户端向网关发送请求、网关使用设定的公钥进行Token验证的过程。
认证服务生成Token
下文将通过Java示例来说明Token的生成方式,其他语言您也可使用相关的工具生成密钥对。
新建Maven项目并注入依赖。
首先,新建一个Maven项目,注入如下依赖项:
<dependency> <groupId>org.bitbucket.b_c</groupId> <artifactId>jose4j</artifactId> <version>0.7.0</version> </dependency>
选择Token生成方式。
您可以选择使用默认对称密钥示例生成Token和非对称密钥示例生成Token两种方式来生成Token。根据您的需求进行选择:
使用默认对称密钥示例生成Token
代码示例:
package org.example; import java.io.UnsupportedEncodingException; import java.security.PrivateKey; import org.jose4j.base64url.Base64; import org.jose4j.json.JsonUtil; import org.jose4j.jwk.OctJwkGenerator; import org.jose4j.jwk.OctetSequenceJsonWebKey; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.NumericDate; import org.jose4j.keys.HmacKey; import org.jose4j.lang.JoseException; import sun.lwawt.macosx.CSystemTray; public class Main { public static void main(String[] args) throws JoseException, UnsupportedEncodingException { //使用本文上述示例 String privateKeyJson = "{\n" + " \"k\": \"VoBG-oyqVoyCr9G56ozmq8n_rlDDyYMQOd_DO4GOkEY\",\n" + " \"kty\": \"oct\",\n" + " \"alg\": \"HS256\",\n" + "}"; JwtClaims claims = new JwtClaims(); claims.setGeneratedJwtId(); claims.setIssuedAtToNow(); //设置过期时间,并且小于7天 NumericDate date = NumericDate.now(); date.addSeconds(120*60); claims.setExpirationTime(date); claims.setNotBeforeMinutesInThePast(1); //添加自定义参数,所有值请都使用String类型 //设置消费者标识 claims.setClaim("uid", "11215ac069234abcb8944232b79ae711"); JsonWebSignature jws = new JsonWebSignature(); //设置加密算法 jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256); jws.setKey(new HmacKey(Base64.decode(JsonUtil.parseJson(privateKeyJson).get("k").toString()))); jws.setPayload(claims.toJson()); String jwtResult = jws.getCompactSerialization(); System.out.println("Generate Json Web token , result is \n " + jwtResult); } }
代码相关设置说明:
privateKeyJson
:即在创建消费者时使用的JWKS,可以在创建消费者时记录下自己使用的JWKS,也可以在创建消费者后,在消费者基础配置页获取JWKS。设置消费者标识。即
claims.setClaim("uid", "11215ac069234abcb8944232b79ae711")
,该消费者标识为创建消费者时控制台默认生成,也可以根据自身逻辑进行修改。您也可以在创建消费者后,在消费者基础配置页获取消费者标识。设置加密算法。即
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256)
,该加密算法要和JWKS保持一致。说明目前支持的加密算法有ES256、ES384、ES512、RS256、RS384、RS512、PS256、PS384、PS512、HS256、HS384、HS512和EdDSA。
使用对称加密的时,需要对"k"进行解码。
jws.setKey(new HmacKey(Base64.decode(JsonUtil.parseJson(privateKeyJson).get("k").toString())));
设置过期时间。过期时间需要小于7天,超出过期时间后,请重新生成Token,以保证Token的安全性。
... NumericDate date = NumericDate.now(); date.addSeconds(120*60); claims.setExpirationTime(date); claims.setNotBeforeMinutesInThePast(1); ...
根据自身业务需要,可以在JWKS的PAYLOAD中添加自定义参数。
使用非对称密钥示例生成Token
代码示例:
package org.example; import java.io.UnsupportedEncodingException; import java.security.PrivateKey; import org.jose4j.base64url.Base64; import org.jose4j.json.JsonUtil; import org.jose4j.jwk.OctJwkGenerator; import org.jose4j.jwk.OctetSequenceJsonWebKey; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.NumericDate; import org.jose4j.keys.HmacKey; import org.jose4j.lang.JoseException; import sun.lwawt.macosx.CSystemTray; public class Main { public static void main(String[] args) throws JoseException, UnsupportedEncodingException { //使用本文上述示例 String privateKeyJson = "{\n" + " \"k\": \"VoBG-oyqVoyCr9G56ozmq8n_rlDDyYMQOd_DO4GOkEY\",\n" + " \"kty\": \"oct\",\n" + " \"alg\": \"HS256\",\n" + "}"; JwtClaims claims = new JwtClaims(); claims.setGeneratedJwtId(); claims.setIssuedAtToNow(); //设置过期时间,并且小于7天 NumericDate date = NumericDate.now(); date.addSeconds(120*60); claims.setExpirationTime(date); claims.setNotBeforeMinutesInThePast(1); //添加自定义参数,所有值请都使用String类型 //设置消费者标识 claims.setClaim("uid", "11215ac069234abcb8944232b79ae711"); JsonWebSignature jws = new JsonWebSignature(); //设置加密算法 jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256); jws.setKey(new HmacKey(Base64.decode(JsonUtil.parseJson(privateKeyJson).get("k").toString()))); jws.setPayload(claims.toJson()); String jwtResult = jws.getCompactSerialization(); System.out.println("Generate Json Web token , result is \n " + jwtResult); } }
代码相关设置说明:
设置
privateKeyJson
、消费者标识、过期时间,同对称加密算法。设置加密算法,即
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256)
。该加密算法和JWKS保持一致。对于非对称加密算法,要用其私钥进行加密。
... jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyJson)).getPrivateKey(); jws.setKey(privateKey); ...
根据自身业务需要,可以在JWKS的
PAYLOAD
中添加自定义参数。
客户端向网关发送业务请求
目前AI 网关支持Header的模式传递Token,客户可以自定义请求Header的名字和Token的前缀,请求验证时必须与消费者认证方式配对的key和前缀保持一致。
请求不携带提供JWT,返回401。
curl http://xxx.hello.com/test
请求携带错误JWT,返回401。
curl http://xxx.hello.com/test -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ1'
请求携带JWT,但JWT代表的消费者无权访问API时,返回403。
# consumer1没有授权给下列路径指定api curl 'http://xxx.example.com/test' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
服务端验证Token请求
服务端验证Token主要分为以下三步:
服务端收到用户请求时,先检查是否携带Token,未携带Token的请求,直接拒绝,返回401。
携带Token的请求,使用用户jwks中配置的私钥验证Token是否合法且有效,不合法或者失效,直接拒绝,返回401。
如果Token合法且有效后,再校验其代表的消费者是否授权给正在访问的API。
常见错误码说明
HTTP 状态码 | 出错信息 | 原因说明 |
401 | Jwt missing | 请求头未提供JWT |
401 | Jwt expired | JWT已经过期 |
401 | Jwt verification fails | JWT payload校验失败,如iss不匹配 |
403 | Access Denied | 无权限访问当前API |
API Key认证
如果Model API未开启消费者认证鉴权策略,可直接访问。
如果Model API已开启消费者认证鉴权策略,但未授权,则提示401。
调试和调用类似,本文以调试Model API为例。
API Key的凭证来源主要有三类:
默认凭证来源:Authorization: <Bearer> Token。
自定义Header,填写Header参数名。
自定义Query参数,填写query参数名。
默认凭证来源
在调试Model API时,您可以通过在调试页面的HTTP请求头中设置API Key,以确保请求的身份验证。具体步骤如下:
在Authorization字段中,将Bearer作为前缀。
确保在Bearer和API Key之间留一个空格。
假设我的API Key是123456abc,则完整的CURL命令应如下所示。
自定义Header
除了标准的请求头,您还可以定义自定义HTTP请求头来传递特定信息。假设我们需要添加一个名为X-Client-ID的自定义请求头,以标识客户端应用,则完整的CURL命令应如下所示。
自定义Query参数
在请求URL中,您还可以通过增加自定义Query参数来传递额外的信息或配置。假设我需要添加一个名为version的Query参数,以指定API版本,则完整的CURL命令应如下所示。
相关错误码
HTTP 状态码 | 出错信息 | 原因说明 |
401 | Request denied by Key Auth check. Muti API key found in request. | 请求提供多个 API Key。 |
401 | Request denied by Key Auth check. No API key found in request. | 请求未提供 API Key。 |
401 | Request denied by Key Auth check. Invalid API key. | 不允许当前 API Key 访问。 |
403 | Request denied by Key Auth check. Unauthorized consumer. | 请求的调用方无访问权限。 |
相关文档
您可以对API进行授权和管理,具体操作,请参见授权管理。