commit
89760cd7bf
@ -1,8 +1,6 @@
|
|||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
recipients:
|
recipients:
|
||||||
- xchaos8@126.com
|
|
||||||
- nacos_dev@linux.alibaba.com
|
|
||||||
- dev-nacos@googlegroups.com
|
- dev-nacos@googlegroups.com
|
||||||
- mw_configcenter@list.alibaba-inc.com
|
- mw_configcenter@list.alibaba-inc.com
|
||||||
on_success: change
|
on_success: change
|
||||||
@ -30,7 +28,7 @@ before_install:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true
|
- mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true
|
||||||
- mvn -Prelease-nacos clean install -U
|
- mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
|
||||||
- mvn clean package -Pit-test
|
- mvn clean package -Pit-test
|
||||||
after_success:
|
after_success:
|
||||||
- mvn clean package -Pit-test
|
- mvn clean package -Pit-test
|
||||||
|
2
BUILDING
2
BUILDING
@ -35,4 +35,4 @@ Build Instructions for NACOS
|
|||||||
Execute the following command in order to build the tar.gz packages and install JAR into local repository:
|
Execute the following command in order to build the tar.gz packages and install JAR into local repository:
|
||||||
|
|
||||||
#build nacos
|
#build nacos
|
||||||
$ mvn -Prelease-nacos -DskipTests clean install -U
|
$ mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
|
||||||
|
@ -163,7 +163,6 @@ These are only part of the companies using Nacos, for reference only. If you are
|
|||||||
![时代光华](https://img.alicdn.com/tfs/TB1UzuyNhTpK1RjSZR0XXbEwXXa-201-86.jpg)
|
![时代光华](https://img.alicdn.com/tfs/TB1UzuyNhTpK1RjSZR0XXbEwXXa-201-86.jpg)
|
||||||
![康美](https://img.alicdn.com/tfs/TB19RCANgHqK1RjSZFPXXcwapXa-180-180.jpg)
|
![康美](https://img.alicdn.com/tfs/TB19RCANgHqK1RjSZFPXXcwapXa-180-180.jpg)
|
||||||
![环球易购](https://img.alicdn.com/tfs/TB1iCGyNb2pK1RjSZFsXXaNlXXa-143-143.jpg)
|
![环球易购](https://img.alicdn.com/tfs/TB1iCGyNb2pK1RjSZFsXXaNlXXa-143-143.jpg)
|
||||||
![海格管家](https://img.alicdn.com/tfs/TB1FNq4EwHqK1RjSZFgXXa7JXXa-232-232.jpg)
|
|
||||||
![Nepxion](https://avatars0.githubusercontent.com/u/16344119?s=200&v=4)
|
![Nepxion](https://avatars0.githubusercontent.com/u/16344119?s=200&v=4)
|
||||||
![东莞最佳拍档](https://img.alicdn.com/tfs/TB11ugsDzTpK1RjSZKPXXa3UpXa-300-300.png)
|
![东莞最佳拍档](https://img.alicdn.com/tfs/TB11ugsDzTpK1RjSZKPXXa3UpXa-300-300.png)
|
||||||
![chigua](https://img.alicdn.com/tfs/TB1aUe5EpzqK1RjSZSgXXcpAVXa-248-124.png)
|
![chigua](https://img.alicdn.com/tfs/TB1aUe5EpzqK1RjSZSgXXcpAVXa-248-124.png)
|
||||||
|
@ -32,6 +32,10 @@ public class PropertyKeyConst {
|
|||||||
|
|
||||||
public final static String NAMESPACE = "namespace";
|
public final static String NAMESPACE = "namespace";
|
||||||
|
|
||||||
|
public final static String USERNAME = "username";
|
||||||
|
|
||||||
|
public final static String PASSWORD = "password";
|
||||||
|
|
||||||
public final static String ACCESS_KEY = "accessKey";
|
public final static String ACCESS_KEY = "accessKey";
|
||||||
|
|
||||||
public final static String SECRET_KEY = "secretKey";
|
public final static String SECRET_KEY = "secretKey";
|
||||||
|
@ -62,6 +62,8 @@ public class Constants {
|
|||||||
|
|
||||||
public static final String CONFIG_VERSION = "Config-Version";
|
public static final String CONFIG_VERSION = "Config-Version";
|
||||||
|
|
||||||
|
public static final String CONFIG_TYPE = "Config-Type";
|
||||||
|
|
||||||
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
|
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
|
||||||
|
|
||||||
public static final String SPACING_INTERVAL = "client-spacing-interval";
|
public static final String SPACING_INTERVAL = "client-spacing-interval";
|
||||||
@ -70,6 +72,16 @@ public class Constants {
|
|||||||
|
|
||||||
public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs";
|
public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs";
|
||||||
|
|
||||||
|
public static final String TOKEN = "token";
|
||||||
|
|
||||||
|
public static final String ACCESS_TOKEN = "accessToken";
|
||||||
|
|
||||||
|
public static final String TOKEN_TTL = "tokenTtl";
|
||||||
|
|
||||||
|
public static final String GLOBAL_ADMIN = "globalAdmin";
|
||||||
|
|
||||||
|
public static final String TOKEN_REFRESH_WINDOW = "tokenRefreshWindow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* second
|
* second
|
||||||
*/
|
*/
|
||||||
@ -169,4 +181,6 @@ public class Constants {
|
|||||||
|
|
||||||
public static final String SNOWFLAKE_INSTANCE_ID_GENERATOR = "snowflake";
|
public static final String SNOWFLAKE_INSTANCE_ID_GENERATOR = "snowflake";
|
||||||
|
|
||||||
|
public static final String HTTP_PREFIX = "http";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.api.config;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigChangeEvent
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public class ConfigChangeEvent {
|
||||||
|
private Map<String, ConfigChangeItem> data;
|
||||||
|
|
||||||
|
public ConfigChangeEvent(Map<String, ConfigChangeItem> data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigChangeItem getChangeItem(String key) {
|
||||||
|
return data.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ConfigChangeItem> getChangeItems() {
|
||||||
|
return data.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.api.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigChangeItem
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public class ConfigChangeItem {
|
||||||
|
private String key;
|
||||||
|
private String oldValue;
|
||||||
|
private String newValue;
|
||||||
|
|
||||||
|
private PropertyChangeType type;
|
||||||
|
|
||||||
|
public ConfigChangeItem(String key, String oldValue, String newValue) {
|
||||||
|
this.key = key;
|
||||||
|
this.oldValue = oldValue;
|
||||||
|
this.newValue = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOldValue() {
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOldValue(String oldValue) {
|
||||||
|
this.oldValue = oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNewValue() {
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNewValue(String newValue) {
|
||||||
|
this.newValue = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyChangeType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(PropertyChangeType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ConfigChangeItem{" +
|
||||||
|
"key='" + key + '\'' +
|
||||||
|
", oldValue='" + oldValue + '\'' +
|
||||||
|
", newValue='" + newValue + '\'' +
|
||||||
|
", type=" + type +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.api.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property Change Type
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public enum PropertyChangeType {
|
||||||
|
/** add */
|
||||||
|
ADDED,
|
||||||
|
/** modified */
|
||||||
|
MODIFIED,
|
||||||
|
/** deleted */
|
||||||
|
DELETED
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.api.config.listener;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigChangeParser
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public interface ConfigChangeParser {
|
||||||
|
/**
|
||||||
|
* judge type
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean isResponsibleFor(String type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare old and new data
|
||||||
|
* @param oldContent
|
||||||
|
* @param newContent
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) throws IOException;
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
package com.alibaba.nacos.api.naming.utils;
|
package com.alibaba.nacos.api.naming.utils;
|
||||||
|
|
||||||
import com.alibaba.nacos.api.common.Constants;
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nkorange
|
* @author nkorange
|
||||||
@ -32,6 +33,9 @@ public class NamingUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getServiceName(String serviceNameWithGroup) {
|
public static String getServiceName(String serviceNameWithGroup) {
|
||||||
|
if (StringUtils.isBlank(serviceNameWithGroup)) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
|
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
|
||||||
return serviceNameWithGroup;
|
return serviceNameWithGroup;
|
||||||
}
|
}
|
||||||
@ -39,6 +43,9 @@ public class NamingUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getGroupName(String serviceNameWithGroup) {
|
public static String getGroupName(String serviceNameWithGroup) {
|
||||||
|
if (StringUtils.isBlank(serviceNameWithGroup)) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
|
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
|
||||||
return Constants.DEFAULT_GROUP;
|
return Constants.DEFAULT_GROUP;
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,10 @@
|
|||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
package com.alibaba.nacos.client.config;
|
package com.alibaba.nacos.client.config;
|
||||||
|
|
||||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||||
import com.alibaba.nacos.api.SystemPropertyKeyConst;
|
|
||||||
import com.alibaba.nacos.api.common.Constants;
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
import com.alibaba.nacos.api.config.ConfigService;
|
import com.alibaba.nacos.api.config.ConfigService;
|
||||||
import com.alibaba.nacos.api.config.listener.Listener;
|
import com.alibaba.nacos.api.config.listener.Listener;
|
||||||
@ -33,8 +32,7 @@ import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor;
|
|||||||
import com.alibaba.nacos.client.config.utils.ContentUtils;
|
import com.alibaba.nacos.client.config.utils.ContentUtils;
|
||||||
import com.alibaba.nacos.client.config.utils.ParamUtils;
|
import com.alibaba.nacos.client.config.utils.ParamUtils;
|
||||||
import com.alibaba.nacos.client.utils.LogUtils;
|
import com.alibaba.nacos.client.utils.LogUtils;
|
||||||
import com.alibaba.nacos.client.utils.TemplateUtils;
|
import com.alibaba.nacos.client.utils.ParamUtil;
|
||||||
import com.alibaba.nacos.client.utils.TenantUtil;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
@ -44,7 +42,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config Impl
|
* Config Impl
|
||||||
@ -86,34 +83,7 @@ public class NacosConfigService implements ConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initNamespace(Properties properties) {
|
private void initNamespace(Properties properties) {
|
||||||
String namespaceTmp = null;
|
namespace = ParamUtil.parseNamespace(properties);
|
||||||
|
|
||||||
String isUseCloudNamespaceParsing =
|
|
||||||
properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING,
|
|
||||||
System.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING,
|
|
||||||
String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING)));
|
|
||||||
|
|
||||||
if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) {
|
|
||||||
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
|
||||||
@Override
|
|
||||||
public String call() {
|
|
||||||
return TenantUtil.getUserTenantForAcm();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
|
||||||
@Override
|
|
||||||
public String call() {
|
|
||||||
String namespace = System.getenv(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE);
|
|
||||||
return StringUtils.isNotBlank(namespace) ? namespace : EMPTY;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(namespaceTmp)) {
|
|
||||||
namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE);
|
|
||||||
}
|
|
||||||
namespace = StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : EMPTY;
|
|
||||||
properties.put(PropertyKeyConst.NAMESPACE, namespace);
|
properties.put(PropertyKeyConst.NAMESPACE, namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,9 +140,8 @@ public class NacosConfigService implements ConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
content = worker.getServerConfig(dataId, group, tenant, timeoutMs);
|
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
|
||||||
|
cr.setContent(ct[0]);
|
||||||
cr.setContent(content);
|
|
||||||
|
|
||||||
configFilterChainManager.doFilter(null, cr);
|
configFilterChainManager.doFilter(null, cr);
|
||||||
content = cr.getContent();
|
content = cr.getContent();
|
||||||
|
@ -23,6 +23,7 @@ import com.alibaba.nacos.client.config.impl.HttpSimpleClient.HttpResult;
|
|||||||
import com.alibaba.nacos.client.config.impl.ServerListManager;
|
import com.alibaba.nacos.client.config.impl.ServerListManager;
|
||||||
import com.alibaba.nacos.client.config.impl.SpasAdapter;
|
import com.alibaba.nacos.client.config.impl.SpasAdapter;
|
||||||
import com.alibaba.nacos.client.identify.STSConfig;
|
import com.alibaba.nacos.client.identify.STSConfig;
|
||||||
|
import com.alibaba.nacos.client.security.SecurityProxy;
|
||||||
import com.alibaba.nacos.client.utils.JSONUtils;
|
import com.alibaba.nacos.client.utils.JSONUtils;
|
||||||
import com.alibaba.nacos.client.utils.LogUtils;
|
import com.alibaba.nacos.client.utils.LogUtils;
|
||||||
import com.alibaba.nacos.client.utils.ParamUtil;
|
import com.alibaba.nacos.client.utils.ParamUtil;
|
||||||
@ -43,7 +44,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server Agent
|
* Server Agent
|
||||||
@ -54,6 +55,12 @@ public class ServerHttpAgent implements HttpAgent {
|
|||||||
|
|
||||||
private static final Logger LOGGER = LogUtils.logger(ServerHttpAgent.class);
|
private static final Logger LOGGER = LogUtils.logger(ServerHttpAgent.class);
|
||||||
|
|
||||||
|
private SecurityProxy securityProxy;
|
||||||
|
|
||||||
|
private String namespaceId;
|
||||||
|
|
||||||
|
private long securityInfoRefreshIntervalMills = TimeUnit.SECONDS.toMillis(5);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param path 相对于web应用根,以/开头
|
* @param path 相对于web应用根,以/开头
|
||||||
* @param headers
|
* @param headers
|
||||||
@ -68,7 +75,7 @@ public class ServerHttpAgent implements HttpAgent {
|
|||||||
long readTimeoutMs) throws IOException {
|
long readTimeoutMs) throws IOException {
|
||||||
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
||||||
final boolean isSSL = false;
|
final boolean isSSL = false;
|
||||||
|
injectSecurityInfo(paramValues);
|
||||||
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
||||||
int maxRetry = this.maxRetry;
|
int maxRetry = this.maxRetry;
|
||||||
|
|
||||||
@ -121,7 +128,7 @@ public class ServerHttpAgent implements HttpAgent {
|
|||||||
long readTimeoutMs) throws IOException {
|
long readTimeoutMs) throws IOException {
|
||||||
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
||||||
boolean isSSL = false;
|
boolean isSSL = false;
|
||||||
|
injectSecurityInfo(paramValues);
|
||||||
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
||||||
int maxRetry = this.maxRetry;
|
int maxRetry = this.maxRetry;
|
||||||
|
|
||||||
@ -176,7 +183,7 @@ public class ServerHttpAgent implements HttpAgent {
|
|||||||
long readTimeoutMs) throws IOException {
|
long readTimeoutMs) throws IOException {
|
||||||
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
||||||
boolean isSSL = false;
|
boolean isSSL = false;
|
||||||
|
injectSecurityInfo(paramValues);
|
||||||
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
||||||
int maxRetry = this.maxRetry;
|
int maxRetry = this.maxRetry;
|
||||||
|
|
||||||
@ -243,7 +250,39 @@ public class ServerHttpAgent implements HttpAgent {
|
|||||||
|
|
||||||
public ServerHttpAgent(Properties properties) throws NacosException {
|
public ServerHttpAgent(Properties properties) throws NacosException {
|
||||||
serverListMgr = new ServerListManager(properties);
|
serverListMgr = new ServerListManager(properties);
|
||||||
|
securityProxy = new SecurityProxy(properties);
|
||||||
|
namespaceId = properties.getProperty(PropertyKeyConst.NAMESPACE);
|
||||||
init(properties);
|
init(properties);
|
||||||
|
securityProxy.login(serverListMgr.getServerUrls());
|
||||||
|
|
||||||
|
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
|
||||||
|
@Override
|
||||||
|
public Thread newThread(Runnable r) {
|
||||||
|
Thread t = new Thread(r);
|
||||||
|
t.setName("com.alibaba.nacos.client.config.security.updater");
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executorService.scheduleWithFixedDelay(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
securityProxy.login(serverListMgr.getServerUrls());
|
||||||
|
}
|
||||||
|
}, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void injectSecurityInfo(List<String> params) {
|
||||||
|
ArrayList<String> list = (ArrayList) params;
|
||||||
|
if (StringUtils.isNotBlank(securityProxy.getAccessToken())) {
|
||||||
|
list.add(Constants.ACCESS_TOKEN);
|
||||||
|
list.add(securityProxy.getAccessToken());
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(namespaceId)) {
|
||||||
|
list.add("tenant");
|
||||||
|
list.add(namespaceId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(Properties properties) {
|
private void init(Properties properties) {
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.impl;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
import com.alibaba.nacos.api.config.PropertyChangeType;
|
||||||
|
import com.alibaba.nacos.api.config.listener.ConfigChangeParser;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbstractConfigChangeParser
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public abstract class AbstractConfigChangeParser implements ConfigChangeParser {
|
||||||
|
private String configType;
|
||||||
|
|
||||||
|
public AbstractConfigChangeParser(String configType) {
|
||||||
|
this.configType = configType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isResponsibleFor(String type) {
|
||||||
|
return this.configType.equalsIgnoreCase(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<String, ConfigChangeItem> filterChangeData(Map oldMap, Map newMap) {
|
||||||
|
Map<String, ConfigChangeItem> result = new HashMap<String, ConfigChangeItem>(16);
|
||||||
|
for (Iterator<Map.Entry<String, Object>> entryItr = oldMap.entrySet().iterator(); entryItr.hasNext();) {
|
||||||
|
Map.Entry<String, Object> e = entryItr.next();
|
||||||
|
ConfigChangeItem cci = null;
|
||||||
|
if (newMap.containsKey(e.getKey())) {
|
||||||
|
if (e.getValue().equals(newMap.get(e.getKey()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cci = new ConfigChangeItem(e.getKey(), e.getValue().toString(), newMap.get(e.getKey()).toString());
|
||||||
|
cci.setType(PropertyChangeType.MODIFIED);
|
||||||
|
} else {
|
||||||
|
cci = new ConfigChangeItem(e.getKey(), e.getValue().toString(), null);
|
||||||
|
cci.setType(PropertyChangeType.DELETED);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.put(e.getKey(), cci);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Iterator<Map.Entry<String, Object>> entryItr = newMap.entrySet().iterator(); entryItr.hasNext();) {
|
||||||
|
Map.Entry<String, Object> e = entryItr.next();
|
||||||
|
if (!oldMap.containsKey(e.getKey())) {
|
||||||
|
ConfigChangeItem cci = new ConfigChangeItem(e.getKey(), null, e.getValue().toString());
|
||||||
|
cci.setType(PropertyChangeType.ADDED);
|
||||||
|
result.put(e.getKey(), cci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,11 +16,13 @@
|
|||||||
package com.alibaba.nacos.client.config.impl;
|
package com.alibaba.nacos.client.config.impl;
|
||||||
|
|
||||||
import com.alibaba.nacos.api.common.Constants;
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeEvent;
|
||||||
import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
|
import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
|
||||||
import com.alibaba.nacos.api.config.listener.Listener;
|
import com.alibaba.nacos.api.config.listener.Listener;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
|
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
|
||||||
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
|
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
|
||||||
|
import com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener;
|
||||||
import com.alibaba.nacos.client.config.utils.MD5;
|
import com.alibaba.nacos.client.config.utils.MD5;
|
||||||
import com.alibaba.nacos.client.utils.LogUtils;
|
import com.alibaba.nacos.client.utils.LogUtils;
|
||||||
import com.alibaba.nacos.client.utils.TenantUtil;
|
import com.alibaba.nacos.client.utils.TenantUtil;
|
||||||
@ -28,6 +30,7 @@ import org.slf4j.Logger;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,9 +62,17 @@ public class CacheData {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContent(String newContent) {
|
public void setContent(String content) {
|
||||||
this.content = newContent;
|
this.content = content;
|
||||||
this.md5 = getMd5String(content);
|
this.md5 = getMd5String(this.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +85,9 @@ public class CacheData {
|
|||||||
if (null == listener) {
|
if (null == listener) {
|
||||||
throw new IllegalArgumentException("listener is null");
|
throw new IllegalArgumentException("listener is null");
|
||||||
}
|
}
|
||||||
ManagerListenerWrap wrap = new ManagerListenerWrap(listener, md5);
|
ManagerListenerWrap wrap = (listener instanceof AbstractConfigChangeListener) ?
|
||||||
|
new ManagerListenerWrap(listener, md5, content) : new ManagerListenerWrap(listener, md5);
|
||||||
|
|
||||||
if (listeners.addIfAbsent(wrap)) {
|
if (listeners.addIfAbsent(wrap)) {
|
||||||
LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", name, tenant, dataId, group,
|
LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", name, tenant, dataId, group,
|
||||||
listeners.size());
|
listeners.size());
|
||||||
@ -158,12 +171,12 @@ public class CacheData {
|
|||||||
void checkListenerMd5() {
|
void checkListenerMd5() {
|
||||||
for (ManagerListenerWrap wrap : listeners) {
|
for (ManagerListenerWrap wrap : listeners) {
|
||||||
if (!md5.equals(wrap.lastCallMd5)) {
|
if (!md5.equals(wrap.lastCallMd5)) {
|
||||||
safeNotifyListener(dataId, group, content, md5, wrap);
|
safeNotifyListener(dataId, group, content, type, md5, wrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void safeNotifyListener(final String dataId, final String group, final String content,
|
private void safeNotifyListener(final String dataId, final String group, final String content, final String type,
|
||||||
final String md5, final ManagerListenerWrap listenerWrap) {
|
final String md5, final ManagerListenerWrap listenerWrap) {
|
||||||
final Listener listener = listenerWrap.listener;
|
final Listener listener = listenerWrap.listener;
|
||||||
|
|
||||||
@ -188,6 +201,15 @@ public class CacheData {
|
|||||||
configFilterChainManager.doFilter(null, cr);
|
configFilterChainManager.doFilter(null, cr);
|
||||||
String contentTmp = cr.getContent();
|
String contentTmp = cr.getContent();
|
||||||
listener.receiveConfigInfo(contentTmp);
|
listener.receiveConfigInfo(contentTmp);
|
||||||
|
|
||||||
|
// compare lastContent and content
|
||||||
|
if (listener instanceof AbstractConfigChangeListener) {
|
||||||
|
Map data = ConfigChangeHandler.getInstance().parseChangeData(listenerWrap.lastContent, content, type);
|
||||||
|
ConfigChangeEvent event = new ConfigChangeEvent(data);
|
||||||
|
((AbstractConfigChangeListener)listener).receiveConfigChange(event);
|
||||||
|
listenerWrap.lastContent = content;
|
||||||
|
}
|
||||||
|
|
||||||
listenerWrap.lastCallMd5 = md5;
|
listenerWrap.lastCallMd5 = md5;
|
||||||
LOGGER.info("[{}] [notify-ok] dataId={}, group={}, md5={}, listener={} ", name, dataId, group, md5,
|
LOGGER.info("[{}] [notify-ok] dataId={}, group={}, md5={}, listener={} ", name, dataId, group, md5,
|
||||||
listener);
|
listener);
|
||||||
@ -282,11 +304,13 @@ public class CacheData {
|
|||||||
private volatile String content;
|
private volatile String content;
|
||||||
private int taskId;
|
private int taskId;
|
||||||
private volatile boolean isInitializing = true;
|
private volatile boolean isInitializing = true;
|
||||||
|
private String type;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ManagerListenerWrap {
|
class ManagerListenerWrap {
|
||||||
final Listener listener;
|
final Listener listener;
|
||||||
String lastCallMd5 = CacheData.getMd5String(null);
|
String lastCallMd5 = CacheData.getMd5String(null);
|
||||||
|
String lastContent = null;
|
||||||
|
|
||||||
ManagerListenerWrap(Listener listener) {
|
ManagerListenerWrap(Listener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
@ -297,6 +321,12 @@ class ManagerListenerWrap {
|
|||||||
this.lastCallMd5 = md5;
|
this.lastCallMd5 = md5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ManagerListenerWrap(Listener listener, String md5, String lastContent) {
|
||||||
|
this.listener = listener;
|
||||||
|
this.lastCallMd5 = md5;
|
||||||
|
this.lastContent = lastContent;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (null == obj || obj.getClass() != getClass()) {
|
if (null == obj || obj.getClass() != getClass()) {
|
||||||
|
@ -17,6 +17,7 @@ package com.alibaba.nacos.client.config.impl;
|
|||||||
|
|
||||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||||
import com.alibaba.nacos.api.common.Constants;
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
|
import com.alibaba.nacos.api.config.ConfigType;
|
||||||
import com.alibaba.nacos.api.config.listener.Listener;
|
import com.alibaba.nacos.api.config.listener.Listener;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
import com.alibaba.nacos.client.config.common.GroupKey;
|
import com.alibaba.nacos.client.config.common.GroupKey;
|
||||||
@ -46,6 +47,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
import static com.alibaba.nacos.api.common.Constants.LINE_SEPARATOR;
|
import static com.alibaba.nacos.api.common.Constants.LINE_SEPARATOR;
|
||||||
import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR;
|
import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR;
|
||||||
|
import static com.alibaba.nacos.api.common.Constants.CONFIG_TYPE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Longpolling
|
* Longpolling
|
||||||
@ -183,8 +185,8 @@ public class ClientWorker {
|
|||||||
cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant);
|
cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant);
|
||||||
// fix issue # 1317
|
// fix issue # 1317
|
||||||
if (enableRemoteSyncConfig) {
|
if (enableRemoteSyncConfig) {
|
||||||
String content = getServerConfig(dataId, group, tenant, 3000L);
|
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
|
||||||
cache.setContent(content);
|
cache.setContent(ct[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,8 +212,9 @@ public class ClientWorker {
|
|||||||
return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
|
return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getServerConfig(String dataId, String group, String tenant, long readTimeout)
|
public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
|
||||||
throws NacosException {
|
throws NacosException {
|
||||||
|
String[] ct = new String[2];
|
||||||
if (StringUtils.isBlank(group)) {
|
if (StringUtils.isBlank(group)) {
|
||||||
group = Constants.DEFAULT_GROUP;
|
group = Constants.DEFAULT_GROUP;
|
||||||
}
|
}
|
||||||
@ -220,9 +223,9 @@ public class ClientWorker {
|
|||||||
try {
|
try {
|
||||||
List<String> params = null;
|
List<String> params = null;
|
||||||
if (StringUtils.isBlank(tenant)) {
|
if (StringUtils.isBlank(tenant)) {
|
||||||
params = Arrays.asList("dataId", dataId, "group", group);
|
params = new ArrayList<String>(Arrays.asList("dataId", dataId, "group", group));
|
||||||
} else {
|
} else {
|
||||||
params = Arrays.asList("dataId", dataId, "group", group, "tenant", tenant);
|
params = new ArrayList<String>(Arrays.asList("dataId", dataId, "group", group, "tenant", tenant));
|
||||||
}
|
}
|
||||||
result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
|
result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -236,10 +239,16 @@ public class ClientWorker {
|
|||||||
switch (result.code) {
|
switch (result.code) {
|
||||||
case HttpURLConnection.HTTP_OK:
|
case HttpURLConnection.HTTP_OK:
|
||||||
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content);
|
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content);
|
||||||
return result.content;
|
ct[0] = result.content;
|
||||||
|
if (result.headers.containsKey(CONFIG_TYPE)) {
|
||||||
|
ct[1] = result.headers.get(CONFIG_TYPE).get(0);
|
||||||
|
} else {
|
||||||
|
ct[1] = ConfigType.TEXT.getType();
|
||||||
|
}
|
||||||
|
return ct;
|
||||||
case HttpURLConnection.HTTP_NOT_FOUND:
|
case HttpURLConnection.HTTP_NOT_FOUND:
|
||||||
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
|
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
|
||||||
return null;
|
return ct;
|
||||||
case HttpURLConnection.HTTP_CONFLICT: {
|
case HttpURLConnection.HTTP_CONFLICT: {
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
|
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
|
||||||
@ -517,12 +526,15 @@ public class ClientWorker {
|
|||||||
tenant = key[2];
|
tenant = key[2];
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String content = getServerConfig(dataId, group, tenant, 3000L);
|
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
|
||||||
CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
|
CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
|
||||||
cache.setContent(content);
|
cache.setContent(ct[0]);
|
||||||
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}",
|
if (null != ct[1]) {
|
||||||
|
cache.setType(ct[1]);
|
||||||
|
}
|
||||||
|
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}, type={}",
|
||||||
agent.getName(), dataId, group, tenant, cache.getMd5(),
|
agent.getName(), dataId, group, tenant, cache.getMd5(),
|
||||||
ContentUtils.truncateContent(content));
|
ContentUtils.truncateContent(ct[0]), ct[1]);
|
||||||
} catch (NacosException ioe) {
|
} catch (NacosException ioe) {
|
||||||
String message = String.format(
|
String message = String.format(
|
||||||
"[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",
|
"[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.impl;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.listener.ConfigChangeParser;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigChangeHandler
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public class ConfigChangeHandler {
|
||||||
|
private static class ConfigChangeHandlerHolder {
|
||||||
|
private final static ConfigChangeHandler INSTANCE = new ConfigChangeHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigChangeHandler() {
|
||||||
|
this.parserList = new LinkedList<ConfigChangeParser>();
|
||||||
|
|
||||||
|
ServiceLoader<ConfigChangeParser> loader = ServiceLoader.load(ConfigChangeParser.class);
|
||||||
|
Iterator<ConfigChangeParser> itr = loader.iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
this.parserList.add(itr.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parserList.add(new PropertiesChangeParser());
|
||||||
|
this.parserList.add(new YmlChangeParser());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigChangeHandler getInstance() {
|
||||||
|
return ConfigChangeHandlerHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map parseChangeData(String oldContent, String newContent, String type) throws IOException {
|
||||||
|
for (ConfigChangeParser changeParser: this.parserList) {
|
||||||
|
if (changeParser.isResponsibleFor(type)) {
|
||||||
|
return changeParser.doParse(oldContent, newContent, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ConfigChangeParser> parserList;
|
||||||
|
|
||||||
|
}
|
@ -29,10 +29,7 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Http tool
|
* Http tool
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.impl;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
import com.alibaba.nacos.client.utils.StringUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertiesChangeParser
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public class PropertiesChangeParser extends AbstractConfigChangeParser {
|
||||||
|
public PropertiesChangeParser() {
|
||||||
|
super("properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) throws IOException {
|
||||||
|
Properties oldProps = new Properties();
|
||||||
|
Properties newProps = new Properties();
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(oldContent)) {
|
||||||
|
oldProps.load(new StringReader(oldContent));
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(newContent)) {
|
||||||
|
newProps.load(new StringReader(newContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterChangeData(oldProps, newProps);
|
||||||
|
}
|
||||||
|
}
|
@ -234,6 +234,10 @@ public class ServerListManager {
|
|||||||
isStarted = true;
|
isStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getServerUrls() {
|
||||||
|
return serverUrls;
|
||||||
|
}
|
||||||
|
|
||||||
Iterator<String> iterator() {
|
Iterator<String> iterator() {
|
||||||
if (serverUrls.isEmpty()) {
|
if (serverUrls.isEmpty()) {
|
||||||
LOGGER.error("[{}] [iterator-serverlist] No server address defined!", name);
|
LOGGER.error("[{}] [iterator-serverlist] No server address defined!", name);
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.impl;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
import com.alibaba.nacos.client.utils.StringUtils;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YmlChangeParser
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public class YmlChangeParser extends AbstractConfigChangeParser {
|
||||||
|
public YmlChangeParser() {
|
||||||
|
super("yaml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) {
|
||||||
|
Map<String, Object> oldMap = Collections.emptyMap();
|
||||||
|
Map<String, Object> newMap = Collections.emptyMap();
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(oldContent)) {
|
||||||
|
oldMap = (new Yaml()).load(oldContent);
|
||||||
|
oldMap = getFlattenedMap(oldMap);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(newContent)) {
|
||||||
|
newMap = (new Yaml()).load(newContent);
|
||||||
|
newMap = getFlattenedMap(newMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterChangeData(oldMap, newMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<String, Object>(128);
|
||||||
|
buildFlattenedMap(result, source, null);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, String path) {
|
||||||
|
for (Iterator<Map.Entry<String, Object>> itr = source.entrySet().iterator(); itr.hasNext(); ) {
|
||||||
|
Map.Entry<String, Object> e = itr.next();
|
||||||
|
String key = e.getKey();
|
||||||
|
if (StringUtils.isNotBlank(path)) {
|
||||||
|
if (e.getKey().startsWith("[")) {
|
||||||
|
key = path + key;
|
||||||
|
} else {
|
||||||
|
key = path + '.' + key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.getValue() instanceof String) {
|
||||||
|
result.put(key, e.getValue());
|
||||||
|
} else if (e.getValue() instanceof Map) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> map = (Map<String, Object>) e.getValue();
|
||||||
|
buildFlattenedMap(result, map, key);
|
||||||
|
} else if (e.getValue() instanceof Collection) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Collection<Object> collection = (Collection<Object>) e.getValue();
|
||||||
|
if (collection.isEmpty()) {
|
||||||
|
result.put(key, "");
|
||||||
|
} else {
|
||||||
|
int count = 0;
|
||||||
|
for (Object object : collection) {
|
||||||
|
buildFlattenedMap(result, Collections.singletonMap("[" + (count++) + "]", object), key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.put(key, (e.getValue() != null ? e.getValue() : ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.listener.impl;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.listener.AbstractListener;
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbstractConfigChangeListener
|
||||||
|
*
|
||||||
|
* @author rushsky518
|
||||||
|
*/
|
||||||
|
public abstract class AbstractConfigChangeListener extends AbstractListener {
|
||||||
|
/**
|
||||||
|
* handle config change
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
public abstract void receiveConfigChange(final ConfigChangeEvent event);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receiveConfigInfo(final String configInfo) {}
|
||||||
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ public final class CredentialService implements SpasCredentialLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CredentialService getInstance(String appName) {
|
public static CredentialService getInstance(String appName) {
|
||||||
String key = appName != null ? appName : Constants.NO_APP_NAME;
|
String key = appName != null ? appName : IdentifyConstants.NO_APP_NAME;
|
||||||
CredentialService instance = instances.get(key);
|
CredentialService instance = instances.get(key);
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new CredentialService(appName);
|
instance = new CredentialService(appName);
|
||||||
@ -70,7 +70,7 @@ public final class CredentialService implements SpasCredentialLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CredentialService freeInstance(String appName) {
|
public static CredentialService freeInstance(String appName) {
|
||||||
String key = appName != null ? appName : Constants.NO_APP_NAME;
|
String key = appName != null ? appName : IdentifyConstants.NO_APP_NAME;
|
||||||
CredentialService instance = instances.remove(key);
|
CredentialService instance = instances.remove(key);
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
instance.free();
|
instance.free();
|
||||||
|
@ -94,7 +94,7 @@ public class CredentialWatcher {
|
|||||||
private void loadCredential(boolean init) {
|
private void loadCredential(boolean init) {
|
||||||
boolean logWarn = init;
|
boolean logWarn = init;
|
||||||
if (propertyPath == null) {
|
if (propertyPath == null) {
|
||||||
URL url = ClassLoader.getSystemResource(Constants.PROPERTIES_FILENAME);
|
URL url = ClassLoader.getSystemResource(IdentifyConstants.PROPERTIES_FILENAME);
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
propertyPath = url.getPath();
|
propertyPath = url.getPath();
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ public class CredentialWatcher {
|
|||||||
propertyPath = value;
|
propertyPath = value;
|
||||||
}
|
}
|
||||||
if (propertyPath == null || propertyPath.isEmpty()) {
|
if (propertyPath == null || propertyPath.isEmpty()) {
|
||||||
propertyPath = Constants.CREDENTIAL_PATH + (appName == null ? Constants.CREDENTIAL_DEFAULT
|
propertyPath = IdentifyConstants.CREDENTIAL_PATH + (appName == null ? IdentifyConstants.CREDENTIAL_DEFAULT
|
||||||
: appName);
|
: appName);
|
||||||
} else {
|
} else {
|
||||||
if (logWarn) {
|
if (logWarn) {
|
||||||
@ -115,7 +115,7 @@ public class CredentialWatcher {
|
|||||||
} else {
|
} else {
|
||||||
if (logWarn) {
|
if (logWarn) {
|
||||||
SpasLogger.info("[{}] Load credential file from classpath: {}", appName,
|
SpasLogger.info("[{}] Load credential file from classpath: {}", appName,
|
||||||
Constants.PROPERTIES_FILENAME);
|
IdentifyConstants.PROPERTIES_FILENAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,13 +125,13 @@ public class CredentialWatcher {
|
|||||||
try {
|
try {
|
||||||
propertiesIS = new FileInputStream(propertyPath);
|
propertiesIS = new FileInputStream(propertyPath);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
if (appName != null && !appName.equals(Constants.CREDENTIAL_DEFAULT) && propertyPath.equals(
|
if (appName != null && !appName.equals(IdentifyConstants.CREDENTIAL_DEFAULT) && propertyPath.equals(
|
||||||
Constants.CREDENTIAL_PATH + appName)) {
|
IdentifyConstants.CREDENTIAL_PATH + appName)) {
|
||||||
propertyPath = Constants.CREDENTIAL_PATH + Constants.CREDENTIAL_DEFAULT;
|
propertyPath = IdentifyConstants.CREDENTIAL_PATH + IdentifyConstants.CREDENTIAL_DEFAULT;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!Constants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
if (!IdentifyConstants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
||||||
propertyPath = Constants.DOCKER_CREDENTIAL_PATH;
|
propertyPath = IdentifyConstants.DOCKER_CREDENTIAL_PATH;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,8 +143,8 @@ public class CredentialWatcher {
|
|||||||
String tenantId = null;
|
String tenantId = null;
|
||||||
if (propertiesIS == null) {
|
if (propertiesIS == null) {
|
||||||
propertyPath = null;
|
propertyPath = null;
|
||||||
accessKey = System.getenv(Constants.ENV_ACCESS_KEY);
|
accessKey = System.getenv(IdentifyConstants.ENV_ACCESS_KEY);
|
||||||
secretKey = System.getenv(Constants.ENV_SECRET_KEY);
|
secretKey = System.getenv(IdentifyConstants.ENV_SECRET_KEY);
|
||||||
if (accessKey == null && secretKey == null) {
|
if (accessKey == null && secretKey == null) {
|
||||||
if (logWarn) {
|
if (logWarn) {
|
||||||
SpasLogger.info("{} No credential found", appName);
|
SpasLogger.info("{} No credential found", appName);
|
||||||
@ -173,26 +173,26 @@ public class CredentialWatcher {
|
|||||||
SpasLogger.info("[{}] Load credential file {}", appName, propertyPath);
|
SpasLogger.info("[{}] Load credential file {}", appName, propertyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Constants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
if (!IdentifyConstants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
||||||
if (properties.containsKey(Constants.ACCESS_KEY)) {
|
if (properties.containsKey(IdentifyConstants.ACCESS_KEY)) {
|
||||||
accessKey = properties.getProperty(Constants.ACCESS_KEY);
|
accessKey = properties.getProperty(IdentifyConstants.ACCESS_KEY);
|
||||||
}
|
}
|
||||||
if (properties.containsKey(Constants.SECRET_KEY)) {
|
if (properties.containsKey(IdentifyConstants.SECRET_KEY)) {
|
||||||
secretKey = properties.getProperty(Constants.SECRET_KEY);
|
secretKey = properties.getProperty(IdentifyConstants.SECRET_KEY);
|
||||||
}
|
}
|
||||||
if (properties.containsKey(Constants.TENANT_ID)) {
|
if (properties.containsKey(IdentifyConstants.TENANT_ID)) {
|
||||||
tenantId = properties.getProperty(Constants.TENANT_ID);
|
tenantId = properties.getProperty(IdentifyConstants.TENANT_ID);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (properties.containsKey(Constants.DOCKER_ACCESS_KEY)) {
|
if (properties.containsKey(IdentifyConstants.DOCKER_ACCESS_KEY)) {
|
||||||
accessKey = properties.getProperty(Constants.DOCKER_ACCESS_KEY);
|
accessKey = properties.getProperty(IdentifyConstants.DOCKER_ACCESS_KEY);
|
||||||
}
|
}
|
||||||
if (properties.containsKey(Constants.DOCKER_SECRET_KEY)) {
|
if (properties.containsKey(IdentifyConstants.DOCKER_SECRET_KEY)) {
|
||||||
secretKey = properties.getProperty(Constants.DOCKER_SECRET_KEY);
|
secretKey = properties.getProperty(IdentifyConstants.DOCKER_SECRET_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.containsKey(Constants.DOCKER_TENANT_ID)) {
|
if (properties.containsKey(IdentifyConstants.DOCKER_TENANT_ID)) {
|
||||||
tenantId = properties.getProperty(Constants.DOCKER_TENANT_ID);
|
tenantId = properties.getProperty(IdentifyConstants.DOCKER_TENANT_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,7 +211,7 @@ public class CredentialWatcher {
|
|||||||
Credentials credential = new Credentials(accessKey, secretKey, tenantId);
|
Credentials credential = new Credentials(accessKey, secretKey, tenantId);
|
||||||
if (!credential.valid()) {
|
if (!credential.valid()) {
|
||||||
SpasLogger.warn("[1] Credential file missing required property {} Credential file missing {} or {}",
|
SpasLogger.warn("[1] Credential file missing required property {} Credential file missing {} or {}",
|
||||||
appName, Constants.ACCESS_KEY, Constants.SECRET_KEY);
|
appName, IdentifyConstants.ACCESS_KEY, IdentifyConstants.SECRET_KEY);
|
||||||
propertyPath = null;
|
propertyPath = null;
|
||||||
// return;
|
// return;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ package com.alibaba.nacos.client.identify;
|
|||||||
*
|
*
|
||||||
* @author Nacos
|
* @author Nacos
|
||||||
*/
|
*/
|
||||||
public class Constants {
|
public class IdentifyConstants {
|
||||||
public static final String ACCESS_KEY = "accessKey";
|
public static final String ACCESS_KEY = "accessKey";
|
||||||
|
|
||||||
public static final String SECRET_KEY = "secretKey";
|
public static final String SECRET_KEY = "secretKey";
|
@ -63,9 +63,7 @@ public class NacosNamingMaintainService implements NamingMaintainService {
|
|||||||
namespace = InitUtils.initNamespaceForNaming(properties);
|
namespace = InitUtils.initNamespaceForNaming(properties);
|
||||||
initServerAddr(properties);
|
initServerAddr(properties);
|
||||||
InitUtils.initWebRootContext();
|
InitUtils.initWebRootContext();
|
||||||
|
serverProxy = new NamingProxy(namespace, endpoint, serverList, properties);
|
||||||
serverProxy = new NamingProxy(namespace, endpoint, serverList);
|
|
||||||
serverProxy.setProperties(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initServerAddr(Properties properties) {
|
private void initServerAddr(Properties properties) {
|
||||||
|
@ -34,6 +34,7 @@ import com.alibaba.nacos.client.naming.net.NamingProxy;
|
|||||||
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
|
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
|
||||||
import com.alibaba.nacos.client.naming.utils.InitUtils;
|
import com.alibaba.nacos.client.naming.utils.InitUtils;
|
||||||
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
|
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
|
||||||
|
import com.alibaba.nacos.client.security.SecurityProxy;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
@ -45,15 +46,15 @@ import java.util.Properties;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Nacos Naming Service
|
||||||
|
*
|
||||||
* @author nkorange
|
* @author nkorange
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
|
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
|
||||||
public class NacosNamingService implements NamingService {
|
public class NacosNamingService implements NamingService {
|
||||||
private static final String DEFAULT_PORT = "8080";
|
|
||||||
private static final long DEFAULT_HEART_BEAT_INTERVAL = TimeUnit.SECONDS.toMillis(5);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Each Naming instance should have different namespace.
|
* Each Naming service should have different namespace.
|
||||||
*/
|
*/
|
||||||
private String namespace;
|
private String namespace;
|
||||||
|
|
||||||
@ -92,8 +93,7 @@ public class NacosNamingService implements NamingService {
|
|||||||
initLogName(properties);
|
initLogName(properties);
|
||||||
|
|
||||||
eventDispatcher = new EventDispatcher();
|
eventDispatcher = new EventDispatcher();
|
||||||
serverProxy = new NamingProxy(namespace, endpoint, serverList);
|
serverProxy = new NamingProxy(namespace, endpoint, serverList, properties);
|
||||||
serverProxy.setProperties(properties);
|
|
||||||
beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
|
beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
|
||||||
hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties),
|
hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties),
|
||||||
initPollingThreadCount(properties));
|
initPollingThreadCount(properties));
|
||||||
|
@ -45,9 +45,6 @@ public class HttpClient {
|
|||||||
private static final boolean ENABLE_HTTPS = Boolean
|
private static final boolean ENABLE_HTTPS = Boolean
|
||||||
.getBoolean("com.alibaba.nacos.client.naming.tls.enable");
|
.getBoolean("com.alibaba.nacos.client.naming.tls.enable");
|
||||||
|
|
||||||
private static final String POST = "POST";
|
|
||||||
private static final String PUT = "PUT";
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// limit max redirection
|
// limit max redirection
|
||||||
System.setProperty("http.maxRedirects", "5");
|
System.setProperty("http.maxRedirects", "5");
|
||||||
@ -80,7 +77,6 @@ public class HttpClient {
|
|||||||
conn.setRequestMethod(method);
|
conn.setRequestMethod(method);
|
||||||
conn.setDoOutput(true);
|
conn.setDoOutput(true);
|
||||||
if (StringUtils.isNotBlank(body)) {
|
if (StringUtils.isNotBlank(body)) {
|
||||||
// fix: apache http nio framework must set some content to request body
|
|
||||||
byte[] b = body.getBytes();
|
byte[] b = body.getBytes();
|
||||||
conn.setRequestProperty("Content-Length", String.valueOf(b.length));
|
conn.setRequestProperty("Content-Length", String.valueOf(b.length));
|
||||||
conn.getOutputStream().write(b, 0, b.length);
|
conn.getOutputStream().write(b, 0, b.length);
|
||||||
@ -88,7 +84,9 @@ public class HttpClient {
|
|||||||
conn.getOutputStream().close();
|
conn.getOutputStream().close();
|
||||||
}
|
}
|
||||||
conn.connect();
|
conn.connect();
|
||||||
NAMING_LOGGER.debug("Request from server: " + url);
|
if (NAMING_LOGGER.isDebugEnabled()) {
|
||||||
|
NAMING_LOGGER.debug("Request from server: " + url);
|
||||||
|
}
|
||||||
return getResult(conn);
|
return getResult(conn);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
try {
|
try {
|
||||||
|
@ -20,6 +20,7 @@ import com.alibaba.fastjson.JSONObject;
|
|||||||
import com.alibaba.fastjson.TypeReference;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||||
import com.alibaba.nacos.api.SystemPropertyKeyConst;
|
import com.alibaba.nacos.api.SystemPropertyKeyConst;
|
||||||
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
import com.alibaba.nacos.api.naming.CommonParams;
|
import com.alibaba.nacos.api.naming.CommonParams;
|
||||||
import com.alibaba.nacos.api.naming.pojo.Instance;
|
import com.alibaba.nacos.api.naming.pojo.Instance;
|
||||||
@ -35,6 +36,7 @@ import com.alibaba.nacos.client.naming.utils.CollectionUtils;
|
|||||||
import com.alibaba.nacos.client.naming.utils.NetUtils;
|
import com.alibaba.nacos.client.naming.utils.NetUtils;
|
||||||
import com.alibaba.nacos.client.naming.utils.SignUtil;
|
import com.alibaba.nacos.client.naming.utils.SignUtil;
|
||||||
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
|
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
|
||||||
|
import com.alibaba.nacos.client.security.SecurityProxy;
|
||||||
import com.alibaba.nacos.client.utils.AppNameUtils;
|
import com.alibaba.nacos.client.utils.AppNameUtils;
|
||||||
import com.alibaba.nacos.client.utils.TemplateUtils;
|
import com.alibaba.nacos.client.utils.TemplateUtils;
|
||||||
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
|
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
|
||||||
@ -71,14 +73,21 @@ public class NamingProxy {
|
|||||||
|
|
||||||
private List<String> serversFromEndpoint = new ArrayList<String>();
|
private List<String> serversFromEndpoint = new ArrayList<String>();
|
||||||
|
|
||||||
|
private SecurityProxy securityProxy;
|
||||||
|
|
||||||
private long lastSrvRefTime = 0L;
|
private long lastSrvRefTime = 0L;
|
||||||
|
|
||||||
private long vipSrvRefInterMillis = TimeUnit.SECONDS.toMillis(30);
|
private long vipSrvRefInterMillis = TimeUnit.SECONDS.toMillis(30);
|
||||||
|
|
||||||
|
private long securityInfoRefreshIntervalMills = TimeUnit.SECONDS.toMillis(5);
|
||||||
|
|
||||||
private Properties properties;
|
private Properties properties;
|
||||||
|
|
||||||
public NamingProxy(String namespaceId, String endpoint, String serverList) {
|
public NamingProxy(String namespaceId, String endpoint, String serverList, Properties properties) {
|
||||||
|
|
||||||
|
securityProxy = new SecurityProxy(properties);
|
||||||
|
this.properties = properties;
|
||||||
|
this.setServerPort(DEFAULT_SERVER_PORT);
|
||||||
this.namespaceId = namespaceId;
|
this.namespaceId = namespaceId;
|
||||||
this.endpoint = endpoint;
|
this.endpoint = endpoint;
|
||||||
if (StringUtils.isNotEmpty(serverList)) {
|
if (StringUtils.isNotEmpty(serverList)) {
|
||||||
@ -88,19 +97,16 @@ public class NamingProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initRefreshSrvIfNeed();
|
initRefreshTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initRefreshSrvIfNeed() {
|
private void initRefreshTask() {
|
||||||
if (StringUtils.isEmpty(endpoint)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
|
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(2, new ThreadFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Thread newThread(Runnable r) {
|
public Thread newThread(Runnable r) {
|
||||||
Thread t = new Thread(r);
|
Thread t = new Thread(r);
|
||||||
t.setName("com.alibaba.nacos.client.naming.serverlist.updater");
|
t.setName("com.alibaba.nacos.client.naming.updater");
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@ -113,6 +119,14 @@ public class NamingProxy {
|
|||||||
}
|
}
|
||||||
}, 0, vipSrvRefInterMillis, TimeUnit.MILLISECONDS);
|
}, 0, vipSrvRefInterMillis, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
|
||||||
|
executorService.scheduleWithFixedDelay(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
securityProxy.login(getServerList());
|
||||||
|
}
|
||||||
|
}, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
refreshSrvIfNeed();
|
refreshSrvIfNeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,13 +391,15 @@ public class NamingProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String reqAPI(String api, Map<String, String> params, String body, String method) throws NacosException {
|
public String reqAPI(String api, Map<String, String> params, String body, String method) throws NacosException {
|
||||||
|
return reqAPI(api, params, body, getServerList(), method);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getServerList() {
|
||||||
List<String> snapshot = serversFromEndpoint;
|
List<String> snapshot = serversFromEndpoint;
|
||||||
if (!CollectionUtils.isEmpty(serverList)) {
|
if (!CollectionUtils.isEmpty(serverList)) {
|
||||||
snapshot = serverList;
|
snapshot = serverList;
|
||||||
}
|
}
|
||||||
|
return snapshot;
|
||||||
return reqAPI(api, params, body, snapshot, method);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String callServer(String api, Map<String, String> params, String body, String curServer) throws NacosException {
|
public String callServer(String api, Map<String, String> params, String body, String curServer) throws NacosException {
|
||||||
@ -394,7 +410,7 @@ public class NamingProxy {
|
|||||||
throws NacosException {
|
throws NacosException {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
long end = 0;
|
long end = 0;
|
||||||
checkSignature(params);
|
injectSecurityInfo(params);
|
||||||
List<String> headers = builderHeaders();
|
List<String> headers = builderHeaders();
|
||||||
|
|
||||||
String url;
|
String url;
|
||||||
@ -456,7 +472,7 @@ public class NamingProxy {
|
|||||||
if (StringUtils.isNotBlank(nacosDomain)) {
|
if (StringUtils.isNotBlank(nacosDomain)) {
|
||||||
for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
|
for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
|
||||||
try {
|
try {
|
||||||
return callServer(api, params, body, nacosDomain);
|
return callServer(api, params, body, nacosDomain, method);
|
||||||
} catch (NacosException e) {
|
} catch (NacosException e) {
|
||||||
exception = e;
|
exception = e;
|
||||||
if (NAMING_LOGGER.isDebugEnabled()) {
|
if (NAMING_LOGGER.isDebugEnabled()) {
|
||||||
@ -474,22 +490,27 @@ public class NamingProxy {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSignature(Map<String, String> params) {
|
private void injectSecurityInfo(Map<String, String> params) {
|
||||||
|
|
||||||
|
// Inject token if exist:
|
||||||
|
if (StringUtils.isNotBlank(securityProxy.getAccessToken())) {
|
||||||
|
params.put(Constants.ACCESS_TOKEN, securityProxy.getAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject ak/sk if exist:
|
||||||
String ak = getAccessKey();
|
String ak = getAccessKey();
|
||||||
String sk = getSecretKey();
|
String sk = getSecretKey();
|
||||||
params.put("app", AppNameUtils.getAppName());
|
params.put("app", AppNameUtils.getAppName());
|
||||||
if (StringUtils.isEmpty(ak) && StringUtils.isEmpty(sk)) {
|
if (StringUtils.isNotBlank(ak) && StringUtils.isNotBlank(sk)) {
|
||||||
return;
|
try {
|
||||||
}
|
String signData = getSignData(params.get("serviceName"));
|
||||||
|
String signature = SignUtil.sign(signData, sk);
|
||||||
try {
|
params.put("signature", signature);
|
||||||
String signData = getSignData(params.get("serviceName"));
|
params.put("data", signData);
|
||||||
String signature = SignUtil.sign(signData, sk);
|
params.put("ak", ak);
|
||||||
params.put("signature", signature);
|
} catch (Exception e) {
|
||||||
params.put("data", signData);
|
NAMING_LOGGER.error("inject ak/sk failed.", e);
|
||||||
params.put("ak", ak);
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nkorange
|
* @author alibaba
|
||||||
*/
|
*/
|
||||||
public class Chooser<K, T> {
|
public class Chooser<K, T> {
|
||||||
|
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.security;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||||
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
|
import com.alibaba.nacos.client.naming.net.HttpClient;
|
||||||
|
import com.alibaba.nacos.common.utils.HttpMethod;
|
||||||
|
import org.apache.commons.codec.Charsets;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Security proxy to update security information
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class SecurityProxy {
|
||||||
|
|
||||||
|
private static final Logger SECURITY_LOGGER = LoggerFactory.getLogger(SecurityProxy.class);
|
||||||
|
|
||||||
|
private static final String LOGIN_URL = "/v1/auth/users/login";
|
||||||
|
|
||||||
|
private String contextPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's name
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's password
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A token to take with when sending request to Nacos server
|
||||||
|
*/
|
||||||
|
private String accessToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TTL of token in seconds
|
||||||
|
*/
|
||||||
|
private long tokenTtl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last timestamp refresh security info from server
|
||||||
|
*/
|
||||||
|
private long lastRefreshTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* time window to refresh security info in seconds
|
||||||
|
*/
|
||||||
|
private long tokenRefreshWindow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct from properties, keeping flexibility
|
||||||
|
*
|
||||||
|
* @param properties a bunch of properties to read
|
||||||
|
*/
|
||||||
|
public SecurityProxy(Properties properties) {
|
||||||
|
username = properties.getProperty(PropertyKeyConst.USERNAME, StringUtils.EMPTY);
|
||||||
|
password = properties.getProperty(PropertyKeyConst.PASSWORD, StringUtils.EMPTY);
|
||||||
|
contextPath = properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean login(List<String> servers) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ((System.currentTimeMillis() - lastRefreshTime) < TimeUnit.SECONDS.toMillis(tokenTtl - tokenRefreshWindow)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String server : servers) {
|
||||||
|
if (login(server)) {
|
||||||
|
lastRefreshTime = System.currentTimeMillis();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean login(String server) {
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(username)) {
|
||||||
|
String body = "username=" + username + "&password=" + password;
|
||||||
|
String url = "http://" + server + contextPath + LOGIN_URL;
|
||||||
|
|
||||||
|
if (server.contains(Constants.HTTP_PREFIX)) {
|
||||||
|
url = server + contextPath + LOGIN_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpClient.HttpResult result = HttpClient.request(url, new ArrayList<String>(2),
|
||||||
|
new HashMap<String, String>(2), body, Charsets.UTF_8.name(), HttpMethod.POST);
|
||||||
|
|
||||||
|
if (result.code != HttpURLConnection.HTTP_OK) {
|
||||||
|
SECURITY_LOGGER.error("login failed: {}", JSON.toJSONString(result));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject obj = JSON.parseObject(result.content);
|
||||||
|
if (obj.containsKey(Constants.ACCESS_TOKEN)) {
|
||||||
|
accessToken = obj.getString(Constants.ACCESS_TOKEN);
|
||||||
|
tokenTtl = obj.getIntValue(Constants.TOKEN_TTL);
|
||||||
|
tokenRefreshWindow = tokenTtl / 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@
|
|||||||
package com.alibaba.nacos.client.utils;
|
package com.alibaba.nacos.client.utils;
|
||||||
|
|
||||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||||
|
import com.alibaba.nacos.api.SystemPropertyKeyConst;
|
||||||
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
import com.alibaba.nacos.client.config.impl.HttpSimpleClient;
|
import com.alibaba.nacos.client.config.impl.HttpSimpleClient;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
@ -152,6 +154,36 @@ public class ParamUtil {
|
|||||||
ParamUtil.defaultNodesPath = defaultNodesPath;
|
ParamUtil.defaultNodesPath = defaultNodesPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String parseNamespace(Properties properties) {
|
||||||
|
String namespaceTmp = null;
|
||||||
|
|
||||||
|
String isUseCloudNamespaceParsing =
|
||||||
|
properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING,
|
||||||
|
System.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING,
|
||||||
|
String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING)));
|
||||||
|
|
||||||
|
if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) {
|
||||||
|
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
||||||
|
@Override
|
||||||
|
public String call() {
|
||||||
|
return TenantUtil.getUserTenantForAcm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
||||||
|
@Override
|
||||||
|
public String call() {
|
||||||
|
String namespace = System.getenv(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE);
|
||||||
|
return org.apache.commons.lang3.StringUtils.isNotBlank(namespace) ? namespace : StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (org.apache.commons.lang3.StringUtils.isBlank(namespaceTmp)) {
|
||||||
|
namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE);
|
||||||
|
}
|
||||||
|
return StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
public static String parsingEndpointRule(String endpointUrl) {
|
public static String parsingEndpointRule(String endpointUrl) {
|
||||||
// 配置文件中输入的话,以 ENV 中的优先,
|
// 配置文件中输入的话,以 ENV 中的优先,
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.listener.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
import com.alibaba.nacos.client.config.impl.ConfigChangeHandler;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ConfigChangeHandlerTest {
|
||||||
|
@Test
|
||||||
|
public void testParseProperties() throws IOException {
|
||||||
|
Map properties = ConfigChangeHandler.getInstance().parseChangeData("", "app.name = nacos", "properties");
|
||||||
|
Assert.assertEquals("nacos", ((ConfigChangeItem)properties.get("app.name")).getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseYaml() throws IOException {
|
||||||
|
Map properties = ConfigChangeHandler.getInstance().parseChangeData("", "app:\n name: nacos", "yaml");
|
||||||
|
Assert.assertEquals("nacos", ((ConfigChangeItem)properties.get("app.name")).getNewValue());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.listener.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
import com.alibaba.nacos.client.config.impl.PropertiesChangeParser;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PropertiesChangeParserTest {
|
||||||
|
private PropertiesChangeParser parser = new PropertiesChangeParser();
|
||||||
|
private final String type = "properties";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testType() {
|
||||||
|
Assert.assertEquals(true, parser.isResponsibleFor(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddKey() throws IOException {
|
||||||
|
Map<String, ConfigChangeItem> map = parser.doParse("", "app.name = nacos", type);
|
||||||
|
Assert.assertEquals(null, map.get("app.name").getOldValue());
|
||||||
|
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveKey() throws IOException {
|
||||||
|
Map<String, ConfigChangeItem> map = parser.doParse("app.name = nacos", "", type);
|
||||||
|
Assert.assertEquals("nacos", map.get("app.name").getOldValue());
|
||||||
|
Assert.assertEquals(null, map.get("app.name").getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyKey() throws IOException {
|
||||||
|
Map<String, ConfigChangeItem> map = parser.doParse("app.name = rocketMQ", "app.name = nacos", type);
|
||||||
|
Assert.assertEquals("rocketMQ", map.get("app.name").getOldValue());
|
||||||
|
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.client.config.listener.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||||
|
import com.alibaba.nacos.client.config.impl.YmlChangeParser;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class YmlChangeParserTest {
|
||||||
|
private YmlChangeParser parser = new YmlChangeParser();
|
||||||
|
private final String type = "yaml";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testType() {
|
||||||
|
Assert.assertEquals(true, parser.isResponsibleFor(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddKey() throws IOException {
|
||||||
|
Map<String, ConfigChangeItem> map = parser.doParse("", "app:\n name: nacos", type);
|
||||||
|
Assert.assertEquals(null, map.get("app.name").getOldValue());
|
||||||
|
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveKey() throws IOException {
|
||||||
|
Map<String, ConfigChangeItem> map = parser.doParse("app:\n name: nacos", "", type);
|
||||||
|
Assert.assertEquals("nacos", map.get("app.name").getOldValue());
|
||||||
|
Assert.assertEquals(null, map.get("app.name").getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifyKey() throws IOException {
|
||||||
|
Map<String, ConfigChangeItem> map = parser.doParse("app:\n name: rocketMQ", "app:\n name: nacos", type);
|
||||||
|
Assert.assertEquals("rocketMQ", map.get("app.name").getOldValue());
|
||||||
|
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,8 +19,6 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config main
|
* Config main
|
||||||
*
|
*
|
||||||
@ -30,7 +28,7 @@ import java.net.UnknownHostException;
|
|||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class Config {
|
public class Config {
|
||||||
|
|
||||||
public static void main(String[] args) throws UnknownHostException {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(Config.class, args);
|
SpringApplication.run(Config.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.config.server.auth;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
|
import com.alibaba.nacos.core.auth.Resource;
|
||||||
|
import com.alibaba.nacos.core.auth.ResourceParser;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config resource parser
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class ConfigResourceParser implements ResourceParser {
|
||||||
|
|
||||||
|
private static final String AUTH_CONFIG_PREFIX = "config/";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseName(Object request) {
|
||||||
|
HttpServletRequest req = (HttpServletRequest) request;
|
||||||
|
String namespaceId = req.getParameter("tenant");
|
||||||
|
String groupName = req.getParameter("group");
|
||||||
|
String dataId = req.getParameter("dataId");
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(namespaceId)) {
|
||||||
|
namespaceId = Constants.DEFAULT_NAMESPACE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(namespaceId).append(Resource.SPLITTER);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(dataId)) {
|
||||||
|
sb.append("*")
|
||||||
|
.append(Resource.SPLITTER)
|
||||||
|
.append(AUTH_CONFIG_PREFIX)
|
||||||
|
.append("*");
|
||||||
|
} else {
|
||||||
|
sb.append(groupName)
|
||||||
|
.append(Resource.SPLITTER)
|
||||||
|
.append(AUTH_CONFIG_PREFIX)
|
||||||
|
.append(dataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.config.server.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class PermissionInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Role name
|
||||||
|
*/
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource
|
||||||
|
*/
|
||||||
|
private String resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action on resource
|
||||||
|
*/
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
public String getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResource(String resource) {
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAction(String action) {
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.config.server.auth;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.model.Page;
|
||||||
|
import com.alibaba.nacos.config.server.service.PersistService;
|
||||||
|
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission CRUD service
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class PermissionPersistService extends PersistService {
|
||||||
|
|
||||||
|
public Page<PermissionInfo> getPermissions(String role, int pageNo, int pageSize) {
|
||||||
|
PaginationHelper<PermissionInfo> helper = new PaginationHelper<>();
|
||||||
|
|
||||||
|
String sqlCountRows = "select count(*) from permissions where ";
|
||||||
|
String sqlFetchRows
|
||||||
|
= "select role,resource,action from permissions where ";
|
||||||
|
|
||||||
|
String where = " role='" + role + "' ";
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(role)) {
|
||||||
|
where = " 1=1 ";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Page<PermissionInfo> pageInfo = helper.fetchPage(jt, sqlCountRows
|
||||||
|
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||||
|
pageSize, PERMISSION_ROW_MAPPER);
|
||||||
|
|
||||||
|
if (pageInfo==null) {
|
||||||
|
pageInfo = new Page<>();
|
||||||
|
pageInfo.setTotalCount(0);
|
||||||
|
pageInfo.setPageItems(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageInfo;
|
||||||
|
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPermission(String role, String resource, String action) {
|
||||||
|
|
||||||
|
String sql = "INSERT into permissions (role, resource, action) VALUES (?, ?, ?)";
|
||||||
|
|
||||||
|
try {
|
||||||
|
jt.update(sql, role, resource, action);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePermission(String role, String resource, String action) {
|
||||||
|
|
||||||
|
String sql = "DELETE from permissions WHERE role=? and resource=? and action=?";
|
||||||
|
try {
|
||||||
|
jt.update(sql, role, resource, action);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class PermissionRowMapper implements
|
||||||
|
RowMapper<PermissionInfo> {
|
||||||
|
@Override
|
||||||
|
public PermissionInfo mapRow(ResultSet rs, int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
PermissionInfo info = new PermissionInfo();
|
||||||
|
info.setResource(rs.getString("resource"));
|
||||||
|
info.setAction(rs.getString("action"));
|
||||||
|
info.setRole(rs.getString("role"));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final PermissionRowMapper PERMISSION_ROW_MAPPER = new PermissionRowMapper();
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.config.server.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Role Info
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class RoleInfo {
|
||||||
|
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
public String getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.config.server.auth;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.model.Page;
|
||||||
|
import com.alibaba.nacos.config.server.model.User;
|
||||||
|
import com.alibaba.nacos.config.server.service.PersistService;
|
||||||
|
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Role CRUD service
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class RolePersistService extends PersistService {
|
||||||
|
|
||||||
|
|
||||||
|
public Page<RoleInfo> getRoles(int pageNo, int pageSize) {
|
||||||
|
|
||||||
|
PaginationHelper<RoleInfo> helper = new PaginationHelper<>();
|
||||||
|
|
||||||
|
String sqlCountRows = "select count(*) from (select distinct role from roles) roles where ";
|
||||||
|
String sqlFetchRows
|
||||||
|
= "select role,username from roles where ";
|
||||||
|
|
||||||
|
String where = " 1=1 ";
|
||||||
|
|
||||||
|
try {
|
||||||
|
Page<RoleInfo> pageInfo = helper.fetchPage(jt, sqlCountRows
|
||||||
|
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||||
|
pageSize, ROLE_INFO_ROW_MAPPER);
|
||||||
|
if (pageInfo == null) {
|
||||||
|
pageInfo = new Page<>();
|
||||||
|
pageInfo.setTotalCount(0);
|
||||||
|
pageInfo.setPageItems(new ArrayList<>());
|
||||||
|
}
|
||||||
|
return pageInfo;
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<RoleInfo> getRolesByUserName(String username, int pageNo, int pageSize) {
|
||||||
|
|
||||||
|
PaginationHelper<RoleInfo> helper = new PaginationHelper<>();
|
||||||
|
|
||||||
|
String sqlCountRows = "select count(*) from roles where ";
|
||||||
|
String sqlFetchRows
|
||||||
|
= "select role,username from roles where ";
|
||||||
|
|
||||||
|
String where = " username='" + username + "' ";
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(username)) {
|
||||||
|
where = " 1=1 ";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return helper.fetchPage(jt, sqlCountRows
|
||||||
|
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||||
|
pageSize, ROLE_INFO_ROW_MAPPER);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRole(String role, String userName) {
|
||||||
|
|
||||||
|
String sql = "INSERT into roles (role, username) VALUES (?, ?)";
|
||||||
|
|
||||||
|
try {
|
||||||
|
jt.update(sql, role, userName);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteRole(String role) {
|
||||||
|
String sql = "DELETE from roles WHERE role=?";
|
||||||
|
try {
|
||||||
|
jt.update(sql, role);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteRole(String role, String username) {
|
||||||
|
String sql = "DELETE from roles WHERE role=? and username=?";
|
||||||
|
try {
|
||||||
|
jt.update(sql, role, username);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RoleInfoRowMapper implements
|
||||||
|
RowMapper<RoleInfo> {
|
||||||
|
@Override
|
||||||
|
public RoleInfo mapRow(ResultSet rs, int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
RoleInfo roleInfo = new RoleInfo();
|
||||||
|
roleInfo.setRole(rs.getString("role"));
|
||||||
|
roleInfo.setUsername(rs.getString("username"));
|
||||||
|
return roleInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final RoleInfoRowMapper ROLE_INFO_ROW_MAPPER = new RoleInfoRowMapper();
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.config.server.auth;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.model.Page;
|
||||||
|
import com.alibaba.nacos.config.server.model.User;
|
||||||
|
import com.alibaba.nacos.config.server.service.PersistService;
|
||||||
|
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
|
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User CRUD service
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserPersistService extends PersistService {
|
||||||
|
|
||||||
|
public void createUser(String username, String password) {
|
||||||
|
String sql = "INSERT into users (username, password, enabled) VALUES (?, ?, ?)";
|
||||||
|
|
||||||
|
try {
|
||||||
|
jt.update(sql, username, password, true);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteUser(String username) {
|
||||||
|
String sql = "DELETE from users WHERE username=?";
|
||||||
|
try {
|
||||||
|
jt.update(sql, username);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUserPassword(String username, String password) {
|
||||||
|
try {
|
||||||
|
jt.update(
|
||||||
|
"UPDATE users SET password = ? WHERE username=?",
|
||||||
|
password, username);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public User findUserByUsername(String username) {
|
||||||
|
String sql = "SELECT username,password FROM users WHERE username=? ";
|
||||||
|
try {
|
||||||
|
return this.jt.queryForObject(sql, new Object[]{username}, USER_ROW_MAPPER);
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
} catch (EmptyResultDataAccessException e) {
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
fatalLog.error("[db-other-error]" + e.getMessage(), e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<User> getUsers(int pageNo, int pageSize) {
|
||||||
|
|
||||||
|
PaginationHelper<User> helper = new PaginationHelper<>();
|
||||||
|
|
||||||
|
String sqlCountRows = "select count(*) from users where ";
|
||||||
|
String sqlFetchRows
|
||||||
|
= "select username,password from users where ";
|
||||||
|
|
||||||
|
String where = " 1=1 ";
|
||||||
|
|
||||||
|
try {
|
||||||
|
Page<User> pageInfo = helper.fetchPage(jt, sqlCountRows
|
||||||
|
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||||
|
pageSize, USER_ROW_MAPPER);
|
||||||
|
if (pageInfo == null) {
|
||||||
|
pageInfo = new Page<>();
|
||||||
|
pageInfo.setTotalCount(0);
|
||||||
|
pageInfo.setPageItems(new ArrayList<>());
|
||||||
|
}
|
||||||
|
return pageInfo;
|
||||||
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
|
fatalLog.error("[db-error] " + e.toString(), e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class UserRowMapper implements
|
||||||
|
RowMapper<User> {
|
||||||
|
@Override
|
||||||
|
public User mapRow(ResultSet rs, int rowNum)
|
||||||
|
throws SQLException {
|
||||||
|
User info = new User();
|
||||||
|
info.setUsername(rs.getString("username"));
|
||||||
|
info.setPassword(rs.getString("password"));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final UserRowMapper USER_ROW_MAPPER = new UserRowMapper();
|
||||||
|
|
||||||
|
}
|
@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.config.server.controller;
|
package com.alibaba.nacos.config.server.controller;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.auth.ConfigResourceParser;
|
||||||
import com.alibaba.nacos.config.server.constant.Constants;
|
import com.alibaba.nacos.config.server.constant.Constants;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
|
import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean;
|
||||||
import com.alibaba.nacos.config.server.model.*;
|
import com.alibaba.nacos.config.server.model.*;
|
||||||
import com.alibaba.nacos.config.server.result.ResultBuilder;
|
import com.alibaba.nacos.config.server.result.ResultBuilder;
|
||||||
import com.alibaba.nacos.config.server.result.code.ResultCodeEnum;
|
import com.alibaba.nacos.config.server.result.code.ResultCodeEnum;
|
||||||
@ -27,6 +29,8 @@ import com.alibaba.nacos.config.server.service.PersistService;
|
|||||||
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
|
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
|
||||||
import com.alibaba.nacos.config.server.utils.*;
|
import com.alibaba.nacos.config.server.utils.*;
|
||||||
import com.alibaba.nacos.config.server.utils.event.EventDispatcher;
|
import com.alibaba.nacos.config.server.utils.event.EventDispatcher;
|
||||||
|
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||||
|
import com.alibaba.nacos.core.auth.Secured;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -46,6 +50,7 @@ import java.io.IOException;
|
|||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.alibaba.nacos.core.utils.SystemUtils.LOCAL_IP;
|
import static com.alibaba.nacos.core.utils.SystemUtils.LOCAL_IP;
|
||||||
|
|
||||||
@ -66,7 +71,7 @@ public class ConfigController {
|
|||||||
|
|
||||||
private static final String EXPORT_CONFIG_FILE_NAME_EXT = ".zip";
|
private static final String EXPORT_CONFIG_FILE_NAME_EXT = ".zip";
|
||||||
|
|
||||||
private static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
private static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyyMMddHHmmss";
|
||||||
|
|
||||||
private final ConfigServletInner inner;
|
private final ConfigServletInner inner;
|
||||||
|
|
||||||
@ -88,6 +93,7 @@ public class ConfigController {
|
|||||||
* @throws NacosException
|
* @throws NacosException
|
||||||
*/
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||||
public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
|
public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
|
||||||
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
||||||
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
|
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
|
||||||
@ -164,6 +170,7 @@ public class ConfigController {
|
|||||||
* @throws NacosException
|
* @throws NacosException
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public void getConfig(HttpServletRequest request, HttpServletResponse response,
|
public void getConfig(HttpServletRequest request, HttpServletResponse response,
|
||||||
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
||||||
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
|
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
|
||||||
@ -184,6 +191,7 @@ public class ConfigController {
|
|||||||
* @throws NacosException
|
* @throws NacosException
|
||||||
*/
|
*/
|
||||||
@GetMapping(params = "show=all")
|
@GetMapping(params = "show=all")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public ConfigAllInfo detailConfigInfo(HttpServletRequest request, HttpServletResponse response,
|
public ConfigAllInfo detailConfigInfo(HttpServletRequest request, HttpServletResponse response,
|
||||||
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
||||||
@RequestParam(value = "tenant", required = false,
|
@RequestParam(value = "tenant", required = false,
|
||||||
@ -200,6 +208,7 @@ public class ConfigController {
|
|||||||
* @throws NacosException
|
* @throws NacosException
|
||||||
*/
|
*/
|
||||||
@DeleteMapping
|
@DeleteMapping
|
||||||
|
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||||
public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response,
|
public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response,
|
||||||
@RequestParam("dataId") String dataId, //
|
@RequestParam("dataId") String dataId, //
|
||||||
@RequestParam("group") String group, //
|
@RequestParam("group") String group, //
|
||||||
@ -229,6 +238,7 @@ public class ConfigController {
|
|||||||
* @Param [request, response, dataId, group, tenant, tag]
|
* @Param [request, response, dataId, group, tenant, tag]
|
||||||
*/
|
*/
|
||||||
@DeleteMapping(params = "delType=ids")
|
@DeleteMapping(params = "delType=ids")
|
||||||
|
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||||
public RestResult<Boolean> deleteConfigs(HttpServletRequest request, HttpServletResponse response,
|
public RestResult<Boolean> deleteConfigs(HttpServletRequest request, HttpServletResponse response,
|
||||||
@RequestParam(value = "ids") List<Long> ids) {
|
@RequestParam(value = "ids") List<Long> ids) {
|
||||||
String clientIp = RequestUtil.getRemoteIp(request);
|
String clientIp = RequestUtil.getRemoteIp(request);
|
||||||
@ -247,6 +257,7 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/catalog")
|
@GetMapping("/catalog")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public RestResult<ConfigAdvanceInfo> getConfigAdvanceInfo(@RequestParam("dataId") String dataId,
|
public RestResult<ConfigAdvanceInfo> getConfigAdvanceInfo(@RequestParam("dataId") String dataId,
|
||||||
@RequestParam("group") String group,
|
@RequestParam("group") String group,
|
||||||
@RequestParam(value = "tenant", required = false,
|
@RequestParam(value = "tenant", required = false,
|
||||||
@ -262,6 +273,7 @@ public class ConfigController {
|
|||||||
* 比较MD5
|
* 比较MD5
|
||||||
*/
|
*/
|
||||||
@PostMapping("/listener")
|
@PostMapping("/listener")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public void listener(HttpServletRequest request, HttpServletResponse response)
|
public void listener(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
|
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
|
||||||
@ -287,6 +299,7 @@ public class ConfigController {
|
|||||||
* 订阅改配置的客户端信息
|
* 订阅改配置的客户端信息
|
||||||
*/
|
*/
|
||||||
@GetMapping("/listener")
|
@GetMapping("/listener")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public GroupkeyListenserStatus getListeners(@RequestParam("dataId") String dataId,
|
public GroupkeyListenserStatus getListeners(@RequestParam("dataId") String dataId,
|
||||||
@RequestParam("group") String group,
|
@RequestParam("group") String group,
|
||||||
@RequestParam(value = "tenant", required = false) String tenant,
|
@RequestParam(value = "tenant", required = false) String tenant,
|
||||||
@ -306,6 +319,7 @@ public class ConfigController {
|
|||||||
* 查询配置信息,返回JSON格式。
|
* 查询配置信息,返回JSON格式。
|
||||||
*/
|
*/
|
||||||
@GetMapping(params = "search=accurate")
|
@GetMapping(params = "search=accurate")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public Page<ConfigInfo> searchConfig(@RequestParam("dataId") String dataId,
|
public Page<ConfigInfo> searchConfig(@RequestParam("dataId") String dataId,
|
||||||
@RequestParam("group") String group,
|
@RequestParam("group") String group,
|
||||||
@RequestParam(value = "appName", required = false) String appName,
|
@RequestParam(value = "appName", required = false) String appName,
|
||||||
@ -335,6 +349,7 @@ public class ConfigController {
|
|||||||
* 模糊查询配置信息。不允许只根据内容模糊查询,即dataId和group都为NULL,但content不是NULL。这种情况下,返回所有配置。
|
* 模糊查询配置信息。不允许只根据内容模糊查询,即dataId和group都为NULL,但content不是NULL。这种情况下,返回所有配置。
|
||||||
*/
|
*/
|
||||||
@GetMapping(params = "search=blur")
|
@GetMapping(params = "search=blur")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public Page<ConfigInfo> fuzzySearchConfig(@RequestParam("dataId") String dataId,
|
public Page<ConfigInfo> fuzzySearchConfig(@RequestParam("dataId") String dataId,
|
||||||
@RequestParam("group") String group,
|
@RequestParam("group") String group,
|
||||||
@RequestParam(value = "appName", required = false) String appName,
|
@RequestParam(value = "appName", required = false) String appName,
|
||||||
@ -361,6 +376,7 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping(params = "beta=true")
|
@DeleteMapping(params = "beta=true")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public RestResult<Boolean> stopBeta(@RequestParam(value = "dataId") String dataId,
|
public RestResult<Boolean> stopBeta(@RequestParam(value = "dataId") String dataId,
|
||||||
@RequestParam(value = "group") String group,
|
@RequestParam(value = "group") String group,
|
||||||
@RequestParam(value = "tenant", required = false,
|
@RequestParam(value = "tenant", required = false,
|
||||||
@ -383,6 +399,7 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(params = "beta=true")
|
@GetMapping(params = "beta=true")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public RestResult<ConfigInfo4Beta> queryBeta(@RequestParam(value = "dataId") String dataId,
|
public RestResult<ConfigInfo4Beta> queryBeta(@RequestParam(value = "dataId") String dataId,
|
||||||
@RequestParam(value = "group") String group,
|
@RequestParam(value = "group") String group,
|
||||||
@RequestParam(value = "tenant", required = false,
|
@RequestParam(value = "tenant", required = false,
|
||||||
@ -403,6 +420,7 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(params = "export=true")
|
@GetMapping(params = "export=true")
|
||||||
|
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||||
public ResponseEntity<byte[]> exportConfig(@RequestParam(value = "dataId", required = false) String dataId,
|
public ResponseEntity<byte[]> exportConfig(@RequestParam(value = "dataId", required = false) String dataId,
|
||||||
@RequestParam(value = "group", required = false) String group,
|
@RequestParam(value = "group", required = false) String group,
|
||||||
@RequestParam(value = "appName", required = false) String appName,
|
@RequestParam(value = "appName", required = false) String appName,
|
||||||
@ -442,6 +460,7 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(params = "import=true")
|
@PostMapping(params = "import=true")
|
||||||
|
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||||
public RestResult<Map<String, Object>> importAndPublishConfig(HttpServletRequest request,
|
public RestResult<Map<String, Object>> importAndPublishConfig(HttpServletRequest request,
|
||||||
@RequestParam(value = "src_user", required = false) String srcUser,
|
@RequestParam(value = "src_user", required = false) String srcUser,
|
||||||
@RequestParam(value = "namespace", required = false) String namespace,
|
@RequestParam(value = "namespace", required = false) String namespace,
|
||||||
@ -525,14 +544,20 @@ public class ConfigController {
|
|||||||
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
|
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(params = "clone=true")
|
@PostMapping(params = "clone=true")
|
||||||
public RestResult<Map<String, Object>> cloneConfig(HttpServletRequest request,
|
public RestResult<Map<String, Object>> cloneConfig(HttpServletRequest request,
|
||||||
@RequestParam(value = "src_user", required = false) String srcUser,
|
@RequestParam(value = "src_user", required = false) String srcUser,
|
||||||
@RequestParam(value = "tenant", required = true) String namespace,
|
@RequestParam(value = "tenant", required = true) String namespace,
|
||||||
@RequestParam(value = "ids", required = true) List<Long> ids,
|
@RequestBody(required = true)
|
||||||
|
List<SameNamespaceCloneConfigBean> configBeansList,
|
||||||
@RequestParam(value = "policy", defaultValue = "ABORT")
|
@RequestParam(value = "policy", defaultValue = "ABORT")
|
||||||
SameConfigPolicy policy) throws NacosException {
|
SameConfigPolicy policy) throws NacosException {
|
||||||
Map<String, Object> failedData = new HashMap<>(4);
|
Map<String, Object> failedData = new HashMap<>(4);
|
||||||
|
if(CollectionUtils.isEmpty(configBeansList)){
|
||||||
|
failedData.put("succCount", 0);
|
||||||
|
return ResultBuilder.buildResult(ResultCodeEnum.NO_SELECTED_CONFIG, failedData);
|
||||||
|
}
|
||||||
|
configBeansList.removeAll(Collections.singleton(null));
|
||||||
|
|
||||||
if (NAMESPACE_PUBLIC_KEY.equalsIgnoreCase(namespace)) {
|
if (NAMESPACE_PUBLIC_KEY.equalsIgnoreCase(namespace)) {
|
||||||
namespace = "";
|
namespace = "";
|
||||||
@ -541,8 +566,14 @@ public class ConfigController {
|
|||||||
return ResultBuilder.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
|
return ResultBuilder.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
ids.removeAll(Collections.singleton(null));
|
List<Long> idList = new ArrayList<>(configBeansList.size());
|
||||||
List<ConfigAllInfo> queryedDataList = persistService.findAllConfigInfo4Export(null, null, null, null, ids);
|
Map<Long, SameNamespaceCloneConfigBean> configBeansMap = configBeansList.stream()
|
||||||
|
.collect(Collectors.toMap(SameNamespaceCloneConfigBean::getCfgId, cfg -> {
|
||||||
|
idList.add(cfg.getCfgId());
|
||||||
|
return cfg;
|
||||||
|
},(k1, k2) -> k1));
|
||||||
|
|
||||||
|
List<ConfigAllInfo> queryedDataList = persistService.findAllConfigInfo4Export(null, null, null, null, idList);
|
||||||
|
|
||||||
if (queryedDataList == null || queryedDataList.isEmpty()) {
|
if (queryedDataList == null || queryedDataList.isEmpty()) {
|
||||||
failedData.put("succCount", 0);
|
failedData.put("succCount", 0);
|
||||||
@ -552,11 +583,12 @@ public class ConfigController {
|
|||||||
List<ConfigAllInfo> configInfoList4Clone = new ArrayList<>(queryedDataList.size());
|
List<ConfigAllInfo> configInfoList4Clone = new ArrayList<>(queryedDataList.size());
|
||||||
|
|
||||||
for (ConfigAllInfo ci : queryedDataList) {
|
for (ConfigAllInfo ci : queryedDataList) {
|
||||||
|
SameNamespaceCloneConfigBean prarmBean = configBeansMap.get(ci.getId());
|
||||||
ConfigAllInfo ci4save = new ConfigAllInfo();
|
ConfigAllInfo ci4save = new ConfigAllInfo();
|
||||||
ci4save.setTenant(namespace);
|
ci4save.setTenant(namespace);
|
||||||
ci4save.setType(ci.getType());
|
ci4save.setType(ci.getType());
|
||||||
ci4save.setGroup(ci.getGroup());
|
ci4save.setGroup((prarmBean != null && StringUtils.isNotBlank(prarmBean.getGroup())) ? prarmBean.getGroup() : ci.getGroup());
|
||||||
ci4save.setDataId(ci.getDataId());
|
ci4save.setDataId((prarmBean != null && StringUtils.isNotBlank(prarmBean.getDataId())) ? prarmBean.getDataId() : ci.getDataId());
|
||||||
ci4save.setContent(ci.getContent());
|
ci4save.setContent(ci.getContent());
|
||||||
if (StringUtils.isNotBlank(ci.getAppName())) {
|
if (StringUtils.isNotBlank(ci.getAppName())) {
|
||||||
ci4save.setAppName(ci.getAppName());
|
ci4save.setAppName(ci.getAppName());
|
||||||
@ -580,7 +612,7 @@ public class ConfigController {
|
|||||||
configInfo.getTenant(), requestIpApp, time.getTime(),
|
configInfo.getTenant(), requestIpApp, time.getTime(),
|
||||||
LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
|
LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
|
||||||
}
|
}
|
||||||
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
|
return ResultBuilder.buildSuccessResult("克隆成功", saveResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,8 @@ public class ConfigServletInner {
|
|||||||
isBeta = true;
|
isBeta = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
String configType = cacheItem.getType();
|
||||||
|
response.setHeader("Config-Type", (null != configType) ? configType : "text");
|
||||||
}
|
}
|
||||||
File file = null;
|
File file = null;
|
||||||
ConfigInfoBase configInfoBase = null;
|
ConfigInfoBase configInfoBase = null;
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.config.server.controller.parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author klw(213539 @ qq.com)
|
||||||
|
* @ClassName: SameNamespaceCloneConfigBean
|
||||||
|
* @Description: 同namespace克隆接口的配制bean
|
||||||
|
* @date 2019/12/13 16:10
|
||||||
|
*/
|
||||||
|
public class SameNamespaceCloneConfigBean {
|
||||||
|
|
||||||
|
private Long cfgId;
|
||||||
|
|
||||||
|
private String dataId;
|
||||||
|
|
||||||
|
private String group;
|
||||||
|
|
||||||
|
public Long getCfgId() {
|
||||||
|
return cfgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCfgId(Long cfgId) {
|
||||||
|
this.cfgId = cfgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDataId() {
|
||||||
|
return dataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataId(String dataId) {
|
||||||
|
this.dataId = dataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroup(String group) {
|
||||||
|
this.group = group;
|
||||||
|
}
|
||||||
|
}
|
@ -109,6 +109,14 @@ public class CacheItem {
|
|||||||
this.tagLastModifiedTs = tagLastModifiedTs;
|
this.tagLastModifiedTs = tagLastModifiedTs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
final String groupKey;
|
final String groupKey;
|
||||||
public volatile String md5 = Constants.NULL;
|
public volatile String md5 = Constants.NULL;
|
||||||
public volatile long lastModifiedTs;
|
public volatile long lastModifiedTs;
|
||||||
@ -123,5 +131,6 @@ public class CacheItem {
|
|||||||
public volatile Map<String, String> tagMd5;
|
public volatile Map<String, String> tagMd5;
|
||||||
public volatile Map<String, Long> tagLastModifiedTs;
|
public volatile Map<String, Long> tagLastModifiedTs;
|
||||||
public SimpleReadWriteLock rwLock = new SimpleReadWriteLock();
|
public SimpleReadWriteLock rwLock = new SimpleReadWriteLock();
|
||||||
|
public String type;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ public class ConfigInfo extends ConfigInfoBase {
|
|||||||
|
|
||||||
private String appName;
|
private String appName;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
|
||||||
public ConfigInfo() {
|
public ConfigInfo() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -63,6 +65,14 @@ public class ConfigInfo extends ConfigInfoBase {
|
|||||||
this.appName = appName;
|
this.appName = appName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return super.hashCode();
|
return super.hashCode();
|
||||||
|
@ -44,6 +44,8 @@ public enum ResultCodeEnum implements IResultCode {
|
|||||||
|
|
||||||
DATA_EMPTY(100005, "导入的文件数据为空"),
|
DATA_EMPTY(100005, "导入的文件数据为空"),
|
||||||
|
|
||||||
|
NO_SELECTED_CONFIG(100006, "没有选择任何配制"),
|
||||||
|
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -57,9 +57,10 @@ public class ConfigService {
|
|||||||
/**
|
/**
|
||||||
* 保存配置文件,并缓存md5.
|
* 保存配置文件,并缓存md5.
|
||||||
*/
|
*/
|
||||||
static public boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs) {
|
static public boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs, String type) {
|
||||||
String groupKey = GroupKey2.getKey(dataId, group, tenant);
|
String groupKey = GroupKey2.getKey(dataId, group, tenant);
|
||||||
makeSure(groupKey);
|
CacheItem ci = makeSure(groupKey);
|
||||||
|
ci.setType(type);
|
||||||
final int lockResult = tryWriteLock(groupKey);
|
final int lockResult = tryWriteLock(groupKey);
|
||||||
assert (lockResult != 0);
|
assert (lockResult != 0);
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ public class DiskUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public File heartBeatFile() {
|
static public File heartBeatFile() {
|
||||||
return new File(NACOS_HOME, "status/heartBeat.txt");
|
return new File(NACOS_HOME, "status" + File.separator + "heartBeat.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
static public String relativePath(String dataId, String group) {
|
static public String relativePath(String dataId, String group) {
|
||||||
|
@ -113,6 +113,7 @@ public class PersistService {
|
|||||||
info.setGroup(rs.getString("group_id"));
|
info.setGroup(rs.getString("group_id"));
|
||||||
info.setTenant(rs.getString("tenant_id"));
|
info.setTenant(rs.getString("tenant_id"));
|
||||||
info.setAppName(rs.getString("app_name"));
|
info.setAppName(rs.getString("app_name"));
|
||||||
|
info.setType(rs.getString("type"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
info.setContent(rs.getString("content"));
|
info.setContent(rs.getString("content"));
|
||||||
@ -235,6 +236,11 @@ public class PersistService {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
info.setType(rs.getString("type"));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1325,7 +1331,7 @@ public class PersistService {
|
|||||||
final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName");
|
final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName");
|
||||||
final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags");
|
final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags");
|
||||||
String sqlCount = "select count(*) from config_info";
|
String sqlCount = "select count(*) from config_info";
|
||||||
String sql = "select ID,data_id,group_id,tenant_id,app_name,content from config_info";
|
String sql = "select ID,data_id,group_id,tenant_id,app_name,content,type from config_info";
|
||||||
StringBuilder where = new StringBuilder(" where ");
|
StringBuilder where = new StringBuilder(" where ");
|
||||||
List<String> paramList = new ArrayList<String>();
|
List<String> paramList = new ArrayList<String>();
|
||||||
paramList.add(tenantTmp);
|
paramList.add(tenantTmp);
|
||||||
@ -1944,7 +1950,7 @@ public class PersistService {
|
|||||||
|
|
||||||
public Page<ConfigInfoWrapper> findAllConfigInfoFragment(final long lastMaxId, final int pageSize) {
|
public Page<ConfigInfoWrapper> findAllConfigInfoFragment(final long lastMaxId, final int pageSize) {
|
||||||
String select
|
String select
|
||||||
= "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified from config_info where id > ? "
|
= "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,type from config_info where id > ? "
|
||||||
+ "order by id asc limit ?,?";
|
+ "order by id asc limit ?,?";
|
||||||
PaginationHelper<ConfigInfoWrapper> helper = new PaginationHelper<ConfigInfoWrapper>();
|
PaginationHelper<ConfigInfoWrapper> helper = new PaginationHelper<ConfigInfoWrapper>();
|
||||||
try {
|
try {
|
||||||
@ -2927,8 +2933,8 @@ public class PersistService {
|
|||||||
final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
|
final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
|
||||||
try {
|
try {
|
||||||
return this.jt.queryForObject(
|
return this.jt.queryForObject(
|
||||||
"SELECT ID,data_id,group_id,tenant_id,app_name,content,md5 FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?",
|
"SELECT ID,data_id,group_id,tenant_id,app_name,content,md5,type FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?",
|
||||||
new Object[]{dataId, group, tenantTmp}, CONFIG_INFO_ROW_MAPPER);
|
new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_ROW_MAPPER);
|
||||||
} catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null
|
} catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null
|
||||||
return null;
|
return null;
|
||||||
} catch (CannotGetJdbcConnectionException e) {
|
} catch (CannotGetJdbcConnectionException e) {
|
||||||
@ -3231,36 +3237,6 @@ public class PersistService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public User findUserByUsername(String username) {
|
|
||||||
String sql = "SELECT username,password FROM users WHERE username=? ";
|
|
||||||
try {
|
|
||||||
return this.jt.queryForObject(sql, new Object[]{username}, USER_ROW_MAPPER);
|
|
||||||
} catch (CannotGetJdbcConnectionException e) {
|
|
||||||
fatalLog.error("[db-error] " + e.toString(), e);
|
|
||||||
throw e;
|
|
||||||
} catch (EmptyResultDataAccessException e) {
|
|
||||||
return null;
|
|
||||||
} catch (Exception e) {
|
|
||||||
fatalLog.error("[db-other-error]" + e.getMessage(), e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户密码
|
|
||||||
*/
|
|
||||||
public void updateUserPassword(String username, String password) {
|
|
||||||
try {
|
|
||||||
jt.update(
|
|
||||||
"UPDATE users SET password = ? WHERE username=?",
|
|
||||||
password, username);
|
|
||||||
} catch (CannotGetJdbcConnectionException e) {
|
|
||||||
fatalLog.error("[db-error] " + e.toString(), e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private List<ConfigInfo> convertDeletedConfig(List<Map<String, Object>> list) {
|
private List<ConfigInfo> convertDeletedConfig(List<Map<String, Object>> list) {
|
||||||
List<ConfigInfo> configs = new ArrayList<ConfigInfo>();
|
List<ConfigInfo> configs = new ArrayList<ConfigInfo>();
|
||||||
for (Map<String, Object> map : list) {
|
for (Map<String, Object> map : list) {
|
||||||
@ -3566,7 +3542,7 @@ public class PersistService {
|
|||||||
|
|
||||||
static final TenantInfoRowMapper TENANT_INFO_ROW_MAPPER = new TenantInfoRowMapper();
|
static final TenantInfoRowMapper TENANT_INFO_ROW_MAPPER = new TenantInfoRowMapper();
|
||||||
|
|
||||||
static final UserRowMapper USER_ROW_MAPPER = new UserRowMapper();
|
protected static final UserRowMapper USER_ROW_MAPPER = new UserRowMapper();
|
||||||
|
|
||||||
static final ConfigInfoWrapperRowMapper CONFIG_INFO_WRAPPER_ROW_MAPPER = new ConfigInfoWrapperRowMapper();
|
static final ConfigInfoWrapperRowMapper CONFIG_INFO_WRAPPER_ROW_MAPPER = new ConfigInfoWrapperRowMapper();
|
||||||
|
|
||||||
@ -3599,7 +3575,7 @@ public class PersistService {
|
|||||||
|
|
||||||
private static String PATTERN_STR = "*";
|
private static String PATTERN_STR = "*";
|
||||||
private final static int QUERY_LIMIT_SIZE = 50;
|
private final static int QUERY_LIMIT_SIZE = 50;
|
||||||
private JdbcTemplate jt;
|
protected JdbcTemplate jt;
|
||||||
private TransactionTemplate tjt;
|
protected TransactionTemplate tjt;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ class DumpProcessor implements TaskProcessor {
|
|||||||
|
|
||||||
boolean result;
|
boolean result;
|
||||||
if (null != cf) {
|
if (null != cf) {
|
||||||
result = ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified);
|
result = ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified, cf.getType());
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
|
ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
|
||||||
@ -261,7 +261,7 @@ class DumpAllProcessor implements TaskProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean result = ConfigService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(),
|
boolean result = ConfigService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(),
|
||||||
cf.getLastModified());
|
cf.getLastModified(), cf.getType());
|
||||||
|
|
||||||
final String content = cf.getContent();
|
final String content = cf.getContent();
|
||||||
final String md5 = MD5.getInstance().getMD5String(content);
|
final String md5 = MD5.getInstance().getMD5String(content);
|
||||||
|
@ -76,7 +76,7 @@ public class PaginationHelper<E> {
|
|||||||
page.setTotalCount(rowCountInt);
|
page.setTotalCount(rowCountInt);
|
||||||
|
|
||||||
if (pageNo > pageCount) {
|
if (pageNo > pageCount) {
|
||||||
return null;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int startRow = (pageNo - 1) * pageSize;
|
final int startRow = (pageNo - 1) * pageSize;
|
||||||
@ -121,7 +121,7 @@ public class PaginationHelper<E> {
|
|||||||
page.setTotalCount(rowCountInt);
|
page.setTotalCount(rowCountInt);
|
||||||
|
|
||||||
if (pageNo > pageCount) {
|
if (pageNo > pageCount) {
|
||||||
return null;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
String selectSQL = sqlFetchRows;
|
String selectSQL = sqlFetchRows;
|
||||||
@ -162,7 +162,7 @@ public class PaginationHelper<E> {
|
|||||||
page.setTotalCount(rowCountInt);
|
page.setTotalCount(rowCountInt);
|
||||||
|
|
||||||
if (pageNo > pageCount) {
|
if (pageNo > pageCount) {
|
||||||
return null;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
String selectSQL = sqlFetchRows;
|
String selectSQL = sqlFetchRows;
|
||||||
|
@ -19,7 +19,6 @@ import org.apache.commons.lang3.time.FastDateFormat;
|
|||||||
|
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time util
|
* Time util
|
||||||
@ -29,13 +28,12 @@ import java.util.Date;
|
|||||||
public class TimeUtils {
|
public class TimeUtils {
|
||||||
|
|
||||||
public static Timestamp getCurrentTime() {
|
public static Timestamp getCurrentTime() {
|
||||||
Date date = new Date();
|
return new Timestamp(System.currentTimeMillis());
|
||||||
return new Timestamp(date.getTime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getCurrentTimeStr() {
|
public static String getCurrentTimeStr() {
|
||||||
Calendar c = Calendar.getInstance();
|
Calendar c = Calendar.getInstance();
|
||||||
c.setTime(new Date());
|
c.setTimeInMillis(System.currentTimeMillis());
|
||||||
FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
|
FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
|
||||||
return format.format(c.getTime());
|
return format.format(c.getTime());
|
||||||
}
|
}
|
||||||
|
0
config/src/main/resources/META-INF/logback/nacos-included.xml → config/src/main/resources/META-INF/logback/config-included.xml
Executable file → Normal file
0
config/src/main/resources/META-INF/logback/nacos-included.xml → config/src/main/resources/META-INF/logback/config-included.xml
Executable file → Normal file
@ -189,6 +189,13 @@ CREATE TABLE roles (
|
|||||||
role varchar(50) NOT NULL
|
role varchar(50) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE permissions (
|
||||||
|
role varchar(50) NOT NULL,
|
||||||
|
resource varchar(512) NOT NULL,
|
||||||
|
action varchar(8) NOT NULL,
|
||||||
|
constraint uk_role_permission UNIQUE (role,resource,action)
|
||||||
|
);
|
||||||
|
|
||||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||||
|
|
||||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||||
|
@ -184,6 +184,13 @@ CREATE TABLE roles (
|
|||||||
role varchar(50) NOT NULL
|
role varchar(50) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE permissions (
|
||||||
|
role varchar(50) NOT NULL,
|
||||||
|
resource varchar(512) NOT NULL,
|
||||||
|
action varchar(8) NOT NULL,
|
||||||
|
constraint uk_role_permission UNIQUE (role,resource,action)
|
||||||
|
);
|
||||||
|
|
||||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||||
|
|
||||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||||
|
@ -13,24 +13,40 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.alibaba.nacos.console.config;
|
package com.alibaba.nacos.console.config;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.core.code.ControllerMethodsCache;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
import org.springframework.web.filter.CorsFilter;
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
/**
|
import javax.annotation.PostConstruct;
|
||||||
* Spring cors config
|
|
||||||
*
|
|
||||||
* @author yshen
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class CorsConfig {
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yshen
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@EnableScheduling
|
||||||
|
@PropertySource("/application.properties")
|
||||||
|
public class ConsoleConfig {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ControllerMethodsCache methodsCache;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
methodsCache.initClassMethod("com.alibaba.nacos.naming.controllers");
|
||||||
|
methodsCache.initClassMethod("com.alibaba.nacos.console.controller");
|
||||||
|
methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller");
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public CorsFilter corsFilter() {
|
public CorsFilter corsFilter() {
|
||||||
@ -44,5 +60,4 @@ public class CorsConfig {
|
|||||||
source.registerCorsConfiguration("/**", config);
|
source.registerCorsConfiguration("/**", config);
|
||||||
return new CorsFilter(source);
|
return new CorsFilter(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
|
||||||
*
|
|
||||||
* Licensed 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 com.alibaba.nacos.console.controller;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.config.server.model.RestResult;
|
|
||||||
import com.alibaba.nacos.console.config.WebSecurityConfig;
|
|
||||||
import com.alibaba.nacos.console.security.CustomUserDetailsServiceImpl;
|
|
||||||
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
|
||||||
import com.alibaba.nacos.console.utils.PasswordEncoderUtil;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* auth
|
|
||||||
*
|
|
||||||
* @author wfnuser
|
|
||||||
*/
|
|
||||||
@RestController("auth")
|
|
||||||
@RequestMapping("/v1/auth")
|
|
||||||
public class AuthController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JwtTokenUtils jwtTokenUtils;
|
|
||||||
@Autowired
|
|
||||||
private AuthenticationManager authenticationManager;
|
|
||||||
@Autowired
|
|
||||||
private CustomUserDetailsServiceImpl userDetailsService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the Nacos is in broken states or not, and cannot recover except by being restarted
|
|
||||||
*
|
|
||||||
* @return HTTP code equal to 200 indicates that Nacos is in right states. HTTP code equal to 500 indicates that
|
|
||||||
* Nacos is in broken states.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@PostMapping("login")
|
|
||||||
public RestResult<String> login(@RequestParam String username, @RequestParam String password, HttpServletResponse response) {
|
|
||||||
|
|
||||||
// 通过用户名和密码创建一个 Authentication 认证对象,实现类为 UsernamePasswordAuthenticationToken
|
|
||||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
|
||||||
RestResult<String> rr = new RestResult<String>();
|
|
||||||
|
|
||||||
try {
|
|
||||||
//通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证 Authentication 对象
|
|
||||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
|
||||||
//将 Authentication 绑定到 SecurityContext
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
|
||||||
//生成Token
|
|
||||||
String token = jwtTokenUtils.createToken(authentication);
|
|
||||||
//将Token写入到Http头部
|
|
||||||
response.addHeader(WebSecurityConfig.AUTHORIZATION_HEADER, "Bearer " + token);
|
|
||||||
rr.setCode(200);
|
|
||||||
rr.setData("Bearer " + token);
|
|
||||||
return rr;
|
|
||||||
} catch (BadCredentialsException authentication) {
|
|
||||||
rr.setCode(401);
|
|
||||||
rr.setMessage("Login failed");
|
|
||||||
return rr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@PutMapping("password")
|
|
||||||
public RestResult<String> updatePassword(@RequestParam(value = "oldPassword") String oldPassword,
|
|
||||||
@RequestParam(value = "newPassword") String newPassword) {
|
|
||||||
|
|
||||||
RestResult<String> rr = new RestResult<String>();
|
|
||||||
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
|
||||||
String username = ((UserDetails) principal).getUsername();
|
|
||||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
|
||||||
String password = userDetails.getPassword();
|
|
||||||
|
|
||||||
// TODO: throw out more fine grained exceptions
|
|
||||||
try {
|
|
||||||
if (PasswordEncoderUtil.matches(oldPassword, password)) {
|
|
||||||
userDetailsService.updateUserPassword(username, PasswordEncoderUtil.encode(newPassword));
|
|
||||||
rr.setCode(200);
|
|
||||||
rr.setMessage("Update password success");
|
|
||||||
} else {
|
|
||||||
rr.setCode(401);
|
|
||||||
rr.setMessage("Old password is invalid");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
rr.setCode(500);
|
|
||||||
rr.setMessage("Update userpassword failed");
|
|
||||||
}
|
|
||||||
return rr;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.model.RestResult;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.roles.NacosRoleServiceImpl;
|
||||||
|
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||||
|
import com.alibaba.nacos.core.auth.Secured;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission operation controller
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/v1/auth/permissions")
|
||||||
|
public class PermissionController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosRoleServiceImpl nacosRoleService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query permissions of a role
|
||||||
|
*
|
||||||
|
* @param role the role
|
||||||
|
* @param pageNo page index
|
||||||
|
* @param pageSize page size
|
||||||
|
* @return permission of a role
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "permissions", action = ActionTypes.READ)
|
||||||
|
public Object getPermissions(@RequestParam int pageNo, @RequestParam int pageSize,
|
||||||
|
@RequestParam(name = "role", defaultValue = StringUtils.EMPTY) String role) {
|
||||||
|
return nacosRoleService.getPermissionsFromDatabase(role, pageNo, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a permission to a role
|
||||||
|
*
|
||||||
|
* @param role the role
|
||||||
|
* @param resource the related resource
|
||||||
|
* @param action the related action
|
||||||
|
* @return ok if succeed
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "permissions", action = ActionTypes.WRITE)
|
||||||
|
public Object addPermission(@RequestParam String role, @RequestParam String resource, @RequestParam String action) {
|
||||||
|
nacosRoleService.addPermission(role, resource, action);
|
||||||
|
return new RestResult<>(200, "add permission ok!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a permission from a role
|
||||||
|
*
|
||||||
|
* @param role the role
|
||||||
|
* @param resource the related resource
|
||||||
|
* @param action the related action
|
||||||
|
* @return ok if succeed
|
||||||
|
*/
|
||||||
|
@DeleteMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "permissions", action = ActionTypes.WRITE)
|
||||||
|
public Object deletePermission(@RequestParam String role, @RequestParam String resource, @RequestParam String action) {
|
||||||
|
nacosRoleService.deletePermission(role, resource, action);
|
||||||
|
return new RestResult<>(200, "delete permission ok!");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.model.RestResult;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.roles.NacosRoleServiceImpl;
|
||||||
|
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||||
|
import com.alibaba.nacos.core.auth.Secured;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Role operation controller
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/v1/auth/roles")
|
||||||
|
public class RoleController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosRoleServiceImpl roleService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get roles list
|
||||||
|
*
|
||||||
|
* @param pageNo number index of page
|
||||||
|
* @param pageSize page size
|
||||||
|
* @param username optional, username of user
|
||||||
|
* @return role list
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.READ)
|
||||||
|
public Object getRoles(@RequestParam int pageNo, @RequestParam int pageSize,
|
||||||
|
@RequestParam(name = "username", defaultValue = "") String username) {
|
||||||
|
return roleService.getRolesFromDatabase(username, pageNo, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a role to a user
|
||||||
|
* <p>
|
||||||
|
* This method is used for 2 functions:
|
||||||
|
* 1. create a role and bind it to GLOBAL_ADMIN.
|
||||||
|
* 2. bind a role to an user.
|
||||||
|
*
|
||||||
|
* @param role
|
||||||
|
* @param username
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.WRITE)
|
||||||
|
public Object addRole(@RequestParam String role, @RequestParam String username) {
|
||||||
|
roleService.addRole(role, username);
|
||||||
|
return new RestResult<>(200, "add role ok!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a role. If no username is specified, all users under this role are deleted
|
||||||
|
*
|
||||||
|
* @param role role
|
||||||
|
* @param username username
|
||||||
|
* @return ok if succeed
|
||||||
|
*/
|
||||||
|
@DeleteMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "roles", action = ActionTypes.WRITE)
|
||||||
|
public Object deleteRole(@RequestParam String role,
|
||||||
|
@RequestParam(name = "username", defaultValue = StringUtils.EMPTY) String username) {
|
||||||
|
if (StringUtils.isBlank(username)) {
|
||||||
|
roleService.deleteRole(role);
|
||||||
|
} else {
|
||||||
|
roleService.deleteRole(role, username);
|
||||||
|
}
|
||||||
|
return new RestResult<>(200, "delete role of user " + username + " ok!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.controller;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
|
import com.alibaba.nacos.config.server.model.RestResult;
|
||||||
|
import com.alibaba.nacos.config.server.model.User;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.NacosAuthManager;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.users.NacosUser;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||||
|
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
||||||
|
import com.alibaba.nacos.console.utils.PasswordEncoderUtil;
|
||||||
|
import com.alibaba.nacos.core.auth.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User related methods entry
|
||||||
|
*
|
||||||
|
* @author wfnuser
|
||||||
|
* @author nkorange
|
||||||
|
*/
|
||||||
|
@RestController("user")
|
||||||
|
@RequestMapping({"/v1/auth", "/v1/auth/users"})
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtTokenUtils jwtTokenUtils;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosUserDetailsServiceImpl userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthConfigs authConfigs;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosAuthManager authManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new user
|
||||||
|
*
|
||||||
|
* @param username username
|
||||||
|
* @param password password
|
||||||
|
* @return ok if create succeed
|
||||||
|
* @throws IllegalArgumentException if user already exist
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.WRITE)
|
||||||
|
@PostMapping
|
||||||
|
public Object createUser(@RequestParam String username, @RequestParam String password) {
|
||||||
|
|
||||||
|
User user = userDetailsService.getUserFromDatabase(username);
|
||||||
|
if (user != null) {
|
||||||
|
throw new IllegalArgumentException("user '" + username + "' already exist!");
|
||||||
|
}
|
||||||
|
userDetailsService.createUser(username, PasswordEncoderUtil.encode(password));
|
||||||
|
return new RestResult<>(200, "create user ok!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an existed user
|
||||||
|
*
|
||||||
|
* @param username username of user
|
||||||
|
* @return ok if deleted succeed, keep silent if user not exist
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@DeleteMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.WRITE)
|
||||||
|
public Object deleteUser(@RequestParam String username) {
|
||||||
|
|
||||||
|
userDetailsService.deleteUser(username);
|
||||||
|
return new RestResult<>(200, "delete user ok!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an user
|
||||||
|
*
|
||||||
|
* @param username username of user
|
||||||
|
* @param newPassword new password of user
|
||||||
|
* @return ok if update succeed
|
||||||
|
* @throws IllegalArgumentException if user not exist or oldPassword is incorrect
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@PutMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.WRITE)
|
||||||
|
public Object updateUser(@RequestParam String username, @RequestParam String newPassword) {
|
||||||
|
|
||||||
|
User user = userDetailsService.getUserFromDatabase(username);
|
||||||
|
if (user == null) {
|
||||||
|
throw new IllegalArgumentException("user " + username + " not exist!");
|
||||||
|
}
|
||||||
|
|
||||||
|
userDetailsService.updateUserPassword(username, PasswordEncoderUtil.encode(newPassword));
|
||||||
|
|
||||||
|
return new RestResult<>(200, "update user ok!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get paged users
|
||||||
|
*
|
||||||
|
* @param pageNo number index of page
|
||||||
|
* @param pageSize size of page
|
||||||
|
* @return A collection of users, empty set if no user is found
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.READ)
|
||||||
|
public Object getUsers(@RequestParam int pageNo, @RequestParam int pageSize) {
|
||||||
|
return userDetailsService.getUsersFromDatabase(pageNo, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login to Nacos
|
||||||
|
* <p>
|
||||||
|
* This methods uses username and password to require a new token.
|
||||||
|
*
|
||||||
|
* @param username username of user
|
||||||
|
* @param password password
|
||||||
|
* @param response http response
|
||||||
|
* @param request http request
|
||||||
|
* @return new token of the user
|
||||||
|
* @throws AccessException if user info is incorrect
|
||||||
|
*/
|
||||||
|
@PostMapping("/login")
|
||||||
|
public Object login(@RequestParam String username, @RequestParam String password,
|
||||||
|
HttpServletResponse response, HttpServletRequest request) throws AccessException {
|
||||||
|
|
||||||
|
|
||||||
|
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
|
||||||
|
NacosUser user = (NacosUser) authManager.login(request);
|
||||||
|
|
||||||
|
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER,
|
||||||
|
NacosAuthConfig.TOKEN_PREFIX + user.getToken());
|
||||||
|
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
result.put(Constants.ACCESS_TOKEN, user.getToken());
|
||||||
|
result.put(Constants.TOKEN_TTL, authConfigs.getTokenValidityInSeconds());
|
||||||
|
result.put(Constants.GLOBAL_ADMIN, user.isGlobalAdmin());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过用户名和密码创建一个 Authentication 认证对象,实现类为 UsernamePasswordAuthenticationToken
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
|
||||||
|
|
||||||
|
RestResult<String> rr = new RestResult<String>();
|
||||||
|
try {
|
||||||
|
//通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证 Authentication 对象
|
||||||
|
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||||
|
//将 Authentication 绑定到 SecurityContext
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
//生成Token
|
||||||
|
String token = jwtTokenUtils.createToken(authentication);
|
||||||
|
//将Token写入到Http头部
|
||||||
|
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER, "Bearer " + token);
|
||||||
|
rr.setCode(200);
|
||||||
|
rr.setData("Bearer " + token);
|
||||||
|
return rr;
|
||||||
|
} catch (BadCredentialsException authentication) {
|
||||||
|
rr.setCode(401);
|
||||||
|
rr.setMessage("Login failed");
|
||||||
|
return rr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/password")
|
||||||
|
@Deprecated
|
||||||
|
public RestResult<String> updatePassword(@RequestParam(value = "oldPassword") String oldPassword,
|
||||||
|
@RequestParam(value = "newPassword") String newPassword) {
|
||||||
|
|
||||||
|
RestResult<String> rr = new RestResult<String>();
|
||||||
|
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
String username = ((UserDetails) principal).getUsername();
|
||||||
|
User user = userDetailsService.getUserFromDatabase(username);
|
||||||
|
String password = user.getPassword();
|
||||||
|
|
||||||
|
// TODO: throw out more fine grained exceptions
|
||||||
|
try {
|
||||||
|
if (PasswordEncoderUtil.matches(oldPassword, password)) {
|
||||||
|
userDetailsService.updateUserPassword(username, PasswordEncoderUtil.encode(newPassword));
|
||||||
|
rr.setCode(200);
|
||||||
|
rr.setMessage("Update password success");
|
||||||
|
} else {
|
||||||
|
rr.setCode(401);
|
||||||
|
rr.setMessage("Old password is invalid");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
rr.setCode(500);
|
||||||
|
rr.setMessage("Update userpassword failed");
|
||||||
|
}
|
||||||
|
return rr;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.exception;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.core.auth.AccessException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception handler for console module
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@ControllerAdvice
|
||||||
|
public class ConsoleExceptionHandler {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ConsoleExceptionHandler.class);
|
||||||
|
|
||||||
|
@ExceptionHandler(AccessException.class)
|
||||||
|
private ResponseEntity<String> handleAccessException(AccessException e) {
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(IllegalArgumentException.class)
|
||||||
|
private ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
|
||||||
|
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
private ResponseEntity<String> handleException(Exception e) {
|
||||||
|
logger.error("CONSOLE", e);
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -15,11 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.console.filter;
|
package com.alibaba.nacos.console.filter;
|
||||||
|
|
||||||
import com.alibaba.nacos.console.config.WebSecurityConfig;
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
import com.alibaba.nacos.console.security.nacos.JwtTokenManager;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
@ -37,29 +38,23 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
private static final String TOKEN_PREFIX = "Bearer ";
|
private static final String TOKEN_PREFIX = "Bearer ";
|
||||||
|
|
||||||
private JwtTokenUtils tokenProvider;
|
private JwtTokenManager tokenManager;
|
||||||
|
|
||||||
public JwtAuthenticationTokenFilter(JwtTokenUtils tokenProvider) {
|
public JwtAuthenticationTokenFilter(JwtTokenManager tokenManager) {
|
||||||
this.tokenProvider = tokenProvider;
|
this.tokenManager = tokenManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
String jwt = resolveToken(request);
|
String jwt = resolveToken(request);
|
||||||
|
|
||||||
if (jwt != null && !"".equals(jwt.trim()) && SecurityContextHolder.getContext().getAuthentication() == null) {
|
if (StringUtils.isNotBlank(jwt) && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
if (this.tokenProvider.validateToken(jwt)) {
|
this.tokenManager.validateToken(jwt);
|
||||||
/**
|
Authentication authentication = this.tokenManager.getAuthentication(jwt);
|
||||||
* get auth info
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
*/
|
|
||||||
Authentication authentication = this.tokenProvider.getAuthentication(jwt);
|
|
||||||
/**
|
|
||||||
* save user info to securityContext
|
|
||||||
*/
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,12 +62,12 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
|||||||
* Get token from header
|
* Get token from header
|
||||||
*/
|
*/
|
||||||
private String resolveToken(HttpServletRequest request) {
|
private String resolveToken(HttpServletRequest request) {
|
||||||
String bearerToken = request.getHeader(WebSecurityConfig.AUTHORIZATION_HEADER);
|
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
|
||||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
||||||
return bearerToken.substring(7, bearerToken.length());
|
return bearerToken.substring(7);
|
||||||
}
|
}
|
||||||
String jwt = request.getParameter(WebSecurityConfig.AUTHORIZATION_TOKEN);
|
String jwt = request.getParameter(Constants.ACCESS_TOKEN);
|
||||||
if (StringUtils.hasText(jwt)) {
|
if (StringUtils.isNotBlank(jwt)) {
|
||||||
return jwt;
|
return jwt;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
|
||||||
*
|
|
||||||
* Licensed 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 com.alibaba.nacos.console.security;
|
|
||||||
|
|
||||||
|
|
||||||
import com.alibaba.nacos.config.server.model.User;
|
|
||||||
import com.alibaba.nacos.config.server.service.PersistService;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custem user service
|
|
||||||
*
|
|
||||||
* @author wfnuser
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class CustomUserDetailsServiceImpl implements UserDetailsService {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private transient PersistService persistService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
|
|
||||||
User user = persistService.findUserByUsername(userName);
|
|
||||||
if (user == null) {
|
|
||||||
throw new UsernameNotFoundException(userName);
|
|
||||||
}
|
|
||||||
return new CustomUserDetails(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateUserPassword(String username, String password) throws Exception {
|
|
||||||
persistService.updateUserPassword(username, password);
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,8 +13,9 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.console.security;
|
package com.alibaba.nacos.console.security.nacos;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
@ -32,7 +33,7 @@ import org.springframework.stereotype.Component;
|
|||||||
public class CustomAuthenticationProvider implements AuthenticationProvider {
|
public class CustomAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CustomUserDetailsServiceImpl userDetailsService;
|
private NacosUserDetailsServiceImpl userDetailsService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.console.security;
|
package com.alibaba.nacos.console.security.nacos;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -37,10 +37,10 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
|||||||
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
|
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commence(HttpServletRequest httpServletRequest,
|
public void commence(HttpServletRequest request,
|
||||||
HttpServletResponse httpServletResponse,
|
HttpServletResponse response,
|
||||||
AuthenticationException e) throws IOException, ServletException {
|
AuthenticationException e) throws IOException, ServletException {
|
||||||
logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
|
logger.error("Responding with unauthorized error. Message:{}, url:{}", e.getMessage(), request.getRequestURI());
|
||||||
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.security.nacos;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT token manager
|
||||||
|
*
|
||||||
|
* @author wfnuser
|
||||||
|
* @author nkorange
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtTokenManager {
|
||||||
|
|
||||||
|
private static final String AUTHORITIES_KEY = "auth";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthConfigs authConfigs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create token
|
||||||
|
*
|
||||||
|
* @param authentication auth info
|
||||||
|
* @return token
|
||||||
|
*/
|
||||||
|
public String createToken(Authentication authentication) {
|
||||||
|
return createToken(authentication.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createToken(String userName) {
|
||||||
|
|
||||||
|
long now = (new Date()).getTime();
|
||||||
|
|
||||||
|
Date validity;
|
||||||
|
validity = new Date(now + authConfigs.getTokenValidityInSeconds() * 1000L);
|
||||||
|
|
||||||
|
Claims claims = Jwts.claims().setSubject(userName);
|
||||||
|
|
||||||
|
return Jwts.builder()
|
||||||
|
.setClaims(claims)
|
||||||
|
.setExpiration(validity)
|
||||||
|
.signWith(SignatureAlgorithm.HS256, authConfigs.getSecretKey())
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get auth Info
|
||||||
|
*
|
||||||
|
* @param token token
|
||||||
|
* @return auth info
|
||||||
|
*/
|
||||||
|
public Authentication getAuthentication(String token) {
|
||||||
|
/**
|
||||||
|
* parse the payload of token
|
||||||
|
*/
|
||||||
|
Claims claims = Jwts.parser()
|
||||||
|
.setSigningKey(authConfigs.getSecretKey())
|
||||||
|
.parseClaimsJws(token)
|
||||||
|
.getBody();
|
||||||
|
|
||||||
|
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY));
|
||||||
|
|
||||||
|
User principal = new User(claims.getSubject(), "", authorities);
|
||||||
|
return new UsernamePasswordAuthenticationToken(principal, "", authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* validate token
|
||||||
|
*
|
||||||
|
* @param token token
|
||||||
|
* @return whether valid
|
||||||
|
*/
|
||||||
|
public void validateToken(String token) {
|
||||||
|
Jwts.parser().setSigningKey(authConfigs.getSecretKey()).parseClaimsJws(token);
|
||||||
|
}
|
||||||
|
}
|
@ -13,12 +13,14 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.console.config;
|
package com.alibaba.nacos.console.security.nacos;
|
||||||
|
|
||||||
|
|
||||||
import com.alibaba.nacos.console.filter.JwtAuthenticationTokenFilter;
|
import com.alibaba.nacos.console.filter.JwtAuthenticationTokenFilter;
|
||||||
import com.alibaba.nacos.console.security.CustomUserDetailsServiceImpl;
|
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||||
import com.alibaba.nacos.console.security.JwtAuthenticationEntryPoint;
|
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||||
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
import com.alibaba.nacos.core.auth.AuthSystemTypes;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -43,64 +45,95 @@ import org.springframework.web.cors.CorsUtils;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||||
|
|
||||||
public static final String AUTHORIZATION_TOKEN = "access_token";
|
|
||||||
|
|
||||||
public static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ",";
|
public static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ",";
|
||||||
|
|
||||||
@Autowired
|
public static final String LOGIN_ENTRY_POINT = "/v1/auth/login";
|
||||||
private CustomUserDetailsServiceImpl userDetailsService;
|
|
||||||
|
|
||||||
@Autowired
|
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/v1/auth/**";
|
||||||
private JwtAuthenticationEntryPoint unauthorizedHandler;
|
|
||||||
|
|
||||||
@Autowired
|
public static final String TOKEN_PREFIX = "Bearer ";
|
||||||
private JwtTokenUtils tokenProvider;
|
|
||||||
|
public static final String CONSOLE_RESOURCE_NAME_PREFIX = "console/";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Environment env;
|
private Environment env;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtTokenManager tokenProvider;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthConfigs authConfigs;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosUserDetailsServiceImpl userDetailsService;
|
||||||
|
|
||||||
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
|
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||||
return super.authenticationManagerBean();
|
return super.authenticationManagerBean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) {
|
||||||
|
|
||||||
|
String ignoreURLs = null;
|
||||||
|
//
|
||||||
|
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
|
||||||
|
ignoreURLs = "/**";
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if (StringUtils.isBlank(authConfigs.getNacosAuthSystemType())) {
|
||||||
|
ignoreURLs = env.getProperty("nacos.security.ignore.urls", "/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(ignoreURLs)) {
|
||||||
|
for (String ignoreURL : ignoreURLs.trim().split(SECURITY_IGNORE_URLS_SPILT_CHAR)) {
|
||||||
|
web.ignoring().antMatchers(ignoreURL.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configure(WebSecurity web) {
|
|
||||||
String ignoreURLs = env.getProperty("nacos.security.ignore.urls", "/**");
|
|
||||||
for (String ignoreURL : ignoreURLs.trim().split(SECURITY_IGNORE_URLS_SPILT_CHAR)) {
|
|
||||||
web.ignoring().antMatchers(ignoreURL.trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http
|
|
||||||
//open cors
|
|
||||||
.cors().and()
|
|
||||||
.authorizeRequests()
|
|
||||||
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
|
|
||||||
.anyRequest().authenticated().and()
|
|
||||||
// custom token authorize exception handler
|
|
||||||
.exceptionHandling()
|
|
||||||
.authenticationEntryPoint(unauthorizedHandler).and()
|
|
||||||
// since we use jwt, session is not necessary
|
|
||||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
|
||||||
// since we use jwt, csrf is not necessary
|
|
||||||
.csrf().disable();
|
|
||||||
http.addFilterBefore(new JwtAuthenticationTokenFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
|
|
||||||
|
|
||||||
// disable cache
|
if (StringUtils.isBlank(authConfigs.getNacosAuthSystemType())) {
|
||||||
http.headers().cacheControl();
|
http
|
||||||
|
|
||||||
|
.csrf().disable()
|
||||||
|
.cors() // We don't need CSRF for JWT based authentication
|
||||||
|
|
||||||
|
.and()
|
||||||
|
.sessionManagement()
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
|
||||||
|
.and()
|
||||||
|
.authorizeRequests()
|
||||||
|
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
|
||||||
|
.antMatchers(LOGIN_ENTRY_POINT).permitAll()
|
||||||
|
|
||||||
|
.and()
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated()
|
||||||
|
|
||||||
|
.and()
|
||||||
|
.exceptionHandling()
|
||||||
|
.authenticationEntryPoint(new JwtAuthenticationEntryPoint());
|
||||||
|
|
||||||
|
// disable cache
|
||||||
|
http.headers().cacheControl();
|
||||||
|
|
||||||
|
http.addFilterBefore(new JwtAuthenticationTokenFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.security.nacos;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
|
import com.alibaba.nacos.config.server.auth.RoleInfo;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.roles.NacosRoleServiceImpl;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.users.NacosUser;
|
||||||
|
import com.alibaba.nacos.core.auth.AccessException;
|
||||||
|
import com.alibaba.nacos.core.auth.AuthManager;
|
||||||
|
import com.alibaba.nacos.core.auth.Permission;
|
||||||
|
import com.alibaba.nacos.core.auth.User;
|
||||||
|
import com.alibaba.nacos.core.utils.Loggers;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builtin access control entry of Nacos
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class NacosAuthManager implements AuthManager {
|
||||||
|
|
||||||
|
private static final String TOKEN_PREFIX = "Bearer ";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtTokenManager tokenManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosRoleServiceImpl roleService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User login(Object request) throws AccessException {
|
||||||
|
HttpServletRequest req = (HttpServletRequest) request;
|
||||||
|
String token = resolveToken(req);
|
||||||
|
if (StringUtils.isBlank(token)) {
|
||||||
|
throw new AccessException("user not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
tokenManager.validateToken(token);
|
||||||
|
} catch (ExpiredJwtException e) {
|
||||||
|
throw new AccessException("token expired!");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AccessException("token invalid!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Authentication authentication = tokenManager.getAuthentication(token);
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
|
||||||
|
String username = authentication.getName();
|
||||||
|
NacosUser user = new NacosUser();
|
||||||
|
user.setUserName(username);
|
||||||
|
user.setToken(token);
|
||||||
|
List<RoleInfo> roleInfoList = roleService.getRoles(username);
|
||||||
|
for (RoleInfo roleInfo : roleInfoList) {
|
||||||
|
if (roleInfo.getRole().equals(NacosRoleServiceImpl.GLOBAL_ADMIN_ROLE)) {
|
||||||
|
user.setGlobalAdmin(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void auth(Permission permission, User user) throws AccessException {
|
||||||
|
if (Loggers.AUTH.isDebugEnabled()) {
|
||||||
|
Loggers.AUTH.debug("auth permission: {}, user: {}", permission, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!roleService.hasPermission(user.getUserName(), permission)) {
|
||||||
|
throw new AccessException("authorization failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get token from header
|
||||||
|
*/
|
||||||
|
private String resolveToken(HttpServletRequest request) throws AccessException {
|
||||||
|
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
|
||||||
|
if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
||||||
|
return bearerToken.substring(7);
|
||||||
|
}
|
||||||
|
bearerToken = request.getParameter(Constants.ACCESS_TOKEN);
|
||||||
|
if (StringUtils.isBlank(bearerToken)) {
|
||||||
|
String userName = request.getParameter("username");
|
||||||
|
String password = request.getParameter("password");
|
||||||
|
bearerToken = resolveTokenFromUser(userName, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bearerToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveTokenFromUser(String userName, String rawPassword) throws AccessException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, rawPassword);
|
||||||
|
authenticationManager.authenticate(authenticationToken);
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
throw new AccessException("unknown user!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenManager.createToken(userName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.security.nacos.roles;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.auth.PermissionInfo;
|
||||||
|
import com.alibaba.nacos.config.server.auth.PermissionPersistService;
|
||||||
|
import com.alibaba.nacos.config.server.auth.RoleInfo;
|
||||||
|
import com.alibaba.nacos.config.server.auth.RolePersistService;
|
||||||
|
import com.alibaba.nacos.config.server.model.Page;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
|
||||||
|
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||||
|
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||||
|
import com.alibaba.nacos.core.auth.Permission;
|
||||||
|
import com.alibaba.nacos.core.utils.Loggers;
|
||||||
|
import io.jsonwebtoken.lang.Collections;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nacos builtin role service.
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class NacosRoleServiceImpl {
|
||||||
|
|
||||||
|
public static final String GLOBAL_ADMIN_ROLE = "GLOBAL_ADMIN";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthConfigs authConfigs;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RolePersistService rolePersistService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosUserDetailsServiceImpl userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PermissionPersistService permissionPersistService;
|
||||||
|
|
||||||
|
private Map<String, List<RoleInfo>> roleInfoMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Map<String, List<PermissionInfo>> permissionInfoMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Scheduled(initialDelay = 5000, fixedDelay = 15000)
|
||||||
|
private void reload() {
|
||||||
|
try {
|
||||||
|
Page<RoleInfo> roleInfoPage = rolePersistService.getRolesByUserName(StringUtils.EMPTY, 1, Integer.MAX_VALUE);
|
||||||
|
if (roleInfoPage == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<String> roleSet = new HashSet<>(16);
|
||||||
|
Map<String, List<RoleInfo>> tmpRoleInfoMap = new ConcurrentHashMap<>(16);
|
||||||
|
for (RoleInfo roleInfo : roleInfoPage.getPageItems()) {
|
||||||
|
if (!tmpRoleInfoMap.containsKey(roleInfo.getUsername())) {
|
||||||
|
tmpRoleInfoMap.put(roleInfo.getUsername(), new ArrayList<>());
|
||||||
|
}
|
||||||
|
tmpRoleInfoMap.get(roleInfo.getUsername()).add(roleInfo);
|
||||||
|
roleSet.add(roleInfo.getRole());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<PermissionInfo>> tmpPermissionInfoMap = new ConcurrentHashMap<>(16);
|
||||||
|
for (String role : roleSet) {
|
||||||
|
Page<PermissionInfo> permissionInfoPage = permissionPersistService.getPermissions(role, 1, Integer.MAX_VALUE);
|
||||||
|
tmpPermissionInfoMap.put(role, permissionInfoPage.getPageItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
roleInfoMap = tmpRoleInfoMap;
|
||||||
|
permissionInfoMap = tmpPermissionInfoMap;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Loggers.AUTH.warn("[LOAD-ROLES] load failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the user has permission of the resource.
|
||||||
|
* <p>
|
||||||
|
* Note if the user has many roles, this method returns true if any one role of the user has the
|
||||||
|
* desired permission.
|
||||||
|
*
|
||||||
|
* @param username user info
|
||||||
|
* @param permission permission to auth
|
||||||
|
* @return true if granted, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean hasPermission(String username, Permission permission) {
|
||||||
|
|
||||||
|
List<RoleInfo> roleInfoList = getRoles(username);
|
||||||
|
if (Collections.isEmpty(roleInfoList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global admin pass:
|
||||||
|
for (RoleInfo roleInfo : roleInfoList) {
|
||||||
|
if (GLOBAL_ADMIN_ROLE.equals(roleInfo.getRole())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old global admin can pass resource 'console/':
|
||||||
|
if (permission.getResource().startsWith(NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other roles, use a pattern match to decide if pass or not.
|
||||||
|
for (RoleInfo roleInfo : roleInfoList) {
|
||||||
|
List<PermissionInfo> permissionInfoList = getPermissions(roleInfo.getRole());
|
||||||
|
if (Collections.isEmpty(permissionInfoList)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (PermissionInfo permissionInfo : permissionInfoList) {
|
||||||
|
String permissionResource = permissionInfo.getResource().replaceAll("\\*", ".*");
|
||||||
|
String permissionAction = permissionInfo.getAction();
|
||||||
|
if (permissionAction.contains(permission.getAction()) &&
|
||||||
|
Pattern.matches(permissionResource, permission.getResource())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RoleInfo> getRoles(String username) {
|
||||||
|
List<RoleInfo> roleInfoList = roleInfoMap.get(username);
|
||||||
|
if (!authConfigs.isCachingEnabled()) {
|
||||||
|
Page<RoleInfo> roleInfoPage = getRolesFromDatabase(username, 1, Integer.MAX_VALUE);
|
||||||
|
if (roleInfoPage != null) {
|
||||||
|
roleInfoList = roleInfoPage.getPageItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return roleInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<RoleInfo> getRolesFromDatabase(String userName, int pageNo, int pageSize) {
|
||||||
|
Page<RoleInfo> roles = rolePersistService.getRolesByUserName(userName, pageNo, pageSize);
|
||||||
|
if (roles == null) {
|
||||||
|
return new Page<>();
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PermissionInfo> getPermissions(String role) {
|
||||||
|
List<PermissionInfo> permissionInfoList = permissionInfoMap.get(role);
|
||||||
|
if (!authConfigs.isCachingEnabled()) {
|
||||||
|
Page<PermissionInfo> permissionInfoPage = getPermissionsFromDatabase(role, 1, Integer.MAX_VALUE);
|
||||||
|
if (permissionInfoPage != null) {
|
||||||
|
permissionInfoList = permissionInfoPage.getPageItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return permissionInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<PermissionInfo> getPermissionsByRoleFromDatabase(String role, int pageNo, int pageSize) {
|
||||||
|
return permissionPersistService.getPermissions(role, pageNo, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRole(String role, String username) {
|
||||||
|
if (userDetailsService.getUser(username) == null) {
|
||||||
|
throw new IllegalArgumentException("user '" + username + "' not found!");
|
||||||
|
}
|
||||||
|
rolePersistService.addRole(role, username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteRole(String role, String userName) {
|
||||||
|
rolePersistService.deleteRole(role, userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteRole(String role) {
|
||||||
|
|
||||||
|
rolePersistService.deleteRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<PermissionInfo> getPermissionsFromDatabase(String role, int pageNo, int pageSize) {
|
||||||
|
Page<PermissionInfo> pageInfo = permissionPersistService.getPermissions(role, pageNo, pageSize);
|
||||||
|
if (pageInfo == null) {
|
||||||
|
return new Page<>();
|
||||||
|
}
|
||||||
|
return pageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPermission(String role, String resource, String action) {
|
||||||
|
permissionPersistService.addPermission(role, resource, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePermission(String role, String resource, String action) {
|
||||||
|
permissionPersistService.deletePermission(role, resource, action);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.security.nacos.users;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.nacos.core.auth.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class NacosUser extends User {
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
private boolean globalAdmin = false;
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGlobalAdmin() {
|
||||||
|
return globalAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGlobalAdmin(boolean globalAdmin) {
|
||||||
|
this.globalAdmin = globalAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSON.toJSONString(this);
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.console.security;
|
package com.alibaba.nacos.console.security.nacos.users;
|
||||||
|
|
||||||
import com.alibaba.nacos.config.server.model.User;
|
import com.alibaba.nacos.config.server.model.User;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
@ -27,11 +27,11 @@ import java.util.Collection;
|
|||||||
*
|
*
|
||||||
* @author wfnuser
|
* @author wfnuser
|
||||||
*/
|
*/
|
||||||
public class CustomUserDetails implements UserDetails {
|
public class NacosUserDetails implements UserDetails {
|
||||||
|
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
public CustomUserDetails(User user) {
|
public NacosUserDetails(User user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.console.security.nacos.users;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.nacos.config.server.auth.UserPersistService;
|
||||||
|
import com.alibaba.nacos.config.server.model.Page;
|
||||||
|
import com.alibaba.nacos.config.server.model.User;
|
||||||
|
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||||
|
import com.alibaba.nacos.core.utils.Loggers;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custem user service
|
||||||
|
*
|
||||||
|
* @author wfnuser
|
||||||
|
* @author nkorange
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class NacosUserDetailsServiceImpl implements UserDetailsService {
|
||||||
|
|
||||||
|
private Map<String, User> userMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserPersistService userPersistService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthConfigs authConfigs;
|
||||||
|
|
||||||
|
@Scheduled(initialDelay = 5000, fixedDelay = 15000)
|
||||||
|
private void reload() {
|
||||||
|
try {
|
||||||
|
Page<User> users = getUsersFromDatabase(1, Integer.MAX_VALUE);
|
||||||
|
if (users == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, User> map = new ConcurrentHashMap<>(16);
|
||||||
|
for (User user : users.getPageItems()) {
|
||||||
|
map.put(user.getUsername(), user);
|
||||||
|
}
|
||||||
|
userMap = map;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Loggers.AUTH.warn("[LOAD-USERS] load failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
|
||||||
|
User user = userMap.get(username);
|
||||||
|
if (!authConfigs.isCachingEnabled()) {
|
||||||
|
user = userPersistService.findUserByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
throw new UsernameNotFoundException(username);
|
||||||
|
}
|
||||||
|
return new NacosUserDetails(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUserPassword(String username, String password) {
|
||||||
|
userPersistService.updateUserPassword(username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<User> getUsersFromDatabase(int pageNo, int pageSize) {
|
||||||
|
return userPersistService.getUsers(pageNo, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser(String username) {
|
||||||
|
User user = userMap.get(username);
|
||||||
|
if (!authConfigs.isCachingEnabled()) {
|
||||||
|
user = getUserFromDatabase(username);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUserFromDatabase(String username) {
|
||||||
|
return userPersistService.findUserByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createUser(String username, String password) {
|
||||||
|
userPersistService.createUser(username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteUser(String username) {
|
||||||
|
userPersistService.deleteUser(username);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
CREATE SCHEMA diamond AUTHORIZATION diamond;
|
CREATE SCHEMA nacos AUTHORIZATION nacos;
|
||||||
|
|
||||||
CREATE TABLE config_info (
|
CREATE TABLE config_info (
|
||||||
id bigint NOT NULL generated by default as identity,
|
id bigint NOT NULL generated by default as identity,
|
||||||
@ -172,18 +172,25 @@ CREATE TABLE tenant_info (
|
|||||||
constraint uk_tenant_info_kptenantid UNIQUE (kp,tenant_id));
|
constraint uk_tenant_info_kptenantid UNIQUE (kp,tenant_id));
|
||||||
CREATE INDEX tenant_info_tenant_id_idx ON tenant_info(tenant_id);
|
CREATE INDEX tenant_info_tenant_id_idx ON tenant_info(tenant_id);
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
username varchar(50) NOT NULL PRIMARY KEY,
|
username varchar(50) NOT NULL PRIMARY KEY,
|
||||||
password varchar(500) NOT NULL,
|
password varchar(500) NOT NULL,
|
||||||
enabled boolean NOT NULL
|
enabled boolean NOT NULL DEFAULT true
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE roles (
|
CREATE TABLE roles (
|
||||||
username varchar(50) NOT NULL,
|
username varchar(50) NOT NULL,
|
||||||
role varchar(50) NOT NULL
|
role varchar(50) NOT NULL,
|
||||||
|
constraint uk_username_role UNIQUE (username,role)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE permissions (
|
||||||
|
role varchar(50) NOT NULL,
|
||||||
|
resource varchar(512) NOT NULL,
|
||||||
|
action varchar(8) NOT NULL,
|
||||||
|
constraint uk_role_permission UNIQUE (role,resource,action)
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
|
||||||
|
|
||||||
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
|
INSERT INTO roles (username, role) VALUES ('nacos', 'GLOBAL_ADMIN');
|
||||||
|
@ -27,4 +27,11 @@ server.tomcat.basedir=
|
|||||||
#management.security=false
|
#management.security=false
|
||||||
#security.basic.enabled=false
|
#security.basic.enabled=false
|
||||||
#nacos.security.ignore.urls=/**
|
#nacos.security.ignore.urls=/**
|
||||||
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**
|
#nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**
|
||||||
|
|
||||||
|
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**
|
||||||
|
|
||||||
|
|
||||||
|
nacos.core.auth.system.type=nacos
|
||||||
|
nacos.core.auth.enabled=false
|
||||||
|
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
|
||||||
|
@ -313,6 +313,7 @@ const I18N_CONF = {
|
|||||||
delSelectedAlertTitle: 'Delete config',
|
delSelectedAlertTitle: 'Delete config',
|
||||||
delSelectedAlertContent: 'please select the configuration to delete',
|
delSelectedAlertContent: 'please select the configuration to delete',
|
||||||
delSuccessMsg: 'delete successful',
|
delSuccessMsg: 'delete successful',
|
||||||
|
cloneEditableTitle: 'Modify Data Id and Group (optional)',
|
||||||
},
|
},
|
||||||
NewConfig: {
|
NewConfig: {
|
||||||
newListingMain: 'Create Configuration',
|
newListingMain: 'Create Configuration',
|
||||||
|
@ -311,6 +311,7 @@ const I18N_CONF = {
|
|||||||
delSelectedAlertTitle: '配置删除',
|
delSelectedAlertTitle: '配置删除',
|
||||||
delSelectedAlertContent: '请选择要删除的配置',
|
delSelectedAlertContent: '请选择要删除的配置',
|
||||||
delSuccessMsg: '删除成功',
|
delSuccessMsg: '删除成功',
|
||||||
|
cloneEditableTitle: '修改 Data Id 和 Group (可选操作)',
|
||||||
},
|
},
|
||||||
NewConfig: {
|
NewConfig: {
|
||||||
newListingMain: '新建配置',
|
newListingMain: '新建配置',
|
||||||
|
@ -39,6 +39,7 @@ import ShowCodeing from 'components/ShowCodeing';
|
|||||||
import DeleteDialog from 'components/DeleteDialog';
|
import DeleteDialog from 'components/DeleteDialog';
|
||||||
import DashboardCard from './DashboardCard';
|
import DashboardCard from './DashboardCard';
|
||||||
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
|
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
import { LANGUAGE_KEY } from '../../../constants';
|
import { LANGUAGE_KEY } from '../../../constants';
|
||||||
@ -689,10 +690,9 @@ class ConfigurationManagement extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exportData() {
|
exportData() {
|
||||||
let url =
|
let url = `v1/cs/configs?export=true&group=${this.group}&tenant=${getParams(
|
||||||
`v1/cs/configs?export=true&group=${this.group}&tenant=${getParams('namespace')}&appName=${
|
'namespace'
|
||||||
this.appName
|
)}&appName=${this.appName}&ids=&dataId=${this.dataId}`;
|
||||||
}&ids=&dataId=` + this.dataId;
|
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,19 +783,54 @@ class ConfigurationManagement extends React.Component {
|
|||||||
}
|
}
|
||||||
let namespaces = data.data;
|
let namespaces = data.data;
|
||||||
let namespaceSelectData = [];
|
let namespaceSelectData = [];
|
||||||
namespaces.forEach(item => {
|
let namespaceSelecItemRender = item => {
|
||||||
if (self.state.nownamespace_id !== item.namespace) {
|
if (item.isCurrent) {
|
||||||
let dataItem = {};
|
return <span style={{ color: '#00AA00', 'font-weight': 'bold' }}>{item.label}</span>;
|
||||||
if (item.namespaceShowName === 'public') {
|
} else {
|
||||||
dataItem.label = 'public | public';
|
return <span>{item.label}</span>;
|
||||||
dataItem.value = 'public';
|
|
||||||
} else {
|
|
||||||
dataItem.label = `${item.namespaceShowName} | ${item.namespace}`;
|
|
||||||
dataItem.value = item.namespace;
|
|
||||||
}
|
|
||||||
namespaceSelectData.push(dataItem);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
namespaces.forEach(item => {
|
||||||
|
let dataItem = {};
|
||||||
|
dataItem.isCurrent = false;
|
||||||
|
if (self.state.nownamespace_id === item.namespace) {
|
||||||
|
dataItem.isCurrent = true;
|
||||||
|
}
|
||||||
|
if (item.namespaceShowName === 'public') {
|
||||||
|
dataItem.label = 'public | public';
|
||||||
|
dataItem.value = 'public';
|
||||||
|
} else {
|
||||||
|
dataItem.label = `${item.namespaceShowName} | ${item.namespace}`;
|
||||||
|
dataItem.value = item.namespace;
|
||||||
|
}
|
||||||
|
namespaceSelectData.push(dataItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let editableTableData = [];
|
||||||
|
let configsTableSelectedDeepCopyed = new Map();
|
||||||
|
configsTableSelected.forEach((value, key, map) => {
|
||||||
|
let dataItem = {};
|
||||||
|
dataItem.id = key;
|
||||||
|
dataItem.dataId = value.dataId;
|
||||||
|
dataItem.group = value.group;
|
||||||
|
editableTableData.push(dataItem);
|
||||||
|
configsTableSelectedDeepCopyed.set(key, JSON.parse(JSON.stringify(value)));
|
||||||
|
});
|
||||||
|
let editableTableOnBlur = (record, type, e) => {
|
||||||
|
if (type === 1) {
|
||||||
|
configsTableSelectedDeepCopyed.get(record.id).dataId = e.target.value;
|
||||||
|
} else {
|
||||||
|
configsTableSelectedDeepCopyed.get(record.id).group = e.target.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let renderEditableTableCellDataId = (value, index, record) => (
|
||||||
|
<Input defaultValue={value} onBlur={editableTableOnBlur.bind(this, record, 1)} />
|
||||||
|
);
|
||||||
|
let renderEditableTableCellGroup = (value, index, record) => (
|
||||||
|
<Input defaultValue={value} onBlur={editableTableOnBlur.bind(this, record, 2)} />
|
||||||
|
);
|
||||||
|
|
||||||
const cloneConfirm = Dialog.confirm({
|
const cloneConfirm = Dialog.confirm({
|
||||||
title: locale.cloningConfiguration,
|
title: locale.cloningConfiguration,
|
||||||
footer: false,
|
footer: false,
|
||||||
@ -822,6 +857,7 @@ class ConfigurationManagement extends React.Component {
|
|||||||
showSearch
|
showSearch
|
||||||
hasClear={false}
|
hasClear={false}
|
||||||
mode="single"
|
mode="single"
|
||||||
|
itemRender={namespaceSelecItemRender}
|
||||||
dataSource={namespaceSelectData}
|
dataSource={namespaceSelectData}
|
||||||
onChange={(value, actionType, item) => {
|
onChange={(value, actionType, item) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
@ -866,7 +902,7 @@ class ConfigurationManagement extends React.Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style={{ marginBottom: 10 }}>
|
||||||
<Button
|
<Button
|
||||||
type={'primary'}
|
type={'primary'}
|
||||||
style={{ marginRight: 10 }}
|
style={{ marginRight: 10 }}
|
||||||
@ -878,13 +914,21 @@ class ConfigurationManagement extends React.Component {
|
|||||||
document.getElementById('cloneTargetSpaceSelectErr').style.display = 'none';
|
document.getElementById('cloneTargetSpaceSelectErr').style.display = 'none';
|
||||||
}
|
}
|
||||||
let idsStr = '';
|
let idsStr = '';
|
||||||
configsTableSelected.forEach((value, key, map) => {
|
let clonePostData = [];
|
||||||
idsStr = `${idsStr + key},`;
|
configsTableSelectedDeepCopyed.forEach((value, key, map) => {
|
||||||
|
let postDataItem = {};
|
||||||
|
postDataItem.cfgId = key;
|
||||||
|
postDataItem.dataId = value.dataId;
|
||||||
|
postDataItem.group = value.group;
|
||||||
|
clonePostData.push(postDataItem);
|
||||||
});
|
});
|
||||||
let cloneTargetSpace = self.field.getValue('cloneTargetSpace');
|
let cloneTargetSpace = self.field.getValue('cloneTargetSpace');
|
||||||
let sameConfigPolicy = self.field.getValue('sameConfigPolicy');
|
let sameConfigPolicy = self.field.getValue('sameConfigPolicy');
|
||||||
request({
|
request({
|
||||||
url: `v1/cs/configs?clone=true&tenant=${cloneTargetSpace}&policy=${sameConfigPolicy}&ids=${idsStr}`,
|
url: `v1/cs/configs?clone=true&tenant=${cloneTargetSpace}&policy=${sameConfigPolicy}&namespaceId=`,
|
||||||
|
method: 'post',
|
||||||
|
data: JSON.stringify(clonePostData),
|
||||||
|
contentType: 'application/json',
|
||||||
beforeSend() {
|
beforeSend() {
|
||||||
self.openLoading();
|
self.openLoading();
|
||||||
},
|
},
|
||||||
@ -908,6 +952,25 @@ class ConfigurationManagement extends React.Component {
|
|||||||
{locale.startCloning}
|
{locale.startCloning}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ marginBottom: 10 }}>
|
||||||
|
<span style={{ color: '#00AA00', 'font-weight': 'bold' }}>
|
||||||
|
{locale.cloneEditableTitle}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Table dataSource={editableTableData}>
|
||||||
|
<Table.Column
|
||||||
|
title="Data Id"
|
||||||
|
dataIndex="dataId"
|
||||||
|
cell={renderEditableTableCellDataId}
|
||||||
|
/>
|
||||||
|
<Table.Column
|
||||||
|
title="Group"
|
||||||
|
dataIndex="group"
|
||||||
|
cell={renderEditableTableCellGroup}
|
||||||
|
/>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@ -1089,7 +1152,9 @@ class ConfigurationManagement extends React.Component {
|
|||||||
rowSelection.selectedRowKeys = ids;
|
rowSelection.selectedRowKeys = ids;
|
||||||
this.setState({ rowSelection });
|
this.setState({ rowSelection });
|
||||||
configsTableSelected.clear();
|
configsTableSelected.clear();
|
||||||
records.forEach(item => configsTableSelected.set(item.id, item));
|
records.forEach((record, i) => {
|
||||||
|
configsTableSelected.set(record.id, record);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
42
core/pom.xml
42
core/pom.xml
@ -59,6 +59,28 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.junit.vintage</groupId>
|
||||||
|
<artifactId>junit-vintage-engine</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
@ -79,5 +101,25 @@
|
|||||||
<artifactId>tomcat-embed-core</artifactId>
|
<artifactId>tomcat-embed-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-impl</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.reflections</groupId>
|
||||||
|
<artifactId>reflections</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception to be thrown if authorization is failed.
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class AccessException extends NacosException {
|
||||||
|
|
||||||
|
public AccessException(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessException(int code) {
|
||||||
|
this.setErrCode(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessException(String msg) {
|
||||||
|
this.setErrMsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource action type definitions
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public enum ActionTypes {
|
||||||
|
/**
|
||||||
|
* Read
|
||||||
|
*/
|
||||||
|
READ("r"),
|
||||||
|
/**
|
||||||
|
* Write
|
||||||
|
*/
|
||||||
|
WRITE("w");
|
||||||
|
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
ActionTypes(String action) {
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.core.env.ReloadableConfigs;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.PropertySource;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth related configurations
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Configuration
|
||||||
|
public class AuthConfigs {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ReloadableConfigs reloadableConfigs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* secret key
|
||||||
|
*/
|
||||||
|
@Value("${nacos.core.auth.default.token.secret.key:}")
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token validity time(seconds)
|
||||||
|
*/
|
||||||
|
@Value("${nacos.core.auth.default.token.expire.seconds:1800}")
|
||||||
|
private long tokenValidityInSeconds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which auth system is in use
|
||||||
|
*/
|
||||||
|
@Value("${nacos.core.auth.system.type:}")
|
||||||
|
private String nacosAuthSystemType;
|
||||||
|
|
||||||
|
public String getSecretKey() {
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTokenValidityInSeconds() {
|
||||||
|
return tokenValidityInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNacosAuthSystemType() {
|
||||||
|
return nacosAuthSystemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAuthEnabled() {
|
||||||
|
return BooleanUtils.toBoolean(reloadableConfigs.getProperties()
|
||||||
|
.getProperty("nacos.core.auth.enabled", "false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCachingEnabled() {
|
||||||
|
return BooleanUtils.toBoolean(reloadableConfigs.getProperties()
|
||||||
|
.getProperty("nacos.core.auth.caching.enabled", "true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean authFilterRegistration() {
|
||||||
|
FilterRegistrationBean<AuthFilter> registration = new FilterRegistrationBean<>();
|
||||||
|
registration.setFilter(authFilter());
|
||||||
|
registration.addUrlPatterns("/*");
|
||||||
|
registration.setName("authFilter");
|
||||||
|
registration.setOrder(6);
|
||||||
|
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthFilter authFilter() {
|
||||||
|
return new AuthFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
119
core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java
Normal file
119
core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.core.code.ControllerMethodsCache;
|
||||||
|
import com.alibaba.nacos.core.utils.Constants;
|
||||||
|
import com.alibaba.nacos.core.utils.ExceptionUtil;
|
||||||
|
import com.alibaba.nacos.core.utils.Loggers;
|
||||||
|
import com.alibaba.nacos.core.utils.WebUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unified filter to handle authentication and authorization
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class AuthFilter implements Filter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthConfigs authConfigs;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthManager authManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ControllerMethodsCache methodsCache;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
|
||||||
|
if (!authConfigs.isAuthEnabled()) {
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServletRequest req = (HttpServletRequest) request;
|
||||||
|
HttpServletResponse resp = (HttpServletResponse) response;
|
||||||
|
|
||||||
|
String userAgent = WebUtils.getUserAgent(req);
|
||||||
|
|
||||||
|
if (StringUtils.startsWith(userAgent, Constants.NACOS_SERVER_HEADER)) {
|
||||||
|
chain.doFilter(req, resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Loggers.AUTH.isDebugEnabled()) {
|
||||||
|
Loggers.AUTH.debug("auth filter start, request: {}", req.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
String path = new URI(req.getRequestURI()).getPath();
|
||||||
|
Method method = methodsCache.getMethod(req.getMethod(), path);
|
||||||
|
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.isAnnotationPresent(Secured.class) && authConfigs.isAuthEnabled()) {
|
||||||
|
|
||||||
|
Secured secured = method.getAnnotation(Secured.class);
|
||||||
|
String action = secured.action().toString();
|
||||||
|
String resource = secured.resource();
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(resource)) {
|
||||||
|
ResourceParser parser = secured.parser().newInstance();
|
||||||
|
resource = parser.parseName(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(resource)) {
|
||||||
|
// deny if we don't find any resource:
|
||||||
|
throw new AccessException("resource name invalid!");
|
||||||
|
}
|
||||||
|
|
||||||
|
authManager.auth(new Permission(resource, action), authManager.login(req));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (AccessException e) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_FORBIDDEN, e.getErrMsg());
|
||||||
|
return;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED,
|
||||||
|
"no such api:" + req.getMethod() + ":" + req.getRequestURI());
|
||||||
|
return;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, ExceptionUtil.getAllExceptionMsg(e));
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server failed," + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access control entry. Can be extended by 3rd party implementations.
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public interface AuthManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication of request, identify the user who request the resource.
|
||||||
|
*
|
||||||
|
* @param request where we can find the user information
|
||||||
|
* @return user related to this request, null if no user info is found.
|
||||||
|
* @throws AccessException if authentication is failed
|
||||||
|
*/
|
||||||
|
User login(Object request) throws AccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorization of request, constituted with resource and user.
|
||||||
|
*
|
||||||
|
* @param permission permission to auth
|
||||||
|
* @param user user who wants to access the resource.
|
||||||
|
* @throws AccessException if authorization is failed
|
||||||
|
*/
|
||||||
|
void auth(Permission permission, User user) throws AccessException;
|
||||||
|
}
|
@ -13,14 +13,19 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.naming.web;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
package com.alibaba.nacos.core.auth;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Innovated By: Xuanyin.zy
|
* Types of all auth implementations
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
*/
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
public enum AuthSystemTypes {
|
||||||
public @interface NeedAuth {
|
|
||||||
|
/**
|
||||||
|
* Nacos builtin auth system
|
||||||
|
*/
|
||||||
|
NACOS
|
||||||
}
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default resource parser
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class DefaultResourceParser implements ResourceParser {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseName(Object request) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permission to auth
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class Permission {
|
||||||
|
|
||||||
|
public Permission() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Permission(String resource, String action) {
|
||||||
|
this.resource = resource;
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An unique key of resource
|
||||||
|
*/
|
||||||
|
private String resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action on resource, refer to class ActionTypes
|
||||||
|
*/
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
public String getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResource(String resource) {
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAction(String action) {
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
}
|
53
core/src/main/java/com/alibaba/nacos/core/auth/Resource.java
Normal file
53
core/src/main/java/com/alibaba/nacos/core/auth/Resource.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource used in authorization.
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class Resource {
|
||||||
|
|
||||||
|
public static final String SPLITTER = ":";
|
||||||
|
public static final String ANY = "*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique key of resource.
|
||||||
|
*/
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
public Resource(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parseName() {
|
||||||
|
return key.substring(0, key.lastIndexOf(SPLITTER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JSON.toJSONString(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource parser
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public interface ResourceParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a unique name of the resource from the request
|
||||||
|
*
|
||||||
|
* @param request where we can find the resource info. Given it may vary from Http request to gRPC request,
|
||||||
|
* we use a Object type for future accommodation.
|
||||||
|
* @return resource name
|
||||||
|
*/
|
||||||
|
String parseName(Object request);
|
||||||
|
}
|
52
core/src/main/java/com/alibaba/nacos/core/auth/Secured.java
Normal file
52
core/src/main/java/com/alibaba/nacos/core/auth/Secured.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation indicating that the annotated request should be authorized.
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Secured {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The action type of the request
|
||||||
|
*
|
||||||
|
* @return action type, default READ
|
||||||
|
*/
|
||||||
|
ActionTypes action() default ActionTypes.READ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of resource related to the request
|
||||||
|
*
|
||||||
|
* @return resource name
|
||||||
|
*/
|
||||||
|
String resource() default StringUtils.EMPTY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource name parser. Should have lower priority than name()
|
||||||
|
*
|
||||||
|
* @return class type of resource parser
|
||||||
|
*/
|
||||||
|
Class<? extends ResourceParser> parser() default DefaultResourceParser.class;
|
||||||
|
}
|
39
core/src/main/java/com/alibaba/nacos/core/auth/User.java
Normal file
39
core/src/main/java/com/alibaba/nacos/core/auth/User.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.auth;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User information in authorization.
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique string representing user
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
}
|
@ -13,64 +13,72 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.alibaba.nacos.naming.web;
|
package com.alibaba.nacos.core.code;
|
||||||
|
|
||||||
|
|
||||||
import com.alibaba.nacos.naming.controllers.*;
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.reflections.Reflections;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic methods for filter to use
|
* Method cache
|
||||||
*
|
*
|
||||||
* @author nkorange
|
* @author nkorange
|
||||||
* @since 1.0.0
|
* @since 1.2.0
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class FilterBase {
|
public class ControllerMethodsCache {
|
||||||
|
|
||||||
private ConcurrentMap<String, Method> methodCache = new
|
private ConcurrentMap<String, Method> methods = new
|
||||||
ConcurrentHashMap<>();
|
ConcurrentHashMap<>();
|
||||||
|
|
||||||
@PostConstruct
|
public ConcurrentMap<String, Method> getMethods() {
|
||||||
public void init() {
|
return methods;
|
||||||
initClassMethod(InstanceController.class);
|
|
||||||
initClassMethod(ServiceController.class);
|
|
||||||
initClassMethod(ClusterController.class);
|
|
||||||
initClassMethod(CatalogController.class);
|
|
||||||
initClassMethod(HealthController.class);
|
|
||||||
initClassMethod(RaftController.class);
|
|
||||||
initClassMethod(DistroController.class);
|
|
||||||
initClassMethod(OperatorController.class);
|
|
||||||
initClassMethod(ApiController.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Method getMethod(String httpMethod, String path) {
|
public Method getMethod(String httpMethod, String path) {
|
||||||
String key = httpMethod + "-->" + path.replace("/nacos", "");
|
String key = httpMethod + "-->" + path.replace("/nacos", "");
|
||||||
return methodCache.get(key);
|
return methods.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initClassMethod(Class<?> clazz) {
|
public void initClassMethod(String packageName) {
|
||||||
|
Reflections reflections = new Reflections(packageName);
|
||||||
|
Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(RequestMapping.class);
|
||||||
|
|
||||||
|
for (Class clazz : classesList) {
|
||||||
|
initClassMethod(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initClassMethod(Set<Class<?>> classesList) {
|
||||||
|
for (Class clazz : classesList) {
|
||||||
|
initClassMethod(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initClassMethod(Class<?> clazz) {
|
||||||
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
|
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
|
||||||
String classPath = requestMapping.value()[0];
|
for (String classPath : requestMapping.value()) {
|
||||||
for (Method method : clazz.getMethods()) {
|
for (Method method : clazz.getMethods()) {
|
||||||
if (!method.isAnnotationPresent(RequestMapping.class)) {
|
if (!method.isAnnotationPresent(RequestMapping.class)) {
|
||||||
parseSubAnnotations(method, classPath);
|
parseSubAnnotations(method, classPath);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
requestMapping = method.getAnnotation(RequestMapping.class);
|
requestMapping = method.getAnnotation(RequestMapping.class);
|
||||||
RequestMethod[] requestMethods = requestMapping.method();
|
RequestMethod[] requestMethods = requestMapping.method();
|
||||||
if (requestMethods.length == 0) {
|
if (requestMethods.length == 0) {
|
||||||
requestMethods = new RequestMethod[1];
|
requestMethods = new RequestMethod[1];
|
||||||
requestMethods[0] = RequestMethod.GET;
|
requestMethods[0] = RequestMethod.GET;
|
||||||
}
|
}
|
||||||
for (String methodPath : requestMapping.value()) {
|
for (String methodPath : requestMapping.value()) {
|
||||||
methodCache.put(requestMethods[0].name() + "-->" + classPath + methodPath, method);
|
methods.put(requestMethods[0].name() + "-->" + classPath + methodPath, method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,11 +115,11 @@ public class FilterBase {
|
|||||||
|
|
||||||
private void put(RequestMethod requestMethod, String classPath, String[] requestPaths, Method method) {
|
private void put(RequestMethod requestMethod, String classPath, String[] requestPaths, Method method) {
|
||||||
if (ArrayUtils.isEmpty(requestPaths)) {
|
if (ArrayUtils.isEmpty(requestPaths)) {
|
||||||
methodCache.put(requestMethod.name() + "-->" + classPath, method);
|
methods.put(requestMethod.name() + "-->" + classPath, method);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (String requestPath : requestPaths) {
|
for (String requestPath : requestPaths) {
|
||||||
methodCache.put(requestMethod.name() + "-->" + classPath + requestPath, method);
|
methods.put(requestMethod.name() + "-->" + classPath + requestPath, method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
69
core/src/main/java/com/alibaba/nacos/core/env/ReloadableConfigs.java
vendored
Normal file
69
core/src/main/java/com/alibaba/nacos/core/env/ReloadableConfigs.java
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.env;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload application.properties
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ReloadableConfigs {
|
||||||
|
|
||||||
|
private Properties properties;
|
||||||
|
|
||||||
|
@Value("${spring.config.location:}")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
private static final String FILE_PREFIX = "file:";
|
||||||
|
|
||||||
|
@Scheduled(fixedRate = 5000)
|
||||||
|
public void reload() throws IOException {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
InputStream inputStream = null;
|
||||||
|
if (StringUtils.isNotBlank(path) && path.contains(FILE_PREFIX)) {
|
||||||
|
String[] paths = path.split(",");
|
||||||
|
path = paths[paths.length - 1].substring(FILE_PREFIX.length());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
inputStream = new FileInputStream(new File(path + "application.properties"));
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
if (inputStream == null) {
|
||||||
|
inputStream = getClass().getResourceAsStream("/application.properties");
|
||||||
|
}
|
||||||
|
properties.load(inputStream);
|
||||||
|
inputStream.close();
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Properties getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
|||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@ -147,9 +148,10 @@ public class StartingSpringApplicationRunListener implements SpringApplicationRu
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void logFilePath() {
|
private void logFilePath() {
|
||||||
LOGGER.info("Nacos Log files: {}/logs/", NACOS_HOME);
|
String[] dirNames = new String[]{"logs", "conf", "data"};
|
||||||
LOGGER.info("Nacos Conf files: {}/conf/", NACOS_HOME);
|
for (String dirName: dirNames) {
|
||||||
LOGGER.info("Nacos Data files: {}/data/", NACOS_HOME);
|
LOGGER.info("Nacos Log files: {}{}{}{}", NACOS_HOME, File.separatorChar, dirName, File.separatorChar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logStarting() {
|
private void logStarting() {
|
||||||
|
@ -58,4 +58,6 @@ public interface Constants {
|
|||||||
String SYSTEM_PREFER_HOSTNAME_OVER_IP = "nacos.preferHostnameOverIp";
|
String SYSTEM_PREFER_HOSTNAME_OVER_IP = "nacos.preferHostnameOverIp";
|
||||||
String WEB_CONTEXT_PATH = "server.servlet.context-path";
|
String WEB_CONTEXT_PATH = "server.servlet.context-path";
|
||||||
String COMMA_DIVISION = ",";
|
String COMMA_DIVISION = ",";
|
||||||
|
|
||||||
|
String NACOS_SERVER_HEADER = "Nacos-Server";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common methods for exception
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class ExceptionUtil {
|
||||||
|
|
||||||
|
public static String getAllExceptionMsg(Throwable e) {
|
||||||
|
Throwable cause = e;
|
||||||
|
StringBuilder strBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
while (cause != null && !StringUtils.isEmpty(cause.getMessage())) {
|
||||||
|
strBuilder.append("caused: ").append(cause.getMessage()).append(";");
|
||||||
|
cause = cause.getCause();
|
||||||
|
}
|
||||||
|
|
||||||
|
return strBuilder.toString();
|
||||||
|
}
|
||||||
|
}
|
30
core/src/main/java/com/alibaba/nacos/core/utils/Loggers.java
Normal file
30
core/src/main/java/com/alibaba/nacos/core/utils/Loggers.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed 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 com.alibaba.nacos.core.utils;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loggers for core
|
||||||
|
*
|
||||||
|
* @author nkorange
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
public class Loggers {
|
||||||
|
|
||||||
|
public static final Logger AUTH = LoggerFactory.getLogger("com.alibaba.nacos.core.auth");
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user