Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
2dafa405d3
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:
|
||||
|
||||
#build nacos
|
||||
$ mvn -Prelease-nacos -DskipTests clean install -U
|
||||
$ mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
|
||||
|
@ -32,6 +32,10 @@ public class PropertyKeyConst {
|
||||
|
||||
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 SECRET_KEY = "secretKey";
|
||||
|
@ -62,6 +62,8 @@ public class Constants {
|
||||
|
||||
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 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 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
|
||||
*/
|
||||
@ -169,4 +181,6 @@ public class Constants {
|
||||
|
||||
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;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author nkorange
|
||||
@ -32,6 +33,9 @@ public class NamingUtils {
|
||||
}
|
||||
|
||||
public static String getServiceName(String serviceNameWithGroup) {
|
||||
if (StringUtils.isBlank(serviceNameWithGroup)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
|
||||
return serviceNameWithGroup;
|
||||
}
|
||||
@ -39,6 +43,9 @@ public class NamingUtils {
|
||||
}
|
||||
|
||||
public static String getGroupName(String serviceNameWithGroup) {
|
||||
if (StringUtils.isBlank(serviceNameWithGroup)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
|
||||
return Constants.DEFAULT_GROUP;
|
||||
}
|
||||
|
@ -116,6 +116,10 @@
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
package com.alibaba.nacos.client.config;
|
||||
|
||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||
import com.alibaba.nacos.api.SystemPropertyKeyConst;
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.config.ConfigService;
|
||||
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.ParamUtils;
|
||||
import com.alibaba.nacos.client.utils.LogUtils;
|
||||
import com.alibaba.nacos.client.utils.TemplateUtils;
|
||||
import com.alibaba.nacos.client.utils.TenantUtil;
|
||||
import com.alibaba.nacos.client.utils.ParamUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@ -44,7 +42,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Config Impl
|
||||
@ -86,34 +83,7 @@ public class NacosConfigService implements ConfigService {
|
||||
}
|
||||
|
||||
private void initNamespace(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 StringUtils.isNotBlank(namespace) ? namespace : EMPTY;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(namespaceTmp)) {
|
||||
namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE);
|
||||
}
|
||||
namespace = StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : EMPTY;
|
||||
namespace = ParamUtil.parseNamespace(properties);
|
||||
properties.put(PropertyKeyConst.NAMESPACE, namespace);
|
||||
}
|
||||
|
||||
@ -170,9 +140,8 @@ public class NacosConfigService implements ConfigService {
|
||||
}
|
||||
|
||||
try {
|
||||
content = worker.getServerConfig(dataId, group, tenant, timeoutMs);
|
||||
|
||||
cr.setContent(content);
|
||||
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
|
||||
cr.setContent(ct[0]);
|
||||
|
||||
configFilterChainManager.doFilter(null, cr);
|
||||
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.SpasAdapter;
|
||||
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.LogUtils;
|
||||
import com.alibaba.nacos.client.utils.ParamUtil;
|
||||
@ -43,7 +44,7 @@ import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Server Agent
|
||||
@ -54,6 +55,12 @@ public class ServerHttpAgent implements HttpAgent {
|
||||
|
||||
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 headers
|
||||
@ -68,7 +75,7 @@ public class ServerHttpAgent implements HttpAgent {
|
||||
long readTimeoutMs) throws IOException {
|
||||
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
||||
final boolean isSSL = false;
|
||||
|
||||
injectSecurityInfo(paramValues);
|
||||
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
||||
int maxRetry = this.maxRetry;
|
||||
|
||||
@ -121,7 +128,7 @@ public class ServerHttpAgent implements HttpAgent {
|
||||
long readTimeoutMs) throws IOException {
|
||||
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
||||
boolean isSSL = false;
|
||||
|
||||
injectSecurityInfo(paramValues);
|
||||
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
||||
int maxRetry = this.maxRetry;
|
||||
|
||||
@ -176,7 +183,7 @@ public class ServerHttpAgent implements HttpAgent {
|
||||
long readTimeoutMs) throws IOException {
|
||||
final long endTime = System.currentTimeMillis() + readTimeoutMs;
|
||||
boolean isSSL = false;
|
||||
|
||||
injectSecurityInfo(paramValues);
|
||||
String currentServerAddr = serverListMgr.getCurrentServerAddr();
|
||||
int maxRetry = this.maxRetry;
|
||||
|
||||
@ -243,7 +250,39 @@ public class ServerHttpAgent implements HttpAgent {
|
||||
|
||||
public ServerHttpAgent(Properties properties) throws NacosException {
|
||||
serverListMgr = new ServerListManager(properties);
|
||||
securityProxy = new SecurityProxy(properties);
|
||||
namespaceId = properties.getProperty(PropertyKeyConst.NAMESPACE);
|
||||
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) {
|
||||
|
@ -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;
|
||||
|
||||
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.Listener;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
|
||||
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.utils.LogUtils;
|
||||
import com.alibaba.nacos.client.utils.TenantUtil;
|
||||
@ -28,6 +30,7 @@ import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
@ -59,9 +62,17 @@ public class CacheData {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String newContent) {
|
||||
this.content = newContent;
|
||||
this.md5 = getMd5String(content);
|
||||
public void setContent(String content) {
|
||||
this.content = 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) {
|
||||
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)) {
|
||||
LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", name, tenant, dataId, group,
|
||||
listeners.size());
|
||||
@ -158,12 +171,12 @@ public class CacheData {
|
||||
void checkListenerMd5() {
|
||||
for (ManagerListenerWrap wrap : listeners) {
|
||||
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 Listener listener = listenerWrap.listener;
|
||||
|
||||
@ -188,6 +201,15 @@ public class CacheData {
|
||||
configFilterChainManager.doFilter(null, cr);
|
||||
String contentTmp = cr.getContent();
|
||||
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;
|
||||
LOGGER.info("[{}] [notify-ok] dataId={}, group={}, md5={}, listener={} ", name, dataId, group, md5,
|
||||
listener);
|
||||
@ -282,11 +304,13 @@ public class CacheData {
|
||||
private volatile String content;
|
||||
private int taskId;
|
||||
private volatile boolean isInitializing = true;
|
||||
private String type;
|
||||
}
|
||||
|
||||
class ManagerListenerWrap {
|
||||
final Listener listener;
|
||||
String lastCallMd5 = CacheData.getMd5String(null);
|
||||
String lastContent = null;
|
||||
|
||||
ManagerListenerWrap(Listener listener) {
|
||||
this.listener = listener;
|
||||
@ -297,6 +321,12 @@ class ManagerListenerWrap {
|
||||
this.lastCallMd5 = md5;
|
||||
}
|
||||
|
||||
ManagerListenerWrap(Listener listener, String md5, String lastContent) {
|
||||
this.listener = listener;
|
||||
this.lastCallMd5 = md5;
|
||||
this.lastContent = lastContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
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.common.Constants;
|
||||
import com.alibaba.nacos.api.config.ConfigType;
|
||||
import com.alibaba.nacos.api.config.listener.Listener;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
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.WORD_SEPARATOR;
|
||||
import static com.alibaba.nacos.api.common.Constants.CONFIG_TYPE;
|
||||
|
||||
/**
|
||||
* Longpolling
|
||||
@ -183,8 +185,8 @@ public class ClientWorker {
|
||||
cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant);
|
||||
// fix issue # 1317
|
||||
if (enableRemoteSyncConfig) {
|
||||
String content = getServerConfig(dataId, group, tenant, 3000L);
|
||||
cache.setContent(content);
|
||||
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
|
||||
cache.setContent(ct[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,8 +212,9 @@ public class ClientWorker {
|
||||
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 {
|
||||
String[] ct = new String[2];
|
||||
if (StringUtils.isBlank(group)) {
|
||||
group = Constants.DEFAULT_GROUP;
|
||||
}
|
||||
@ -220,9 +223,9 @@ public class ClientWorker {
|
||||
try {
|
||||
List<String> params = null;
|
||||
if (StringUtils.isBlank(tenant)) {
|
||||
params = Arrays.asList("dataId", dataId, "group", group);
|
||||
params = new ArrayList<String>(Arrays.asList("dataId", dataId, "group", group));
|
||||
} 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);
|
||||
} catch (IOException e) {
|
||||
@ -236,10 +239,16 @@ public class ClientWorker {
|
||||
switch (result.code) {
|
||||
case HttpURLConnection.HTTP_OK:
|
||||
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:
|
||||
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
|
||||
return null;
|
||||
return ct;
|
||||
case HttpURLConnection.HTTP_CONFLICT: {
|
||||
LOGGER.error(
|
||||
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
|
||||
@ -517,12 +526,15 @@ public class ClientWorker {
|
||||
tenant = key[2];
|
||||
}
|
||||
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));
|
||||
cache.setContent(content);
|
||||
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}",
|
||||
cache.setContent(ct[0]);
|
||||
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(),
|
||||
ContentUtils.truncateContent(content));
|
||||
ContentUtils.truncateContent(ct[0]), ct[1]);
|
||||
} catch (NacosException ioe) {
|
||||
String message = String.format(
|
||||
"[%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.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public List<String> getServerUrls() {
|
||||
return serverUrls;
|
||||
}
|
||||
|
||||
Iterator<String> iterator() {
|
||||
if (serverUrls.isEmpty()) {
|
||||
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) {
|
||||
String key = appName != null ? appName : Constants.NO_APP_NAME;
|
||||
String key = appName != null ? appName : IdentifyConstants.NO_APP_NAME;
|
||||
CredentialService instance = instances.get(key);
|
||||
if (instance == null) {
|
||||
instance = new CredentialService(appName);
|
||||
@ -70,7 +70,7 @@ public final class CredentialService implements SpasCredentialLoader {
|
||||
}
|
||||
|
||||
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);
|
||||
if (instance != null) {
|
||||
instance.free();
|
||||
|
@ -94,7 +94,7 @@ public class CredentialWatcher {
|
||||
private void loadCredential(boolean init) {
|
||||
boolean logWarn = init;
|
||||
if (propertyPath == null) {
|
||||
URL url = ClassLoader.getSystemResource(Constants.PROPERTIES_FILENAME);
|
||||
URL url = ClassLoader.getSystemResource(IdentifyConstants.PROPERTIES_FILENAME);
|
||||
if (url != null) {
|
||||
propertyPath = url.getPath();
|
||||
}
|
||||
@ -105,7 +105,7 @@ public class CredentialWatcher {
|
||||
propertyPath = value;
|
||||
}
|
||||
if (propertyPath == null || propertyPath.isEmpty()) {
|
||||
propertyPath = Constants.CREDENTIAL_PATH + (appName == null ? Constants.CREDENTIAL_DEFAULT
|
||||
propertyPath = IdentifyConstants.CREDENTIAL_PATH + (appName == null ? IdentifyConstants.CREDENTIAL_DEFAULT
|
||||
: appName);
|
||||
} else {
|
||||
if (logWarn) {
|
||||
@ -115,7 +115,7 @@ public class CredentialWatcher {
|
||||
} else {
|
||||
if (logWarn) {
|
||||
SpasLogger.info("[{}] Load credential file from classpath: {}", appName,
|
||||
Constants.PROPERTIES_FILENAME);
|
||||
IdentifyConstants.PROPERTIES_FILENAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,13 +125,13 @@ public class CredentialWatcher {
|
||||
try {
|
||||
propertiesIS = new FileInputStream(propertyPath);
|
||||
} catch (FileNotFoundException e) {
|
||||
if (appName != null && !appName.equals(Constants.CREDENTIAL_DEFAULT) && propertyPath.equals(
|
||||
Constants.CREDENTIAL_PATH + appName)) {
|
||||
propertyPath = Constants.CREDENTIAL_PATH + Constants.CREDENTIAL_DEFAULT;
|
||||
if (appName != null && !appName.equals(IdentifyConstants.CREDENTIAL_DEFAULT) && propertyPath.equals(
|
||||
IdentifyConstants.CREDENTIAL_PATH + appName)) {
|
||||
propertyPath = IdentifyConstants.CREDENTIAL_PATH + IdentifyConstants.CREDENTIAL_DEFAULT;
|
||||
continue;
|
||||
}
|
||||
if (!Constants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
||||
propertyPath = Constants.DOCKER_CREDENTIAL_PATH;
|
||||
if (!IdentifyConstants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
||||
propertyPath = IdentifyConstants.DOCKER_CREDENTIAL_PATH;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -143,8 +143,8 @@ public class CredentialWatcher {
|
||||
String tenantId = null;
|
||||
if (propertiesIS == null) {
|
||||
propertyPath = null;
|
||||
accessKey = System.getenv(Constants.ENV_ACCESS_KEY);
|
||||
secretKey = System.getenv(Constants.ENV_SECRET_KEY);
|
||||
accessKey = System.getenv(IdentifyConstants.ENV_ACCESS_KEY);
|
||||
secretKey = System.getenv(IdentifyConstants.ENV_SECRET_KEY);
|
||||
if (accessKey == null && secretKey == null) {
|
||||
if (logWarn) {
|
||||
SpasLogger.info("{} No credential found", appName);
|
||||
@ -173,26 +173,26 @@ public class CredentialWatcher {
|
||||
SpasLogger.info("[{}] Load credential file {}", appName, propertyPath);
|
||||
}
|
||||
|
||||
if (!Constants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
||||
if (properties.containsKey(Constants.ACCESS_KEY)) {
|
||||
accessKey = properties.getProperty(Constants.ACCESS_KEY);
|
||||
if (!IdentifyConstants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) {
|
||||
if (properties.containsKey(IdentifyConstants.ACCESS_KEY)) {
|
||||
accessKey = properties.getProperty(IdentifyConstants.ACCESS_KEY);
|
||||
}
|
||||
if (properties.containsKey(Constants.SECRET_KEY)) {
|
||||
secretKey = properties.getProperty(Constants.SECRET_KEY);
|
||||
if (properties.containsKey(IdentifyConstants.SECRET_KEY)) {
|
||||
secretKey = properties.getProperty(IdentifyConstants.SECRET_KEY);
|
||||
}
|
||||
if (properties.containsKey(Constants.TENANT_ID)) {
|
||||
tenantId = properties.getProperty(Constants.TENANT_ID);
|
||||
if (properties.containsKey(IdentifyConstants.TENANT_ID)) {
|
||||
tenantId = properties.getProperty(IdentifyConstants.TENANT_ID);
|
||||
}
|
||||
} else {
|
||||
if (properties.containsKey(Constants.DOCKER_ACCESS_KEY)) {
|
||||
accessKey = properties.getProperty(Constants.DOCKER_ACCESS_KEY);
|
||||
if (properties.containsKey(IdentifyConstants.DOCKER_ACCESS_KEY)) {
|
||||
accessKey = properties.getProperty(IdentifyConstants.DOCKER_ACCESS_KEY);
|
||||
}
|
||||
if (properties.containsKey(Constants.DOCKER_SECRET_KEY)) {
|
||||
secretKey = properties.getProperty(Constants.DOCKER_SECRET_KEY);
|
||||
if (properties.containsKey(IdentifyConstants.DOCKER_SECRET_KEY)) {
|
||||
secretKey = properties.getProperty(IdentifyConstants.DOCKER_SECRET_KEY);
|
||||
}
|
||||
|
||||
if (properties.containsKey(Constants.DOCKER_TENANT_ID)) {
|
||||
tenantId = properties.getProperty(Constants.DOCKER_TENANT_ID);
|
||||
if (properties.containsKey(IdentifyConstants.DOCKER_TENANT_ID)) {
|
||||
tenantId = properties.getProperty(IdentifyConstants.DOCKER_TENANT_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,7 +211,7 @@ public class CredentialWatcher {
|
||||
Credentials credential = new Credentials(accessKey, secretKey, tenantId);
|
||||
if (!credential.valid()) {
|
||||
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;
|
||||
// return;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ package com.alibaba.nacos.client.identify;
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
public class Constants {
|
||||
public class IdentifyConstants {
|
||||
public static final String ACCESS_KEY = "accessKey";
|
||||
|
||||
public static final String SECRET_KEY = "secretKey";
|
@ -63,9 +63,7 @@ public class NacosNamingMaintainService implements NamingMaintainService {
|
||||
namespace = InitUtils.initNamespaceForNaming(properties);
|
||||
initServerAddr(properties);
|
||||
InitUtils.initWebRootContext();
|
||||
|
||||
serverProxy = new NamingProxy(namespace, endpoint, serverList);
|
||||
serverProxy.setProperties(properties);
|
||||
serverProxy = new NamingProxy(namespace, endpoint, serverList, 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.InitUtils;
|
||||
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.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
@ -45,15 +46,15 @@ import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Nacos Naming Service
|
||||
*
|
||||
* @author nkorange
|
||||
*/
|
||||
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
|
||||
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;
|
||||
|
||||
@ -92,8 +93,7 @@ public class NacosNamingService implements NamingService {
|
||||
initLogName(properties);
|
||||
|
||||
eventDispatcher = new EventDispatcher();
|
||||
serverProxy = new NamingProxy(namespace, endpoint, serverList);
|
||||
serverProxy.setProperties(properties);
|
||||
serverProxy = new NamingProxy(namespace, endpoint, serverList, properties);
|
||||
beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
|
||||
hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties),
|
||||
initPollingThreadCount(properties));
|
||||
|
@ -45,9 +45,6 @@ public class HttpClient {
|
||||
private static final boolean ENABLE_HTTPS = Boolean
|
||||
.getBoolean("com.alibaba.nacos.client.naming.tls.enable");
|
||||
|
||||
private static final String POST = "POST";
|
||||
private static final String PUT = "PUT";
|
||||
|
||||
static {
|
||||
// limit max redirection
|
||||
System.setProperty("http.maxRedirects", "5");
|
||||
@ -80,7 +77,6 @@ public class HttpClient {
|
||||
conn.setRequestMethod(method);
|
||||
conn.setDoOutput(true);
|
||||
if (StringUtils.isNotBlank(body)) {
|
||||
// fix: apache http nio framework must set some content to request body
|
||||
byte[] b = body.getBytes();
|
||||
conn.setRequestProperty("Content-Length", String.valueOf(b.length));
|
||||
conn.getOutputStream().write(b, 0, b.length);
|
||||
@ -88,7 +84,9 @@ public class HttpClient {
|
||||
conn.getOutputStream().close();
|
||||
}
|
||||
conn.connect();
|
||||
NAMING_LOGGER.debug("Request from server: " + url);
|
||||
if (NAMING_LOGGER.isDebugEnabled()) {
|
||||
NAMING_LOGGER.debug("Request from server: " + url);
|
||||
}
|
||||
return getResult(conn);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
|
@ -20,6 +20,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||
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.naming.CommonParams;
|
||||
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.SignUtil;
|
||||
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.TemplateUtils;
|
||||
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
|
||||
@ -71,14 +73,21 @@ public class NamingProxy {
|
||||
|
||||
private List<String> serversFromEndpoint = new ArrayList<String>();
|
||||
|
||||
private SecurityProxy securityProxy;
|
||||
|
||||
private long lastSrvRefTime = 0L;
|
||||
|
||||
private long vipSrvRefInterMillis = TimeUnit.SECONDS.toMillis(30);
|
||||
|
||||
private long securityInfoRefreshIntervalMills = TimeUnit.SECONDS.toMillis(5);
|
||||
|
||||
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.endpoint = endpoint;
|
||||
if (StringUtils.isNotEmpty(serverList)) {
|
||||
@ -88,19 +97,16 @@ public class NamingProxy {
|
||||
}
|
||||
}
|
||||
|
||||
initRefreshSrvIfNeed();
|
||||
initRefreshTask();
|
||||
}
|
||||
|
||||
private void initRefreshSrvIfNeed() {
|
||||
if (StringUtils.isEmpty(endpoint)) {
|
||||
return;
|
||||
}
|
||||
private void initRefreshTask() {
|
||||
|
||||
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
|
||||
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(2, new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable 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);
|
||||
return t;
|
||||
}
|
||||
@ -113,6 +119,14 @@ public class NamingProxy {
|
||||
}
|
||||
}, 0, vipSrvRefInterMillis, TimeUnit.MILLISECONDS);
|
||||
|
||||
|
||||
executorService.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
securityProxy.login(getServerList());
|
||||
}
|
||||
}, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
|
||||
|
||||
refreshSrvIfNeed();
|
||||
}
|
||||
|
||||
@ -377,13 +391,15 @@ public class NamingProxy {
|
||||
}
|
||||
|
||||
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;
|
||||
if (!CollectionUtils.isEmpty(serverList)) {
|
||||
snapshot = serverList;
|
||||
}
|
||||
|
||||
return reqAPI(api, params, body, snapshot, method);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public String callServer(String api, Map<String, String> params, String body, String curServer) throws NacosException {
|
||||
@ -394,7 +410,7 @@ public class NamingProxy {
|
||||
throws NacosException {
|
||||
long start = System.currentTimeMillis();
|
||||
long end = 0;
|
||||
checkSignature(params);
|
||||
injectSecurityInfo(params);
|
||||
List<String> headers = builderHeaders();
|
||||
|
||||
String url;
|
||||
@ -456,7 +472,7 @@ public class NamingProxy {
|
||||
if (StringUtils.isNotBlank(nacosDomain)) {
|
||||
for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
|
||||
try {
|
||||
return callServer(api, params, body, nacosDomain);
|
||||
return callServer(api, params, body, nacosDomain, method);
|
||||
} catch (NacosException e) {
|
||||
exception = e;
|
||||
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 sk = getSecretKey();
|
||||
params.put("app", AppNameUtils.getAppName());
|
||||
if (StringUtils.isEmpty(ak) && StringUtils.isEmpty(sk)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String signData = getSignData(params.get("serviceName"));
|
||||
String signature = SignUtil.sign(signData, sk);
|
||||
params.put("signature", signature);
|
||||
params.put("data", signData);
|
||||
params.put("ak", ak);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (StringUtils.isNotBlank(ak) && StringUtils.isNotBlank(sk)) {
|
||||
try {
|
||||
String signData = getSignData(params.get("serviceName"));
|
||||
String signature = SignUtil.sign(signData, sk);
|
||||
params.put("signature", signature);
|
||||
params.put("data", signData);
|
||||
params.put("ak", ak);
|
||||
} catch (Exception e) {
|
||||
NAMING_LOGGER.error("inject ak/sk failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nkorange
|
||||
* @author alibaba
|
||||
*/
|
||||
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;
|
||||
|
||||
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 org.slf4j.Logger;
|
||||
|
||||
@ -152,6 +154,36 @@ public class ParamUtil {
|
||||
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) {
|
||||
// 配置文件中输入的话,以 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.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* Config main
|
||||
*
|
||||
@ -30,7 +28,7 @@ import java.net.UnknownHostException;
|
||||
@SpringBootApplication
|
||||
public class Config {
|
||||
|
||||
public static void main(String[] args) throws UnknownHostException {
|
||||
public static void main(String[] 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,30 @@
|
||||
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;
|
||||
|
||||
import com.alibaba.nacos.config.server.auth.ConfigResourceParser;
|
||||
import com.alibaba.nacos.config.server.constant.Constants;
|
||||
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.result.ResultBuilder;
|
||||
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.utils.*;
|
||||
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.time.DateFormatUtils;
|
||||
import org.slf4j.Logger;
|
||||
@ -46,6 +50,7 @@ import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
private static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyyMMddHHmmss";
|
||||
|
||||
private final ConfigServletInner inner;
|
||||
|
||||
@ -88,6 +93,7 @@ public class ConfigController {
|
||||
* @throws NacosException
|
||||
*/
|
||||
@PostMapping
|
||||
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||
public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
||||
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
|
||||
@ -164,6 +170,7 @@ public class ConfigController {
|
||||
* @throws NacosException
|
||||
*/
|
||||
@GetMapping
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public void getConfig(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
||||
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
|
||||
@ -184,6 +191,7 @@ public class ConfigController {
|
||||
* @throws NacosException
|
||||
*/
|
||||
@GetMapping(params = "show=all")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public ConfigAllInfo detailConfigInfo(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("dataId") String dataId, @RequestParam("group") String group,
|
||||
@RequestParam(value = "tenant", required = false,
|
||||
@ -200,6 +208,7 @@ public class ConfigController {
|
||||
* @throws NacosException
|
||||
*/
|
||||
@DeleteMapping
|
||||
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||
public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("dataId") String dataId, //
|
||||
@RequestParam("group") String group, //
|
||||
@ -229,6 +238,7 @@ public class ConfigController {
|
||||
* @Param [request, response, dataId, group, tenant, tag]
|
||||
*/
|
||||
@DeleteMapping(params = "delType=ids")
|
||||
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||
public RestResult<Boolean> deleteConfigs(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam(value = "ids") List<Long> ids) {
|
||||
String clientIp = RequestUtil.getRemoteIp(request);
|
||||
@ -247,6 +257,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
@GetMapping("/catalog")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public RestResult<ConfigAdvanceInfo> getConfigAdvanceInfo(@RequestParam("dataId") String dataId,
|
||||
@RequestParam("group") String group,
|
||||
@RequestParam(value = "tenant", required = false,
|
||||
@ -262,6 +273,7 @@ public class ConfigController {
|
||||
* 比较MD5
|
||||
*/
|
||||
@PostMapping("/listener")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public void listener(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
|
||||
@ -287,6 +299,7 @@ public class ConfigController {
|
||||
* 订阅改配置的客户端信息
|
||||
*/
|
||||
@GetMapping("/listener")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public GroupkeyListenserStatus getListeners(@RequestParam("dataId") String dataId,
|
||||
@RequestParam("group") String group,
|
||||
@RequestParam(value = "tenant", required = false) String tenant,
|
||||
@ -306,6 +319,7 @@ public class ConfigController {
|
||||
* 查询配置信息,返回JSON格式。
|
||||
*/
|
||||
@GetMapping(params = "search=accurate")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public Page<ConfigInfo> searchConfig(@RequestParam("dataId") String dataId,
|
||||
@RequestParam("group") String group,
|
||||
@RequestParam(value = "appName", required = false) String appName,
|
||||
@ -335,6 +349,7 @@ public class ConfigController {
|
||||
* 模糊查询配置信息。不允许只根据内容模糊查询,即dataId和group都为NULL,但content不是NULL。这种情况下,返回所有配置。
|
||||
*/
|
||||
@GetMapping(params = "search=blur")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public Page<ConfigInfo> fuzzySearchConfig(@RequestParam("dataId") String dataId,
|
||||
@RequestParam("group") String group,
|
||||
@RequestParam(value = "appName", required = false) String appName,
|
||||
@ -361,6 +376,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
@DeleteMapping(params = "beta=true")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public RestResult<Boolean> stopBeta(@RequestParam(value = "dataId") String dataId,
|
||||
@RequestParam(value = "group") String group,
|
||||
@RequestParam(value = "tenant", required = false,
|
||||
@ -383,6 +399,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
@GetMapping(params = "beta=true")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public RestResult<ConfigInfo4Beta> queryBeta(@RequestParam(value = "dataId") String dataId,
|
||||
@RequestParam(value = "group") String group,
|
||||
@RequestParam(value = "tenant", required = false,
|
||||
@ -403,6 +420,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
@GetMapping(params = "export=true")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public ResponseEntity<byte[]> exportConfig(@RequestParam(value = "dataId", required = false) String dataId,
|
||||
@RequestParam(value = "group", required = false) String group,
|
||||
@RequestParam(value = "appName", required = false) String appName,
|
||||
@ -442,6 +460,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
@PostMapping(params = "import=true")
|
||||
@Secured(action = ActionTypes.WRITE, parser = ConfigResourceParser.class)
|
||||
public RestResult<Map<String, Object>> importAndPublishConfig(HttpServletRequest request,
|
||||
@RequestParam(value = "src_user", required = false) String srcUser,
|
||||
@RequestParam(value = "namespace", required = false) String namespace,
|
||||
@ -525,14 +544,20 @@ public class ConfigController {
|
||||
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
|
||||
}
|
||||
|
||||
@GetMapping(params = "clone=true")
|
||||
@PostMapping(params = "clone=true")
|
||||
public RestResult<Map<String, Object>> cloneConfig(HttpServletRequest request,
|
||||
@RequestParam(value = "src_user", required = false) String srcUser,
|
||||
@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")
|
||||
SameConfigPolicy policy) throws NacosException {
|
||||
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)) {
|
||||
namespace = "";
|
||||
@ -541,8 +566,14 @@ public class ConfigController {
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
|
||||
}
|
||||
|
||||
ids.removeAll(Collections.singleton(null));
|
||||
List<ConfigAllInfo> queryedDataList = persistService.findAllConfigInfo4Export(null, null, null, null, ids);
|
||||
List<Long> idList = new ArrayList<>(configBeansList.size());
|
||||
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()) {
|
||||
failedData.put("succCount", 0);
|
||||
@ -552,11 +583,12 @@ public class ConfigController {
|
||||
List<ConfigAllInfo> configInfoList4Clone = new ArrayList<>(queryedDataList.size());
|
||||
|
||||
for (ConfigAllInfo ci : queryedDataList) {
|
||||
SameNamespaceCloneConfigBean prarmBean = configBeansMap.get(ci.getId());
|
||||
ConfigAllInfo ci4save = new ConfigAllInfo();
|
||||
ci4save.setTenant(namespace);
|
||||
ci4save.setType(ci.getType());
|
||||
ci4save.setGroup(ci.getGroup());
|
||||
ci4save.setDataId(ci.getDataId());
|
||||
ci4save.setGroup((prarmBean != null && StringUtils.isNotBlank(prarmBean.getGroup())) ? prarmBean.getGroup() : ci.getGroup());
|
||||
ci4save.setDataId((prarmBean != null && StringUtils.isNotBlank(prarmBean.getDataId())) ? prarmBean.getDataId() : ci.getDataId());
|
||||
ci4save.setContent(ci.getContent());
|
||||
if (StringUtils.isNotBlank(ci.getAppName())) {
|
||||
ci4save.setAppName(ci.getAppName());
|
||||
@ -580,7 +612,7 @@ public class ConfigController {
|
||||
configInfo.getTenant(), requestIpApp, time.getTime(),
|
||||
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;
|
||||
}
|
||||
}
|
||||
String configType = cacheItem.getType();
|
||||
response.setHeader("Config-Type", (null != configType) ? configType : "text");
|
||||
}
|
||||
File file = 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;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
final String groupKey;
|
||||
public volatile String md5 = Constants.NULL;
|
||||
public volatile long lastModifiedTs;
|
||||
@ -123,5 +131,6 @@ public class CacheItem {
|
||||
public volatile Map<String, String> tagMd5;
|
||||
public volatile Map<String, Long> tagLastModifiedTs;
|
||||
public SimpleReadWriteLock rwLock = new SimpleReadWriteLock();
|
||||
public String type;
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ public class ConfigInfo extends ConfigInfoBase {
|
||||
|
||||
private String appName;
|
||||
|
||||
private String type;
|
||||
|
||||
public ConfigInfo() {
|
||||
|
||||
}
|
||||
@ -63,6 +65,14 @@ public class ConfigInfo extends ConfigInfoBase {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
|
@ -44,6 +44,8 @@ public enum ResultCodeEnum implements IResultCode {
|
||||
|
||||
DATA_EMPTY(100005, "导入的文件数据为空"),
|
||||
|
||||
NO_SELECTED_CONFIG(100006, "没有选择任何配制"),
|
||||
|
||||
|
||||
;
|
||||
|
||||
|
@ -57,9 +57,10 @@ public class ConfigService {
|
||||
/**
|
||||
* 保存配置文件,并缓存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);
|
||||
makeSure(groupKey);
|
||||
CacheItem ci = makeSure(groupKey);
|
||||
ci.setType(type);
|
||||
final int lockResult = tryWriteLock(groupKey);
|
||||
assert (lockResult != 0);
|
||||
|
||||
|
@ -176,7 +176,7 @@ public class DiskUtil {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -113,6 +113,7 @@ public class PersistService {
|
||||
info.setGroup(rs.getString("group_id"));
|
||||
info.setTenant(rs.getString("tenant_id"));
|
||||
info.setAppName(rs.getString("app_name"));
|
||||
info.setType(rs.getString("type"));
|
||||
|
||||
try {
|
||||
info.setContent(rs.getString("content"));
|
||||
@ -235,6 +236,11 @@ public class PersistService {
|
||||
} catch (SQLException e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
info.setType(rs.getString("type"));
|
||||
} catch (SQLException e) {
|
||||
// ignore
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@ -1325,7 +1331,7 @@ public class PersistService {
|
||||
final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName");
|
||||
final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags");
|
||||
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 ");
|
||||
List<String> paramList = new ArrayList<String>();
|
||||
paramList.add(tenantTmp);
|
||||
@ -1944,7 +1950,7 @@ public class PersistService {
|
||||
|
||||
public Page<ConfigInfoWrapper> findAllConfigInfoFragment(final long lastMaxId, final int pageSize) {
|
||||
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 ?,?";
|
||||
PaginationHelper<ConfigInfoWrapper> helper = new PaginationHelper<ConfigInfoWrapper>();
|
||||
try {
|
||||
@ -2927,8 +2933,8 @@ public class PersistService {
|
||||
final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
|
||||
try {
|
||||
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=?",
|
||||
new Object[]{dataId, group, tenantTmp}, CONFIG_INFO_ROW_MAPPER);
|
||||
"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);
|
||||
} catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null
|
||||
return null;
|
||||
} 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) {
|
||||
List<ConfigInfo> configs = new ArrayList<ConfigInfo>();
|
||||
for (Map<String, Object> map : list) {
|
||||
@ -3566,7 +3542,7 @@ public class PersistService {
|
||||
|
||||
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();
|
||||
|
||||
@ -3599,7 +3575,7 @@ public class PersistService {
|
||||
|
||||
private static String PATTERN_STR = "*";
|
||||
private final static int QUERY_LIMIT_SIZE = 50;
|
||||
private JdbcTemplate jt;
|
||||
private TransactionTemplate tjt;
|
||||
protected JdbcTemplate jt;
|
||||
protected TransactionTemplate tjt;
|
||||
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ class DumpProcessor implements TaskProcessor {
|
||||
|
||||
boolean result;
|
||||
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) {
|
||||
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(),
|
||||
cf.getLastModified());
|
||||
cf.getLastModified(), cf.getType());
|
||||
|
||||
final String content = cf.getContent();
|
||||
final String md5 = MD5.getInstance().getMD5String(content);
|
||||
|
@ -76,7 +76,7 @@ public class PaginationHelper<E> {
|
||||
page.setTotalCount(rowCountInt);
|
||||
|
||||
if (pageNo > pageCount) {
|
||||
return null;
|
||||
return page;
|
||||
}
|
||||
|
||||
final int startRow = (pageNo - 1) * pageSize;
|
||||
@ -121,7 +121,7 @@ public class PaginationHelper<E> {
|
||||
page.setTotalCount(rowCountInt);
|
||||
|
||||
if (pageNo > pageCount) {
|
||||
return null;
|
||||
return page;
|
||||
}
|
||||
|
||||
String selectSQL = sqlFetchRows;
|
||||
@ -162,7 +162,7 @@ public class PaginationHelper<E> {
|
||||
page.setTotalCount(rowCountInt);
|
||||
|
||||
if (pageNo > pageCount) {
|
||||
return null;
|
||||
return page;
|
||||
}
|
||||
|
||||
String selectSQL = sqlFetchRows;
|
||||
|
@ -19,7 +19,6 @@ import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Time util
|
||||
@ -29,13 +28,12 @@ import java.util.Date;
|
||||
public class TimeUtils {
|
||||
|
||||
public static Timestamp getCurrentTime() {
|
||||
Date date = new Date();
|
||||
return new Timestamp(date.getTime());
|
||||
return new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static String getCurrentTimeStr() {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(new Date());
|
||||
c.setTimeInMillis(System.currentTimeMillis());
|
||||
FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
|
||||
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
|
||||
);
|
||||
|
||||
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 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
|
||||
);
|
||||
|
||||
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 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.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.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* Spring cors config
|
||||
*
|
||||
* @author yshen
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @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
|
||||
public CorsFilter corsFilter() {
|
||||
@ -44,5 +60,4 @@ public class CorsConfig {
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
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;
|
||||
|
||||
import com.alibaba.nacos.console.config.WebSecurityConfig;
|
||||
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
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.context.SecurityContextHolder;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
@ -37,29 +38,23 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
private JwtTokenUtils tokenProvider;
|
||||
private JwtTokenManager tokenManager;
|
||||
|
||||
public JwtAuthenticationTokenFilter(JwtTokenUtils tokenProvider) {
|
||||
this.tokenProvider = tokenProvider;
|
||||
public JwtAuthenticationTokenFilter(JwtTokenManager tokenManager) {
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
if (jwt != null && !"".equals(jwt.trim()) && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
if (this.tokenProvider.validateToken(jwt)) {
|
||||
/**
|
||||
* get auth info
|
||||
*/
|
||||
Authentication authentication = this.tokenProvider.getAuthentication(jwt);
|
||||
/**
|
||||
* save user info to securityContext
|
||||
*/
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
if (StringUtils.isNotBlank(jwt) && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
this.tokenManager.validateToken(jwt);
|
||||
Authentication authentication = this.tokenManager.getAuthentication(jwt);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@ -67,12 +62,12 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
* Get token from header
|
||||
*/
|
||||
private String resolveToken(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader(WebSecurityConfig.AUTHORIZATION_HEADER);
|
||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
||||
return bearerToken.substring(7, bearerToken.length());
|
||||
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
|
||||
if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
String jwt = request.getParameter(WebSecurityConfig.AUTHORIZATION_TOKEN);
|
||||
if (StringUtils.hasText(jwt)) {
|
||||
String jwt = request.getParameter(Constants.ACCESS_TOKEN);
|
||||
if (StringUtils.isNotBlank(jwt)) {
|
||||
return jwt;
|
||||
}
|
||||
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
|
||||
* 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.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
@ -32,7 +33,7 @@ import org.springframework.stereotype.Component;
|
||||
public class CustomAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
@Autowired
|
||||
private CustomUserDetailsServiceImpl userDetailsService;
|
||||
private NacosUserDetailsServiceImpl userDetailsService;
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.console.security;
|
||||
package com.alibaba.nacos.console.security.nacos;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -37,10 +37,10 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest httpServletRequest,
|
||||
HttpServletResponse httpServletResponse,
|
||||
public void commence(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException e) throws IOException, ServletException {
|
||||
logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
|
||||
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
|
||||
logger.error("Responding with unauthorized error. Message:{}, url:{}", e.getMessage(), request.getRequestURI());
|
||||
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
|
||||
* 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.security.CustomUserDetailsServiceImpl;
|
||||
import com.alibaba.nacos.console.security.JwtAuthenticationEntryPoint;
|
||||
import com.alibaba.nacos.console.utils.JwtTokenUtils;
|
||||
import com.alibaba.nacos.console.security.nacos.users.NacosUserDetailsServiceImpl;
|
||||
import com.alibaba.nacos.core.auth.AuthConfigs;
|
||||
import com.alibaba.nacos.core.auth.AuthSystemTypes;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -43,64 +45,95 @@ import org.springframework.web.cors.CorsUtils;
|
||||
*/
|
||||
@Configuration
|
||||
@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_TOKEN = "access_token";
|
||||
|
||||
public static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ",";
|
||||
|
||||
@Autowired
|
||||
private CustomUserDetailsServiceImpl userDetailsService;
|
||||
public static final String LOGIN_ENTRY_POINT = "/v1/auth/login";
|
||||
|
||||
@Autowired
|
||||
private JwtAuthenticationEntryPoint unauthorizedHandler;
|
||||
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/v1/auth/**";
|
||||
|
||||
@Autowired
|
||||
private JwtTokenUtils tokenProvider;
|
||||
public static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
public static final String CONSOLE_RESOURCE_NAME_PREFIX = "console/";
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Autowired
|
||||
private JwtTokenManager tokenProvider;
|
||||
|
||||
@Autowired
|
||||
private AuthConfigs authConfigs;
|
||||
|
||||
@Autowired
|
||||
private NacosUserDetailsServiceImpl userDetailsService;
|
||||
|
||||
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
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
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
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
|
||||
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
|
||||
http.headers().cacheControl();
|
||||
if (StringUtils.isBlank(authConfigs.getNacosAuthSystemType())) {
|
||||
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
|
@ -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
|
||||
* 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 org.springframework.security.core.GrantedAuthority;
|
||||
@ -27,11 +27,11 @@ import java.util.Collection;
|
||||
*
|
||||
* @author wfnuser
|
||||
*/
|
||||
public class CustomUserDetails implements UserDetails {
|
||||
public class NacosUserDetails implements UserDetails {
|
||||
|
||||
private User user;
|
||||
|
||||
public CustomUserDetails(User user) {
|
||||
public NacosUserDetails(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 (
|
||||
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));
|
||||
CREATE INDEX tenant_info_tenant_id_idx ON tenant_info(tenant_id);
|
||||
|
||||
|
||||
CREATE TABLE users (
|
||||
username varchar(50) NOT NULL PRIMARY KEY,
|
||||
password varchar(500) NOT NULL,
|
||||
enabled boolean NOT NULL
|
||||
enabled boolean NOT NULL DEFAULT true
|
||||
);
|
||||
|
||||
CREATE TABLE roles (
|
||||
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 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
|
||||
#security.basic.enabled=false
|
||||
#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',
|
||||
delSelectedAlertContent: 'please select the configuration to delete',
|
||||
delSuccessMsg: 'delete successful',
|
||||
cloneEditableTitle: 'Modify Data Id and Group (optional)',
|
||||
},
|
||||
NewConfig: {
|
||||
newListingMain: 'Create Configuration',
|
||||
|
@ -311,6 +311,7 @@ const I18N_CONF = {
|
||||
delSelectedAlertTitle: '配置删除',
|
||||
delSelectedAlertContent: '请选择要删除的配置',
|
||||
delSuccessMsg: '删除成功',
|
||||
cloneEditableTitle: '修改 Data Id 和 Group (可选操作)',
|
||||
},
|
||||
NewConfig: {
|
||||
newListingMain: '新建配置',
|
||||
|
@ -39,6 +39,7 @@ import ShowCodeing from 'components/ShowCodeing';
|
||||
import DeleteDialog from 'components/DeleteDialog';
|
||||
import DashboardCard from './DashboardCard';
|
||||
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
|
||||
import axios from 'axios';
|
||||
|
||||
import './index.scss';
|
||||
import { LANGUAGE_KEY } from '../../../constants';
|
||||
@ -689,10 +690,9 @@ class ConfigurationManagement extends React.Component {
|
||||
}
|
||||
|
||||
exportData() {
|
||||
let url =
|
||||
`v1/cs/configs?export=true&group=${this.group}&tenant=${getParams('namespace')}&appName=${
|
||||
this.appName
|
||||
}&ids=&dataId=` + this.dataId;
|
||||
let url = `v1/cs/configs?export=true&group=${this.group}&tenant=${getParams(
|
||||
'namespace'
|
||||
)}&appName=${this.appName}&ids=&dataId=${this.dataId}`;
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
@ -783,19 +783,54 @@ class ConfigurationManagement extends React.Component {
|
||||
}
|
||||
let namespaces = data.data;
|
||||
let namespaceSelectData = [];
|
||||
namespaces.forEach(item => {
|
||||
if (self.state.nownamespace_id !== item.namespace) {
|
||||
let dataItem = {};
|
||||
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 namespaceSelecItemRender = item => {
|
||||
if (item.isCurrent) {
|
||||
return <span style={{ color: '#00AA00', 'font-weight': 'bold' }}>{item.label}</span>;
|
||||
} else {
|
||||
return <span>{item.label}</span>;
|
||||
}
|
||||
};
|
||||
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({
|
||||
title: locale.cloningConfiguration,
|
||||
footer: false,
|
||||
@ -822,6 +857,7 @@ class ConfigurationManagement extends React.Component {
|
||||
showSearch
|
||||
hasClear={false}
|
||||
mode="single"
|
||||
itemRender={namespaceSelecItemRender}
|
||||
dataSource={namespaceSelectData}
|
||||
onChange={(value, actionType, item) => {
|
||||
if (value) {
|
||||
@ -866,7 +902,7 @@ class ConfigurationManagement extends React.Component {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
@ -878,13 +914,21 @@ class ConfigurationManagement extends React.Component {
|
||||
document.getElementById('cloneTargetSpaceSelectErr').style.display = 'none';
|
||||
}
|
||||
let idsStr = '';
|
||||
configsTableSelected.forEach((value, key, map) => {
|
||||
idsStr = `${idsStr + key},`;
|
||||
let clonePostData = [];
|
||||
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 sameConfigPolicy = self.field.getValue('sameConfigPolicy');
|
||||
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() {
|
||||
self.openLoading();
|
||||
},
|
||||
@ -908,6 +952,25 @@ class ConfigurationManagement extends React.Component {
|
||||
{locale.startCloning}
|
||||
</Button>
|
||||
</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>
|
||||
),
|
||||
});
|
||||
@ -1089,7 +1152,9 @@ class ConfigurationManagement extends React.Component {
|
||||
rowSelection.selectedRowKeys = ids;
|
||||
this.setState({ rowSelection });
|
||||
configsTableSelected.clear();
|
||||
records.forEach(item => configsTableSelected.set(item.id, item));
|
||||
records.forEach((record, i) => {
|
||||
configsTableSelected.set(record.id, record);
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
</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>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
@ -79,5 +101,25 @@
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</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>
|
||||
</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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.naming.web;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
package com.alibaba.nacos.core.auth;
|
||||
|
||||
/**
|
||||
* Innovated By: Xuanyin.zy
|
||||
* Types of all auth implementations
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NeedAuth {
|
||||
public enum AuthSystemTypes {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.reflections.Reflections;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Basic methods for filter to use
|
||||
* Method cache
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.0.0
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Component
|
||||
public class FilterBase {
|
||||
public class ControllerMethodsCache {
|
||||
|
||||
private ConcurrentMap<String, Method> methodCache = new
|
||||
private ConcurrentMap<String, Method> methods = new
|
||||
ConcurrentHashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
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 ConcurrentMap<String, Method> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public Method getMethod(String httpMethod, String path) {
|
||||
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);
|
||||
String classPath = requestMapping.value()[0];
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (!method.isAnnotationPresent(RequestMapping.class)) {
|
||||
parseSubAnnotations(method, classPath);
|
||||
continue;
|
||||
}
|
||||
requestMapping = method.getAnnotation(RequestMapping.class);
|
||||
RequestMethod[] requestMethods = requestMapping.method();
|
||||
if (requestMethods.length == 0) {
|
||||
requestMethods = new RequestMethod[1];
|
||||
requestMethods[0] = RequestMethod.GET;
|
||||
}
|
||||
for (String methodPath : requestMapping.value()) {
|
||||
methodCache.put(requestMethods[0].name() + "-->" + classPath + methodPath, method);
|
||||
for (String classPath : requestMapping.value()) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (!method.isAnnotationPresent(RequestMapping.class)) {
|
||||
parseSubAnnotations(method, classPath);
|
||||
continue;
|
||||
}
|
||||
requestMapping = method.getAnnotation(RequestMapping.class);
|
||||
RequestMethod[] requestMethods = requestMapping.method();
|
||||
if (requestMethods.length == 0) {
|
||||
requestMethods = new RequestMethod[1];
|
||||
requestMethods[0] = RequestMethod.GET;
|
||||
}
|
||||
for (String methodPath : requestMapping.value()) {
|
||||
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) {
|
||||
if (ArrayUtils.isEmpty(requestPaths)) {
|
||||
methodCache.put(requestMethod.name() + "-->" + classPath, method);
|
||||
methods.put(requestMethod.name() + "-->" + classPath, method);
|
||||
return;
|
||||
}
|
||||
for (String requestPath : requestPaths) {
|
||||
methodCache.put(requestMethod.name() + "-->" + classPath + requestPath, method);
|
||||
methods.put(requestMethod.name() + "-->" + classPath + requestPath, method);
|
||||
}
|
||||
}
|
||||
}
|
71
core/src/main/java/com/alibaba/nacos/core/env/ReloadableConfigs.java
vendored
Normal file
71
core/src/main/java/com/alibaba/nacos/core/env/ReloadableConfigs.java
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.omg.PortableInterceptor.SYSTEM_EXCEPTION;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
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.env.ConfigurableEnvironment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@ -147,9 +148,10 @@ public class StartingSpringApplicationRunListener implements SpringApplicationRu
|
||||
}
|
||||
|
||||
private void logFilePath() {
|
||||
LOGGER.info("Nacos Log files: {}/logs/", NACOS_HOME);
|
||||
LOGGER.info("Nacos Conf files: {}/conf/", NACOS_HOME);
|
||||
LOGGER.info("Nacos Data files: {}/data/", NACOS_HOME);
|
||||
String[] dirNames = new String[]{"logs", "conf", "data"};
|
||||
for (String dirName: dirNames) {
|
||||
LOGGER.info("Nacos Log files: {}{}{}{}", NACOS_HOME, File.separatorChar, dirName, File.separatorChar);
|
||||
}
|
||||
}
|
||||
|
||||
private void logStarting() {
|
||||
|
@ -58,4 +58,6 @@ public interface Constants {
|
||||
String SYSTEM_PREFER_HOSTNAME_OVER_IP = "nacos.preferHostnameOverIp";
|
||||
String WEB_CONTEXT_PATH = "server.servlet.context-path";
|
||||
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");
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.naming.web;
|
||||
package com.alibaba.nacos.core.utils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
@ -7,6 +7,8 @@
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
|
||||
<include resource="META-INF/logback/nacos-included.xml"/>
|
||||
<include optional="true" resource="META-INF/logback/config-included.xml"/>
|
||||
<include optional="true" resource="META-INF/logback/naming-included.xml"/>
|
||||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
@ -32,11 +34,33 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="core-auth"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/core-auth.log</file>
|
||||
<append>true</append>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/core-auth.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
|
||||
<maxFileSize>2GB</maxFileSize>
|
||||
<MaxHistory>7</MaxHistory>
|
||||
<totalSizeCap>7GB</totalSizeCap>
|
||||
<cleanHistoryOnStart>true</cleanHistoryOnStart>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<Pattern>%date %level %msg%n%n</Pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root>
|
||||
<level value="INFO"/>
|
||||
<appender-ref ref="rootFile"/>
|
||||
</root>
|
||||
|
||||
<logger name="com.alibaba.nacos.core.auth" additivity="false">
|
||||
<level value="DEBUG"/>
|
||||
<appender-ref ref="core-auth"/>
|
||||
</logger>
|
||||
|
||||
<springProfile name="standalone">
|
||||
|
||||
<logger name="org.springframework">
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user