# OAuth2 测试
若同时引用了fly-security依赖,可以进行OAuth2相关的单元测试:
<dependency>
<groupId>cn.openfuse</groupId>
<artifactId>fly-test</artifactId>
<version>${fly.version}</version>
</dependency>
<dependency>
<groupId>cn.openfuse</groupId>
<artifactId>fly-security</artifactId>
<version>${fly.version}</version>
</dependency>
在编写代码前,先了解下fly-test中针对OAuth2的单元测试配置。
# OAuth2 配置
单元测试中OAuth2的相关配置,可区分于正式运行所用的OAuth2。
配置前缀:fly.test.security.oauth2
详细见配置类:
fly.test.security.oauth2.TOAuth2Properties
| 配置 | 说明 | 类型 | 默认值 |
|---|---|---|---|
server-url | 测试OAuth2服务地址 | String | - |
login-uri | 登录地址 | String | /login |
authorization-uri | 认证地址 | String | /oauth2/authorize |
token-uri | 令牌地址 | String | /oauth2/token |
user-info-uri | 获取用户信息地址 | String | /oauth2/userinfo |
introspection-uri | 内省地址 | String | /oauth2/introspect |
end-session-uri | 登出地址 | String | /oauth2/logout |
default-redirect-uri | 默认跳转地址 | String | http://app.example.com/authroized |
application.yml:
fly:
test:
security:
oauth2:
server-url: "${oauth2.test.server-url:http://localhost:9000}"
login-uri: "${oauth2.test.login-uri:/login}"
authorization-uri: "${oauth2.test.authorization-uri:/oauth2/authorize}"
token-uri: "${oauth2.test.token-uri:/oauth2/token}"
user-info-uri: "${oauth2.test.user-info-uri:/oauth2/userinfo}"
introspection-uri: "${oauth2.test.introspection-uri:/oauth2/introspect}"
end-session-uri: "${oauth2.test.end-session-uri:/oauth2/logout}"
default-redirect-uri: "${oauth2.test.default-redirect-uri:http://app.example.com/authroized}"
需要注意的是,TEST模块有提供默认配置/fly/spring-defaults.yml(加载机制可见配置加载),引入fly-test依赖即生效:
fly:
test:
security:
oauth2:
server-url: "${oauth2.server-url:${fly.security.oauth2.server-url:}}"
一般我们会通过oauth2.server-url或fly.security.oauth2.server-url配置OAuth2,默认从这两处取值作为单元测试的OAuth2配置,如需要可在当前工程配置覆盖。
# 安全配置
具体配置类:
fly.test.security.TestSecurityProperties
application.yml:
fly:
test:
security:
users: # 测试用户
user1: password1 # 简化配置,只有 username 和 password
user2:
username: user2
password: password2
scopes: [ read, write ] # 该用户拥有的 scope 权限
clients: # 测试客户端
client1: secret1 # 简化配置,只有客户端 ID 和密钥
client2:
client-id: client2
client-secret: secret2
redirect-uri:
logout-uri:
- 测试用户及客户端
配置后,可以在后续的OAuth2或Authorization操作中作为参数传入使用,若不配置也可以在单元测试中自行创建TUser/TClient对象,主要便于在多个单元测试中重复使用的用户或客户端统一管理,配置后一般在同名方法中传入username或者clientId即可。
# OAuth2 请求
接口TOAuth2Template类似于Rest请求的TRestTemplate,主要用于OAuth2的单元测试,又衍生出针对客户端、用户信息、认证等不同场景划分的操作对象。可以通过注入获取:
import fly.test.security.oauth2.TOAuth2Template;
...
@Autowired
protected TOAuth2Template oauth2Template;
下面将罗列关键方法:
- 获取当前请求的OAuth2 配置:
oauth2Template.getProperties() - 从单元测试安全配置里获取客户端或者用户,若不存在则抛异常:
mustGetClient(String clientId)
mustGetUser(String username)
authorization(TClient client):获取授权操作对象token(TClient client):获取Token 操作对象introspection(TClient client):获取令牌内省操作对象userinfo():获取用户操作对象client(String clientId):返回客户端操作对象。
以上述等获取对应功能操作对象方法为基础,TOAuth2Template包装了大量快捷测试的方法,如:
@Test
public void getUserInfo() {
final THttpTemplate http = new THttpTemplate();
final TClient client = new TClient("client", "secret", "http://example.com");
final TUser user = new TUser("user", "password");
oauth2.authorizeForUserInfo(http, client, user);
}
更多内容可以查看接口注解。
# 授权操作
TOAuth2AuthorizationOperations接口提供OAuth2授权端点操作。一般通过TOAuth2Template或者TOAuth2Client等接口的authorization方法获取对象,示例:
@Autowired
protected TOAuth2Template oauth2Template;
...
@Test
public void testAuthorize() {
// 方式1
TClient client1 = oauth2Template.mustGetClient("client1");
TOAuth2AuthorizationOperations authorizationOps1 = oauth2Template.authorization(client1);
// 方式2
TOAuth2Client clientOps = oauth2Template.client("client1");
TOAuth2AuthorizationOperations authorizationOps2 = clientOps.authorization();
// ...
}
示例中的client1均是从安全配置的测试客户端列表中获取,后续的授权都基于该客户端身份。常用方法有:
authorize授权
以authorize前缀命名的方法,都是通过授权,然后返回不同的结果,如authorizeForResponse()与authorizeForAccessToken(),前者是将授权请求后响应结果原本返回,而后者是只提取access token字符串返回。另外在授权时,可以选择是否传入用户信息TUser参数。
简单示例如下:
@Autowired
protected TOAuth2Template oauth2Template;
...
@Test
public void testAuthorize() {
TClient client1 = oauth2Template.mustGetClient("client1");
TOAuth2AuthorizationOperations authorizationOps = oauth2Template.authorization(client1);
// 获取纯客户端身份的 access token
String accessToken = authorizationOps.authorizeForAccessToken();
// 获取携带用户身份的 access token
TUser user1 = oauth2Template.mustGetUser("user1");
accessToken = authorizationOps.authorizeForAccessToken(user1);
}
TOAuth2AuthorizationOperations flow(Flow flow)
修改授权身份验证方式,默认为Flow.CODE授权码流,可以切换为隐式流。注意,该方法为创建一个新对象,新对象才应用新的授权方式。
# Token 操作
TOAuth2TokenOperations接口提供OAuth2 token端点操作。一般通过TOAuth2Template或者TOAuth2Client等接口的token方法获取对象,示例:
@Autowired
protected TOAuth2Template oauth2Template;
...
@Test
public void testToken() {
// 方式1
TClient client1 = oauth2Template.mustGetClient("client1");
TOAuth2TokenOperations tokenOps1 = oauth2Template.token(client1);
// 方式2
TOAuth2Client clientOps = oauth2Template.client("client1");
TOAuth2TokenOperations tokenOps2 = clientOps.token();
// ...
}
示例中的client1均是从安全配置的测试客户端列表中获取,后续的授权都基于该客户端身份。常用方法有:
下列方法中的*表示通配符,有一定的命名规则。
get*ByCode(String code)
根据authorization code请求获取token等不同返回类型的信息,如String getAccessTokenByCode(String code),返回为access token字符串。
get*ByClientCredentials()
采用客户端公钥、密钥方式获取token等不同返回类型的信息,只携带客户端身份,所获取的access token只能用于访问与用户无关的API。
get*ByRefreshToken(String refreshToken)
根据refresh token重新获取token信息。
# 令牌内省操作
TOAuth2IntrospectOperations接口提供OAuth2 Introspection端点操作。OAuth2 Introspection提供了一种机制以获取有关访问令牌的信息,检查访问令牌的有效性,和获取如关联的用户和scope等。
一般通过TOAuth2Template或者TOAuth2Client等接口的introspection方法获取对象,简单示例:
@Autowired
protected TOAuth2Template oauth2Template;
...
@Test
public void testIntrospect()
TClient client1 = oauth2Template.mustGetClient("client1");
TOAuth2IntrospectOperations introspectOps = oauth2Template.introspection(client1);
String accessToken = "...";
// 调用 {oauth2.server-url}/oauth2/introspect
TTokenInfo tokenInfo = introspectOps.introspect(accessToken);
// 简化:可以直接使用 TOAuth2Template 的 introspect 方法
tokenInfo = oauth2Template.introspect(client1, accessToken);
// 单元测试只有 TUser 的情况下,自动获取 access token
TUser user1 = oauth2Template.mustGetUser("user1");
tokenInfo = oauth2Template.introspect(client1, user1);
}
# 用户操作
TOAuth2UserInfoOperations接口提供用户信息相关的操作,可以通过TAuth2Template的userinfo方法获取对象:
@Autowired
protected TOAuth2Template oauth2Template;
...
@Test
public void testUserinfo() {
TOAuth2UserInfoOperations userinfoOps = oauth2Template.userinfo();
// 从 access token 获取用户信息
String accessToken = "...";
TUserInfo user = userinfoOps.request(accessToken);
}
# 客户端操作
上面几节中,我们可以了解到授权、令牌等操作在创建对象时需要传入客户端参数,在测试中,如果已经明确用使同一个客户端且会用到多个类似这些的操作,可以使用TOAuth2Client,该接口包装了与客户端相关的操作方法,只要在创建TOAuth2Client时设置一次客户端,后续获取到的授权操作等对象都是围绕该客户端进行。
可以通过TOAuth2Template的client方法获取对象,简单示例:
@Autowired
protected TOAuth2Template oauth2Template;
...
@Test
public void testClient() {
TOAuth2Client clientOps = oauth2Template.client("client1");
// 获取授权操作对象
TOAuth2AuthorizationOperations authorizationOps = client.authorization();
// 获取 token 操作对象
TOAuth2TokenOperations tokenOps = clientOps.token();
// 获取令牌内省操作对象
TOAuth2IntrospectOperations introspectOps = clientOps.introspection();
// 一些常用方法可以直接从 TOAuth2Client 调用,无需先获取对应的操作对象
// tokenOps.getAccessTokenByClientCredentials();
clientOps.authenticateForAccessToken();
}