OSPP-2023_shenyu

This commit is contained in:
Saiph
2023-09-19 19:49:44 +08:00
parent 4142a9e5dd
commit 1cc3ffa7cf
15 changed files with 1922 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shenyu-plugin-security</artifactId>
<groupId>org.apache.shenyu</groupId>
<version>2.6.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shenyu-plugin-maxkey</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-plugin-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<!-- Spring Security Test -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey;
import cn.hutool.core.codec.Base64;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.utils.GsonUtils;
import org.apache.shenyu.common.utils.Singleton;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.result.ShenyuResultEnum;
import org.apache.shenyu.plugin.api.result.ShenyuResultWrap;
import org.apache.shenyu.plugin.api.utils.WebFluxResultUtils;
import org.apache.shenyu.plugin.base.AbstractShenyuPlugin;
import org.apache.shenyu.plugin.maxkey.config.MaxkeyConfig;
import org.apache.shenyu.plugin.maxkey.service.MaxkeyService;
import org.apache.shenyu.plugin.maxkey.service.MaxkeyUser;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Objects;
/**
* The type Maxkey plugin.
*/
public class MaxKeyPlugin extends AbstractShenyuPlugin {
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final ShenyuPluginChain chain, final SelectorData selector, final RuleData rule) {
MaxkeyService maxkeyService = Singleton.INST.get(MaxkeyService.class);
MaxkeyConfig config = maxkeyService.getMaxkeyConfig();
ServerHttpRequest request = exchange.getRequest();
// 这里处理需要token的逻辑
if (config.isBearerOnly()) {
String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
boolean isActive = maxkeyService.introspectAccessToken(token);
if (isActive) {
// 根据配置决定是否获取userInfo
return chain.execute(handlerUserInfo(exchange, token, maxkeyService, config.isSetUserInfoHeader()));
}
Object error = ShenyuResultWrap.error(exchange, ShenyuResultEnum.ERROR_TOKEN);
return WebFluxResultUtils.result(exchange, error);
}
// 走到这里说明没有token 需要处理code授权码的逻辑
String code = request.getQueryParams().getFirst("code");
String state = request.getQueryParams().getFirst("state");
if (Objects.nonNull(code)) {
String token = maxkeyService.getOAuthToken(code);
// 根据配置决定是否获取userInfo
return chain.execute(handlerUserInfo(exchange, token, maxkeyService, config.isSetUserInfoHeader()));
}
// 走到这里说明没有code 需要重定向至IdP服务获取code
return maxkeyService.redirect(exchange, state);
}
@Override
public int getOrder() {
return PluginEnum.MAXKEY.getCode();
}
@Override
public String named() {
return PluginEnum.MAXKEY.getName();
}
@Override
public boolean skip(final ServerWebExchange exchange) {
return false;
}
// 判断直接传递token还是传递userInfo
private ServerWebExchange handlerUserInfo(final ServerWebExchange exchange, final String token, final MaxkeyService maxkeyService, final boolean setUserInfo) {
if (setUserInfo) {
MaxkeyUser maxkeyUser = maxkeyService.getMaxkeyUser(token);
return handleToken(exchange, maxkeyUser);
} else {
return handleToken(exchange, token);
}
}
// 直接使用AccessToken访问收保护的资源
private ServerWebExchange handleToken(final ServerWebExchange exchange, final String accessToken) {
ServerHttpRequest.Builder mutate = exchange.getRequest().mutate();
mutate.headers(httpHeaders -> httpHeaders.remove(HttpHeaders.ACCEPT_ENCODING));
mutate.header(HttpHeaders.AUTHORIZATION, accessToken);
return exchange.mutate().request(mutate.build()).build();
}
// 获取原始请求对象 根据MaxKey认证解析后的userInfo 重新构建请求头 访问收保护的资源
private ServerWebExchange handleToken(final ServerWebExchange exchange, final MaxkeyUser maxkeyUser) {
ServerHttpRequest.Builder mutate = exchange.getRequest().mutate();
mutate.headers(httpHeaders -> httpHeaders.remove(HttpHeaders.ACCEPT_ENCODING));
String maxkeyUserInfoJson = GsonUtils.getInstance().toJson(maxkeyUser);
mutate.header("X-Userinfo", Base64.encode(maxkeyUserInfoJson));
return exchange.mutate().request(mutate.build()).build();
}
}

View File

@@ -0,0 +1,377 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.config;
public class MaxkeyConfig {
private String clientId;
private String clientSecret;
private String authorizationEndpoint;
private String scope;
private String responseType;
private String redirectUrl;
private String realm;
private String grantType;
private String tokenEndpoint;
private boolean bearerOnly;
private String introspectionEndpoint;
private boolean setUserInfoHeader;
private String userInfoEndpoint;
private String introspectionEndpointAuthMethodsSupported;
private String discovery;
public MaxkeyConfig() {
}
public MaxkeyConfig(final String clientId,
final String clientSecret,
final String authorizationEndpoint,
final String scope,
final String responseType,
final String redirectUrl,
final String realm,
final String grantType,
final String tokenEndpoint,
final boolean bearerOnly,
final String introspectionEndpoint,
final boolean setUserInfoHeader,
final String userInfoEndpoint,
final String introspectionEndpointAuthMethodsSupported,
final String discovery) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.authorizationEndpoint = authorizationEndpoint;
this.scope = scope;
this.responseType = responseType;
this.redirectUrl = redirectUrl;
this.realm = realm;
this.grantType = grantType;
this.tokenEndpoint = tokenEndpoint;
this.bearerOnly = bearerOnly;
this.introspectionEndpoint = introspectionEndpoint;
this.setUserInfoHeader = setUserInfoHeader;
this.userInfoEndpoint = userInfoEndpoint;
this.introspectionEndpointAuthMethodsSupported = introspectionEndpointAuthMethodsSupported;
this.discovery = discovery;
}
/**
* Gets clientId.
*
* @return the clientId
*/
public String getClientId() {
return clientId;
}
/**
* Sets clientId.
*
* @param clientId the clientId
*/
public void setClientId(final String clientId) {
this.clientId = clientId;
}
/**
* Gets clientSecret.
*
* @return the clientSecret
*/
public String getClientSecret() {
return clientSecret;
}
/**
* Sets clientSecret.
*
* @param clientSecret the clientSecret
*/
public void setClientSecret(final String clientSecret) {
this.clientSecret = clientSecret;
}
/**
* Gets authorizationEndpoint.
*
* @return the authorizationEndpoint
*/
public String getAuthorizationEndpoint() {
return authorizationEndpoint;
}
/**
* Sets authorizationEndpoint.
*
* @param authorizationEndpoint the authorizationEndpoint
*/
public void setAuthorizationEndpoint(final String authorizationEndpoint) {
this.authorizationEndpoint = authorizationEndpoint;
}
/**
* Gets scope.
*
* @return the scope
*/
public String getScope() {
return scope;
}
/**
* Sets scope.
*
* @param scope the scope
*/
public void setScope(final String scope) {
this.scope = scope;
}
/**
* Gets responseType.
*
* @return the responseType
*/
public String getResponseType() {
return responseType;
}
/**
* Sets responseType.
*
* @param responseType the responseType
*/
public void setResponseType(final String responseType) {
this.responseType = responseType;
}
/**
* Gets redirectUrl.
*
* @return the redirectUrl
*/
public String getRedirectUrl() {
return redirectUrl;
}
/**
* Sets redirectUrl.
*
* @param redirectUrl the redirectUrl
*/
public void setRedirectUrl(final String redirectUrl) {
this.redirectUrl = redirectUrl;
}
/**
* Gets realm.
*
* @return the realm
*/
public String getRealm() {
return realm;
}
/**
* Sets realm.
*
* @param realm the realm
*/
public void setRealm(final String realm) {
this.realm = realm;
}
/**
* Gets tokenType.
*
* @return the tokenType
*/
public String getGrantType() {
return grantType;
}
/**
* Sets grantType.
*
* @param grantType the grantType
*/
public void setGrantType(final String grantType) {
this.grantType = grantType;
}
/**
* Gets tokenEndpoint.
*
* @return the tokenEndpoint
*/
public String getTokenEndpoint() {
return tokenEndpoint;
}
/**
* Sets tokenEndpoint.
*
* @param tokenEndpoint the tokenEndpoint
*/
public void setTokenEndpoint(final String tokenEndpoint) {
this.tokenEndpoint = tokenEndpoint;
}
/**
* Is bearerOnly.
*
* @return is bearerOnly
*/
public boolean isBearerOnly() {
return bearerOnly;
}
/**
* Sets bearerOnly.
*
* @param bearerOnly the bearerOnly
*/
public void setBearerOnly(final boolean bearerOnly) {
this.bearerOnly = bearerOnly;
}
/**
* Gets introspectionEndpoint.
*
* @return the introspectionEndpoint
*/
public String getIntrospectionEndpoint() {
return introspectionEndpoint;
}
/**
* Sets introspectionEndpoint.
*
* @param introspectionEndpoint the introspectionEndpoint
*/
public void setIntrospectionEndpoint(final String introspectionEndpoint) {
this.introspectionEndpoint = introspectionEndpoint;
}
/**
* Is setUserInfoHeader.
*
* @return is setUserInfoHeader
*/
public boolean isSetUserInfoHeader() {
return setUserInfoHeader;
}
/**
* Sets setUserInfoHeader.
*
* @param setUserInfoHeader the setUserInfoHeader
*/
public void setSetUserInfoHeader(final boolean setUserInfoHeader) {
this.setUserInfoHeader = setUserInfoHeader;
}
/**
* Gets userInfoEndpoint.
*
* @return the userInfoEndpoint
*/
public String getUserInfoEndpoint() {
return userInfoEndpoint;
}
/**
* Sets userInfoEndpoint.
*
* @param userInfoEndpoint the userInfoEndpoint
*/
public void setUserInfoEndpoint(final String userInfoEndpoint) {
this.userInfoEndpoint = userInfoEndpoint;
}
/**
* Gets introspectionEndpointAuthMethodsSupported.
*
* @return the introspectionEndpointAuthMethodsSupported
*/
public String getIntrospectionEndpointAuthMethodsSupported() {
return introspectionEndpointAuthMethodsSupported;
}
/**
* Sets introspectionEndpointAuthMethodsSupported.
*
* @param introspectionEndpointAuthMethodsSupported the accessToken
*/
public void setIntrospectionEndpointAuthMethodsSupported(final String introspectionEndpointAuthMethodsSupported) {
this.introspectionEndpointAuthMethodsSupported = introspectionEndpointAuthMethodsSupported;
}
/**
* Gets discovery.
*
* @return the discovery
*/
public String getDiscovery() {
return discovery;
}
/**
* Sets discovery.
*
* @param discovery the discovery
*/
public void setDiscovery(final String discovery) {
this.discovery = discovery;
}
@Override
public String toString() {
return "MaxkeyConfig{"
+ "clientId='" + clientId + '\''
+ ", clientSecret='" + clientSecret + '\''
+ ", authorizationEndpoint='" + authorizationEndpoint + '\''
+ ", scope='" + scope + '\''
+ ", responseType='" + responseType + '\''
+ ", redirectUrl='" + redirectUrl + '\''
+ ", realm='" + realm + '\''
+ ", grantType='" + grantType + '\''
+ ", tokenEndpoint='" + tokenEndpoint + '\''
+ ", bearerOnly=" + bearerOnly
+ ", introspectionEndpoint='" + introspectionEndpoint + '\''
+ ", setUserInfoHeader=" + setUserInfoHeader
+ ", userInfoEndpoint='" + userInfoEndpoint + '\''
+ ", introspectionEndpointAuthMethodsSupported='" + introspectionEndpointAuthMethodsSupported + '\''
+ ", discovery='" + discovery + '\''
+ '}';
}
}

View File

@@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.handle;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.utils.GsonUtils;
import org.apache.shenyu.common.utils.Singleton;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
import org.apache.shenyu.plugin.maxkey.config.MaxkeyConfig;
import org.apache.shenyu.plugin.maxkey.service.MaxkeyService;
import java.util.Map;
import java.util.Optional;
public class MaxkeyPluginDataHandler implements PluginDataHandler {
@Override
public void handlerPlugin(final PluginData pluginData) {
// 获取配置参数
Map<String, String> configMap = GsonUtils.getInstance().toObjectMap(pluginData.getConfig(), String.class);
final String clientId = Optional.ofNullable(configMap.get("clientId")).orElse("");
final String clientSecret = Optional.ofNullable(configMap.get("clientSecret")).orElse("");
final String authorizationEndpoint = Optional.ofNullable(configMap.get("authorizationEndpoint")).orElse("");
final String scope = Optional.ofNullable(configMap.get("scope")).orElse("");
final String responseType = Optional.ofNullable(configMap.get("responseType")).orElse("");
final String redirectUrl = Optional.ofNullable(configMap.get("redirectUrl")).orElse("");
final String realm = Optional.ofNullable(configMap.get("realm")).orElse("");
final String grantType = Optional.ofNullable(configMap.get("grantType")).orElse("");
final String tokenEndpoint = Optional.ofNullable(configMap.get("tokenEndpoint")).orElse("");
final boolean bearerOnly = Optional.ofNullable(configMap.get("bearerOnly")).map(Boolean::parseBoolean).orElse(false);
final String introspectionEndpoint = Optional.ofNullable(configMap.get("introspectionEndpoint")).orElse("");
final String introspectionEndpointAuthMethodsSupported = Optional.ofNullable(configMap.get("introspectionEndpointAuthMethodsSupported")).orElse("");
final boolean setUserInfoHeader = Optional.ofNullable(configMap.get("setUserInfoHeader")).map(Boolean::parseBoolean).orElse(false);
final String userInfoEndpoint = Optional.ofNullable(configMap.get("userInfoEndpoint")).orElse("");
final String discovery = Optional.ofNullable(configMap.get("discovery")).orElse("");
// 获取MaxkeyConfig
MaxkeyConfig maxkeyConfig = new MaxkeyConfig(
clientId,
clientSecret,
authorizationEndpoint,
scope,
responseType,
redirectUrl,
realm,
grantType,
tokenEndpoint,
bearerOnly,
introspectionEndpoint,
setUserInfoHeader,
userInfoEndpoint,
introspectionEndpointAuthMethodsSupported,
discovery);
// 根据参数实例化 MaxkeyService 鉴权服务
MaxkeyService maxkeyService = new MaxkeyService(maxkeyConfig);
Singleton.INST.single(MaxkeyService.class, maxkeyService);
}
@Override
public String pluginNamed() {
return PluginEnum.MAXKEY.getName();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.service;
public class Introspection {
private String token;
private boolean active;
private String sub;
/**
* Gets token.
*
* @return the token
*/
public String getToken() {
return token;
}
/**
* Sets token.
*
* @param token the token
*/
public void setToken(final String token) {
this.token = token;
}
/**
* Gets active.
*
* @return is active
*/
public boolean isActive() {
return active;
}
/**
* Sets active.
*
* @param active the active
*/
public void setActive(final boolean active) {
this.active = active;
}
/**
* Gets sub.
*
* @return the sub
*/
public String getSub() {
return sub;
}
/**
* Sets sub.
*
* @param sub the sub
*/
public void setSub(final String sub) {
this.sub = sub;
}
}

View File

@@ -0,0 +1,180 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.service;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.google.gson.JsonSyntaxException;
import org.apache.commons.lang3.StringUtils;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.shenyu.common.utils.GsonUtils;
import org.apache.shenyu.plugin.maxkey.config.MaxkeyConfig;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
public class MaxkeyService {
private static final int REDIRECT_STATE_CODE = 302;
private final MaxkeyConfig maxkeyConfig;
public MaxkeyService(final MaxkeyConfig maxkeyConfig) {
this.maxkeyConfig = maxkeyConfig;
}
/**
* redirect unauthenticated requests to the IdP service.
*
* @param exchange exchange
* @param state state
* @return void
*/
public Mono<Void> redirect(final ServerWebExchange exchange, final String state) {
ServerHttpResponse response = exchange.getResponse();
String redirectUrl = UriComponentsBuilder.fromUriString(maxkeyConfig.getAuthorizationEndpoint())
.queryParam("response_type", maxkeyConfig.getResponseType())
.queryParam("client_id", maxkeyConfig.getClientId())
.queryParam("redirect_uri", maxkeyConfig.getRedirectUrl())
.queryParam("scope", maxkeyConfig.getScope())
.queryParam("state", state)
.build()
.toUriString();
response.setRawStatusCode(REDIRECT_STATE_CODE);
response.getHeaders().add(HttpHeaders.LOCATION, redirectUrl);
return response.setComplete();
}
/**
* getAccessToken from maxkey service.
*
* @param code code
* @return String
*/
public String getOAuthToken(final String code) {
try {
OAuthClientRequest oAuthClientRequest = OAuthClientRequest
.tokenLocation(maxkeyConfig.getTokenEndpoint())
.setGrantType(GrantType.AUTHORIZATION_CODE)
.setClientId(maxkeyConfig.getClientId())
.setClientSecret(maxkeyConfig.getClientSecret())
.setRedirectURI(String.format("%s", maxkeyConfig.getRedirectUrl()))
.setCode(code)
.buildQueryMessage();
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
OAuthJSONAccessTokenResponse oAuthResponse = oAuthClient.accessToken(oAuthClientRequest, OAuth.HttpMethod.POST);
return oAuthResponse.getAccessToken();
} catch (OAuthSystemException | OAuthProblemException e) {
throw new RuntimeException("Code error, cannot get OAuth token from maxkey server.", e);
}
}
/**
* getOidcToken from maxkey service.
*
* @param code code
* @param state state
* @return OIDCToken
*/
public OIDCToken getOidcToken(final String code, final String state) {
String url = maxkeyConfig.getTokenEndpoint();
String responseType = maxkeyConfig.getResponseType();
String redirectUri = maxkeyConfig.getRedirectUrl();
String scope = maxkeyConfig.getScope();
String clientId = maxkeyConfig.getClientId();
String clientSecret = maxkeyConfig.getClientSecret();
String grantType = maxkeyConfig.getGrantType();
String requestUrl = String.format("%s?response_type=%s&code=%s&redirect_uri=%s&scope=%s&client_id=%s&client_secret=%s&grant_type=%s&state=%s",
url, responseType, code, redirectUri, scope, clientId, clientSecret, grantType, state);
HttpResponse response = HttpUtil.createGet(requestUrl).execute();
OIDCToken oidcToken;
try {
oidcToken = GsonUtils.getInstance().fromJson(response.body(), OIDCToken.class);
} catch (JsonSyntaxException e) {
throw new RuntimeException("Code error, cannot get OAuth token from maxkey server.", e);
}
return oidcToken;
}
/**
* introspect AccessToken via maxkey authentication server.
*
* @param token access token
* @return boolean
*/
public boolean introspectAccessToken(final String token) {
if (StringUtils.isBlank(token)) {
return false;
}
String url = maxkeyConfig.getIntrospectionEndpoint();
String requestUrl = String.format("%s?access_token=%s", url, token);
HttpResponse response = HttpUtil
.createGet(requestUrl)
.execute();
Introspection introspection = GsonUtils.getInstance().fromJson(response.body(), Introspection.class);
return introspection.isActive();
}
/**
* get maxkey user by access token.
*
* @param token access token
* @return MaxkeyUser
*/
public MaxkeyUser getMaxkeyUser(final String token) {
String fullUserInfoJson = getUserInfo(token);
return GsonUtils.getInstance().fromJson(fullUserInfoJson, MaxkeyUser.class);
}
/**
* get user info by access token.
*
* @param token access token
* @return String
*/
public String getUserInfo(final String token) {
String url = maxkeyConfig.getUserInfoEndpoint();
String requestUrl = String.format("%s?access_token=%s", url, token);
HttpResponse response = HttpUtil
.createGet(requestUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.execute();
return response.body();
}
/**
* get maxkey config.
*
* @return MaxkeyConfig
*/
public MaxkeyConfig getMaxkeyConfig() {
return this.maxkeyConfig;
}
}

View File

@@ -0,0 +1,376 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.service;
import com.google.gson.annotations.SerializedName;
public class MaxkeyUser {
private String userId;
private String name;
private String displayName;
private String department;
private String departmentId;
private String gender;
private String phoneNumber;
private String email;
private String region;
private Address address;
/**
* Gets userId.
*
* @return the userId
*/
public String getUserId() {
return userId;
}
/**
* Sets userId.
*
* @param userId the userId
*/
public void setUserId(final String userId) {
this.userId = userId;
}
/**
* Gets clientId.
*
* @return the clientId
*/
public String getName() {
return name;
}
/**
* Sets name.
*
* @param name the name
*/
public void setName(final String name) {
this.name = name;
}
/**
* Gets displayName.
*
* @return the displayName
*/
public String getDisplayName() {
return displayName;
}
/**
* Sets displayName.
*
* @param displayName the displayName
*/
public void setDisplayName(final String displayName) {
this.displayName = displayName;
}
/**
* Gets department.
*
* @return the department
*/
public String getDepartment() {
return department;
}
/**
* Sets department.
*
* @param department the department
*/
public void setDepartment(final String department) {
this.department = department;
}
/**
* Gets departmentId.
*
* @return the departmentId
*/
public String getDepartmentId() {
return departmentId;
}
/**
* Sets departmentId.
*
* @param departmentId the departmentId
*/
public void setDepartmentId(final String departmentId) {
this.departmentId = departmentId;
}
/**
* Gets gender.
*
* @return the gender
*/
public String getGender() {
return gender;
}
/**
* Sets gender.
*
* @param gender the gender
*/
public void setGender(final String gender) {
this.gender = gender;
}
/**
* Gets phoneNumber.
*
* @return the phoneNumber
*/
public String getPhoneNumber() {
return phoneNumber;
}
/**
* Sets gender.
*
* @param phoneNumber the phoneNumber
*/
public void setPhoneNumber(final String phoneNumber) {
this.phoneNumber = phoneNumber;
}
/**
* Gets email.
*
* @return the email
*/
public String getEmail() {
return email;
}
/**
* Sets email.
*
* @param email the email
*/
public void setEmail(final String email) {
this.email = email;
}
/**
* Gets region.
*
* @return the region
*/
public String getRegion() {
return region;
}
/**
* Sets region.
*
* @param region the region
*/
public void setRegion(final String region) {
this.region = region;
}
/**
* Gets address.
*
* @return the address
*/
public Address getAddress() {
return address;
}
/**
* Sets address.
*
* @param address the address
*/
public void setAddress(final Address address) {
this.address = address;
}
@Override
public String toString() {
return "MaxkeyUser{"
+ "userId='" + userId + '\''
+ ", name='" + name + '\''
+ ", displayName='" + displayName + '\''
+ ", department='" + department + '\''
+ ", departmentId='" + departmentId + '\''
+ ", gender='" + gender + '\''
+ ", phoneNumber='" + phoneNumber + '\''
+ ", email='" + email + '\''
+ ", region='" + region + '\''
+ ", address=" + address.toString()
+ '}';
}
public static class Address {
private String country;
@SerializedName("street_address")
private String streetAddress;
private String formatted;
private String locality;
private String region;
@SerializedName("postal_code")
private String postalCode;
/**
* Gets country.
*
* @return the country
*/
public String getCountry() {
return country;
}
/**
* Sets streetAddress.
*
* @param country the streetAddress
*/
public void setCountry(final String country) {
this.country = country;
}
/**
* Gets Street_address.
*
* @return the streetAddress
*/
public String getStreetAddress() {
return streetAddress;
}
/**
* Sets streetAddress.
*
* @param streetAddress the streetAddress
*/
public void setStreetAddress(final String streetAddress) {
this.streetAddress = streetAddress;
}
/**
* Gets formatted.
*
* @return the formatted
*/
public String getFormatted() {
return formatted;
}
/**
* Sets formatted.
*
* @param formatted the formatted
*/
public void setFormatted(final String formatted) {
this.formatted = formatted;
}
/**
* Gets formatted.
*
* @return the formatted
*/
public String getLocality() {
return locality;
}
/**
* Sets locality.
*
* @param locality the locality
*/
public void setLocality(final String locality) {
this.locality = locality;
}
/**
* Gets region.
*
* @return the region
*/
public String getRegion() {
return region;
}
/**
* Sets region.
*
* @param region the region
*/
public void setRegion(final String region) {
this.region = region;
}
/**
* Gets postalCode.
*
* @return the postalCode
*/
public String getPostalCode() {
return postalCode;
}
/**
* Sets postalCode.
*
* @param postalCode the postalCode
*/
public void setPostalCode(final String postalCode) {
this.postalCode = postalCode;
}
@Override
public String toString() {
return "Address{"
+ "country='" + country + '\''
+ ", streetAddress='" + streetAddress + '\''
+ ", formatted='" + formatted + '\''
+ ", locality='" + locality + '\''
+ ", region='" + region + '\''
+ ", postalCode='" + postalCode + '\''
+ '}';
}
}
}

View File

@@ -0,0 +1,178 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.service;
import com.google.gson.annotations.SerializedName;
public class OIDCToken {
@SerializedName("access_token")
private String accessToken;
@SerializedName("token_type")
private String tokenType;
@SerializedName("refresh_token")
private String refreshToken;
@SerializedName("expires_in")
private int expiresIn;
@SerializedName("scope")
private String scope;
@SerializedName("id_token")
private String idToken;
public OIDCToken() {
}
public OIDCToken(final String accessToken,
final String tokenType,
final String refreshToken,
final int expiresIn,
final String scope,
final String idToken) {
this.accessToken = accessToken;
this.tokenType = tokenType;
this.refreshToken = refreshToken;
this.expiresIn = expiresIn;
this.scope = scope;
this.idToken = idToken;
}
/**
* Gets accessToken.
*
* @return the accessToken
*/
public String getAccessToken() {
return accessToken;
}
/**
* Sets accessToken.
*
* @param accessToken the accessToken
*/
public void setAccessToken(final String accessToken) {
this.accessToken = accessToken;
}
/**
* Gets tokenType.
*
* @return the tokenType
*/
public String getTokenType() {
return tokenType;
}
/**
* Sets tokenType.
*
* @param tokenType the tokenType
*/
public void setTokenType(final String tokenType) {
this.tokenType = tokenType;
}
/**
* Gets refreshToken.
*
* @return the refreshToken
*/
public String getRefreshToken() {
return refreshToken;
}
/**
* Sets refreshToken.
*
* @param refreshToken the refreshToken
*/
public void setRefreshToken(final String refreshToken) {
this.refreshToken = refreshToken;
}
/**
* Gets expiresIn.
*
* @return the expiresIn
*/
public int getExpiresIn() {
return expiresIn;
}
/**
* Sets expiresIn.
*
* @param expiresIn the expiresIn
*/
public void setExpiresIn(final int expiresIn) {
this.expiresIn = expiresIn;
}
/**
* Gets scope.
*
* @return the scope
*/
public String getScope() {
return scope;
}
/**
* Sets scope.
*
* @param scope the scope
*/
public void setScope(final String scope) {
this.scope = scope;
}
/**
* Gets idToken.
*
* @return the idToken
*/
public String getIdToken() {
return idToken;
}
/**
* Sets idToken.
*
* @param idToken the idToken
*/
public void setIdToken(final String idToken) {
this.idToken = idToken;
}
@Override
public String toString() {
return "OIDCToken{"
+ "accessToken='" + accessToken + '\''
+ ", tokenType='" + tokenType + '\''
+ ", refreshToken='" + refreshToken + '\''
+ ", expiresIn=" + expiresIn
+ ", scope='" + scope + '\''
+ ", idToken='" + idToken + '\''
+ '}';
}
}

View File

@@ -0,0 +1,175 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.utils.Singleton;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.result.DefaultShenyuResult;
import org.apache.shenyu.plugin.api.result.ShenyuResult;
import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.maxkey.config.MaxkeyConfig;
import org.apache.shenyu.plugin.maxkey.handle.MaxkeyPluginDataHandler;
import org.apache.shenyu.plugin.maxkey.service.MaxkeyService;
import org.apache.shenyu.plugin.maxkey.service.MaxkeyUser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.Mockito;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MaxkeyPluginTest {
@Spy
private MaxKeyPlugin maxkeyPluginTest;
@Spy
private MaxkeyPluginDataHandler maxkeyPluginDataHandlerTest;
private ServerWebExchange exchange;
@Mock
private ShenyuPluginChain chain;
@Mock
private SelectorData selector;
@Mock
private RuleData rule;
@BeforeEach
void setup() {
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
when(context.getBean(ShenyuResult.class)).thenReturn(new DefaultShenyuResult());
SpringBeanUtils springBeanUtils = SpringBeanUtils.getInstance();
springBeanUtils.setApplicationContext(context);
MockitoAnnotations.openMocks(this);
// 模拟请求
exchange = MockServerWebExchange
.from(MockServerHttpRequest
.get("localhost")
.header(HttpHeaders.AUTHORIZATION, "25c8d6a6-ad3a-4767-8bfb-d80641b8dfdc")
.build());
}
@Test
void doExecute() {
final PluginData pluginData = new PluginData(
"pluginId",
"pluginName",
"{\n"
+ "\"clientId\": \"ae20330a-ef0b-4dad-9f10-d5e3485ca2ad\",\n"
+ "\"clientSecret\": \"KQY4MDUwNjIwMjAxNTE3NTM1OTEYty\",\n"
+ "\"authorizationEndpoint\": \"http://192.168.1.16/sign/authz/oauth/v20/authorize\",\n"
+ "\"scope\": \"openid\",\n"
+ "\"responseType\": \"code\",\n"
+ "\"redirectUrl\": \"http://192.168.1.5:9195/http/shenyu/client/hello\",\n"
+ "\"realm\": \"1\",\n"
+ "\"grantType\": \"authorization_code\",\n"
+ "\"tokenEndpoint\": \"http://192.168.1.16/sign/authz/oauth/v20/token\",\n"
+ "\"bearerOnly\": \"true\",\n"
+ "\"introspectionEndpoint\": \"http://192.168.1.16/sign/authz/oauth/v20/introspect\",\n"
+ "\"setUserInfoHeader\": \"true\",\n"
+ "\"userInfoEndpoint\": \"http://192.168.1.16/sign/api/connect/v10/userinfo\",\n"
+ "\"introspectionEndpointAuthMethodsSupported\": \"client_secret_basic\",\n"
+ "\"discovery\": \"http://192.168.1.16/sign/authz/oauth/v20/1/.well-known/openid-configuration\"\n"
+ "}",
"0",
false,
null);
// 测试数据同步
maxkeyPluginDataHandlerTest.handlerPlugin(pluginData);
// 模拟服务
MaxkeyService maxkeyService = mock(MaxkeyService.class);
MaxkeyConfig maxkeyConfig = mock(MaxkeyConfig.class);
when(maxkeyService.getMaxkeyConfig()).thenReturn(maxkeyConfig);
exchange = MockServerWebExchange.from(MockServerHttpRequest
.get("localhost")
.queryParam("state", "state")
.queryParam("code", "code")
.header(HttpHeaders.AUTHORIZATION, "token")
.build());
// 先测试bearerOnly模式和getUserInfo模式
final String token = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
// 处理 MaxkeyUser
MaxkeyUser maxkeyUser = new MaxkeyUser();
maxkeyUser.setAddress(new MaxkeyUser.Address());
Mockito.when(maxkeyService.getMaxkeyUser(token)).thenReturn(maxkeyUser);
// 模拟 Maxkey认证服务执行
Singleton.INST.single(MaxkeyService.class, maxkeyService);
// 认证执行之后 返回一个异步任务
when(this.chain.execute(any())).thenReturn(Mono.empty());
Mono<Void> mono = maxkeyPluginTest.doExecute(exchange, chain, selector, rule);
StepVerifier.create(mono).expectSubscription().verifyComplete();
// 再测试code模式
maxkeyService = Singleton.INST.get(MaxkeyService.class);
maxkeyConfig = maxkeyService.getMaxkeyConfig();
maxkeyConfig.setBearerOnly(false);
exchange = MockServerWebExchange.from(MockServerHttpRequest
.get("localhost")
.queryParam("state", "state")
.queryParam("code", "code")
.build());
Mockito.when(maxkeyService.getOAuthToken("code")).thenReturn(token);
Singleton.INST.single(MaxkeyService.class, maxkeyService);
mono = maxkeyPluginTest.doExecute(exchange, chain, selector, rule);
StepVerifier.create(mono).expectSubscription().verifyComplete();
}
@Test
public void testNamed() {
final String result = maxkeyPluginTest.named();
Assertions.assertEquals(PluginEnum.MAXKEY.getName(), result);
}
@Test
public void testGetOrder() {
final int result = maxkeyPluginTest.getOrder();
Assertions.assertEquals(PluginEnum.MAXKEY.getCode(), result);
}
@Test
public void skipTest() {
Assumptions.assumeFalse(maxkeyPluginTest.skip(exchange));
}
}

View File

@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.config;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
class MaxkeyConfigTest {
@Test
public void maxkeyConfig() {
MaxkeyConfig maxkeyConfig = new MaxkeyConfig("a", "b", "c", "d", "e", "f", "g", "h", "i", false, "j", false, "k", "l", "m");
assertEquals("a", maxkeyConfig.getClientId());
assertEquals("b", maxkeyConfig.getClientSecret());
assertEquals("c", maxkeyConfig.getAuthorizationEndpoint());
assertEquals("d", maxkeyConfig.getScope());
assertEquals("e", maxkeyConfig.getResponseType());
assertEquals("f", maxkeyConfig.getRedirectUrl());
assertEquals("g", maxkeyConfig.getRealm());
assertEquals("h", maxkeyConfig.getGrantType());
assertEquals("i", maxkeyConfig.getTokenEndpoint());
assertFalse(maxkeyConfig.isBearerOnly());
assertEquals("j", maxkeyConfig.getIntrospectionEndpoint());
assertFalse(maxkeyConfig.isSetUserInfoHeader());
assertEquals("k", maxkeyConfig.getUserInfoEndpoint());
assertEquals("l", maxkeyConfig.getIntrospectionEndpointAuthMethodsSupported());
assertEquals("m", maxkeyConfig.getDiscovery());
MaxkeyConfig maxkeyConfig1 = new MaxkeyConfig();
maxkeyConfig1.setClientId("a");
maxkeyConfig1.setClientSecret("b");
maxkeyConfig1.setAuthorizationEndpoint("c");
maxkeyConfig1.setScope("d");
maxkeyConfig1.setResponseType("e");
maxkeyConfig1.setRedirectUrl("f");
maxkeyConfig1.setRealm("g");
maxkeyConfig1.setGrantType("h");
maxkeyConfig1.setTokenEndpoint("i");
maxkeyConfig1.setBearerOnly(false);
maxkeyConfig1.setIntrospectionEndpoint("j");
maxkeyConfig1.setSetUserInfoHeader(false);
maxkeyConfig1.setUserInfoEndpoint("k");
maxkeyConfig1.setIntrospectionEndpointAuthMethodsSupported("l");
maxkeyConfig1.setDiscovery("m");
assertEquals("a", maxkeyConfig.getClientId());
assertEquals("b", maxkeyConfig.getClientSecret());
assertEquals("c", maxkeyConfig.getAuthorizationEndpoint());
assertEquals("d", maxkeyConfig.getScope());
assertEquals("e", maxkeyConfig.getResponseType());
assertEquals("f", maxkeyConfig.getRedirectUrl());
assertEquals("g", maxkeyConfig.getRealm());
assertEquals("h", maxkeyConfig.getGrantType());
assertEquals("i", maxkeyConfig.getTokenEndpoint());
assertFalse(maxkeyConfig.isBearerOnly());
assertEquals("j", maxkeyConfig.getIntrospectionEndpoint());
assertFalse(maxkeyConfig.isSetUserInfoHeader());
assertEquals("k", maxkeyConfig.getUserInfoEndpoint());
assertEquals("l", maxkeyConfig.getIntrospectionEndpointAuthMethodsSupported());
assertEquals("m", maxkeyConfig.getDiscovery());
assertEquals(
"MaxkeyConfig{clientId='a', "
+ "clientSecret='b', "
+ "authorizationEndpoint='c', "
+ "scope='d', "
+ "responseType='e', "
+ "redirectUrl='f', "
+ "realm='g', "
+ "grantType='h', "
+ "tokenEndpoint='i', "
+ "bearerOnly=false, "
+ "introspectionEndpoint='j', "
+ "setUserInfoHeader=false, "
+ "userInfoEndpoint='k', "
+ "introspectionEndpointAuthMethodsSupported='l', "
+ "discovery='m'}",
maxkeyConfig1.toString());
}
}

View File

@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shenyu.plugin.maxkey.handle;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.utils.Singleton;
import org.apache.shenyu.plugin.maxkey.service.MaxkeyService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class MaxkeyPluginDataHandlerTest {
private MaxkeyPluginDataHandler maxkeyPluginDataHandlerTest;
@BeforeEach
public void setup() {
maxkeyPluginDataHandlerTest = new MaxkeyPluginDataHandler();
}
@Test
public void handlerPlugin() {
final PluginData pluginData = new PluginData(
"pluginId",
"pluginName",
"{\n"
+ "\t\"clientId\": \"ae20330a-ef0b-4dad-9f10-d5e3485ca2ad\",\n"
+ "\t\"clientSecret\": \"KQY4MDUwNjIwMjAxNTE3NTM1OTEYty\",\n"
+ "\t\"authorizationEndpoint\": \"http://192.168.1.16/sign/authz/oauth/v20/authorize\",\n"
+ "\t\"scope\": \"openid\",\n"
+ "\t\"responseType\": \"code\",\n"
+ "\t\"redirectUrl\": \"http://192.168.1.5:9195/http/shenyu/client/hello\",\n"
+ "\t\"realm\": \"1\",\n"
+ "\t\"grantType\": \"authorization_code\",\n"
+ "\t\"tokenEndpoint\": \"http://192.168.1.16/sign/authz/oauth/v20/token\",\n"
+ "\t\"bearerOnly\": \"false\",\n"
+ "\t\"introspectionEndpoint\": \"http://192.168.1.16/sign/authz/oauth/v20/introspect\",\n"
+ "\t\"setUserInfoHeader\": \"false\",\n"
+ "\t\"userInfoEndpoint\": \"http://192.168.1.16/sign/api/connect/v10/userinfo\",\n"
+ "\t\"introspectionEndpointAuthMethodsSupported\": \"client_secret_basic\",\n"
+ "\t\"discovery\": \"http://192.168.1.16/sign/authz/oauth/v20/1/.well-known/openid-configuration\"\n"
+ "}\n",
"0",
false,
null);
maxkeyPluginDataHandlerTest.handlerPlugin(pluginData);
MaxkeyService maxkeyService = Singleton.INST.get(MaxkeyService.class);
}
@Test
public void testPluginNamed() {
final String result = maxkeyPluginDataHandlerTest.pluginNamed();
assertEquals(PluginEnum.MAXKEY.getName(), result);
}
}