Merge remote-tracking branch 'upstream/develop' into feature/191226
This commit is contained in:
commit
f29079a411
@ -1,8 +1,6 @@
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- xchaos8@126.com
|
||||
- nacos_dev@linux.alibaba.com
|
||||
- dev-nacos@googlegroups.com
|
||||
- mw_configcenter@list.alibaba-inc.com
|
||||
on_success: change
|
||||
@ -30,7 +28,7 @@ before_install:
|
||||
|
||||
script:
|
||||
- mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true
|
||||
- mvn -Prelease-nacos clean install -U
|
||||
- mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
|
||||
- mvn clean package -Pit-test
|
||||
after_success:
|
||||
- mvn clean package -Pit-test
|
||||
|
2
BUILDING
2
BUILDING
@ -35,4 +35,4 @@ Build Instructions for NACOS
|
||||
Execute the following command in order to build the tar.gz packages and install JAR into local repository:
|
||||
|
||||
#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";
|
||||
|
@ -72,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
|
||||
*/
|
||||
@ -171,4 +181,6 @@ public class Constants {
|
||||
|
||||
public static final String SNOWFLAKE_INSTANCE_ID_GENERATOR = "snowflake";
|
||||
|
||||
public static final String HTTP_PREFIX = "http";
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -223,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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,64 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.auth;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.core.auth.Resource;
|
||||
import com.alibaba.nacos.core.auth.ResourceParser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Config resource parser
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class ConfigResourceParser implements ResourceParser {
|
||||
|
||||
private static final String AUTH_CONFIG_PREFIX = "config/";
|
||||
|
||||
@Override
|
||||
public String parseName(Object request) {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
String namespaceId = req.getParameter("tenant");
|
||||
String groupName = req.getParameter("group");
|
||||
String dataId = req.getParameter("dataId");
|
||||
|
||||
if (StringUtils.isBlank(namespaceId)) {
|
||||
namespaceId = Constants.DEFAULT_NAMESPACE_ID;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(namespaceId).append(Resource.SPLITTER);
|
||||
|
||||
if (StringUtils.isBlank(dataId)) {
|
||||
sb.append("*")
|
||||
.append(Resource.SPLITTER)
|
||||
.append(AUTH_CONFIG_PREFIX)
|
||||
.append("*");
|
||||
} else {
|
||||
sb.append(groupName)
|
||||
.append(Resource.SPLITTER)
|
||||
.append(AUTH_CONFIG_PREFIX)
|
||||
.append(dataId);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.auth;
|
||||
|
||||
/**
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class PermissionInfo {
|
||||
|
||||
/**
|
||||
* Role name
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* Resource
|
||||
*/
|
||||
private String resource;
|
||||
|
||||
/**
|
||||
* Action on resource
|
||||
*/
|
||||
private String action;
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setResource(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.auth;
|
||||
|
||||
|
||||
import com.alibaba.nacos.config.server.model.Page;
|
||||
import com.alibaba.nacos.config.server.service.PersistService;
|
||||
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
|
||||
/**
|
||||
* Permission CRUD service
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Service
|
||||
public class PermissionPersistService extends PersistService {
|
||||
|
||||
public Page<PermissionInfo> getPermissions(String role, int pageNo, int pageSize) {
|
||||
PaginationHelper<PermissionInfo> helper = new PaginationHelper<>();
|
||||
|
||||
String sqlCountRows = "select count(*) from permissions where ";
|
||||
String sqlFetchRows
|
||||
= "select role,resource,action from permissions where ";
|
||||
|
||||
String where = " role='" + role + "' ";
|
||||
|
||||
if (StringUtils.isBlank(role)) {
|
||||
where = " 1=1 ";
|
||||
}
|
||||
|
||||
try {
|
||||
Page<PermissionInfo> pageInfo = helper.fetchPage(jt, sqlCountRows
|
||||
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||
pageSize, PERMISSION_ROW_MAPPER);
|
||||
|
||||
if (pageInfo==null) {
|
||||
pageInfo = new Page<>();
|
||||
pageInfo.setTotalCount(0);
|
||||
pageInfo.setPageItems(new ArrayList<>());
|
||||
}
|
||||
|
||||
return pageInfo;
|
||||
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void addPermission(String role, String resource, String action) {
|
||||
|
||||
String sql = "INSERT into permissions (role, resource, action) VALUES (?, ?, ?)";
|
||||
|
||||
try {
|
||||
jt.update(sql, role, resource, action);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void deletePermission(String role, String resource, String action) {
|
||||
|
||||
String sql = "DELETE from permissions WHERE role=? and resource=? and action=?";
|
||||
try {
|
||||
jt.update(sql, role, resource, action);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PermissionRowMapper implements
|
||||
RowMapper<PermissionInfo> {
|
||||
@Override
|
||||
public PermissionInfo mapRow(ResultSet rs, int rowNum)
|
||||
throws SQLException {
|
||||
PermissionInfo info = new PermissionInfo();
|
||||
info.setResource(rs.getString("resource"));
|
||||
info.setAction(rs.getString("action"));
|
||||
info.setRole(rs.getString("role"));
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
private static final PermissionRowMapper PERMISSION_ROW_MAPPER = new PermissionRowMapper();
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.auth;
|
||||
|
||||
/**
|
||||
* Role Info
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class RoleInfo {
|
||||
|
||||
private String role;
|
||||
|
||||
private String username;
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.auth;
|
||||
|
||||
import com.alibaba.nacos.config.server.model.Page;
|
||||
import com.alibaba.nacos.config.server.model.User;
|
||||
import com.alibaba.nacos.config.server.service.PersistService;
|
||||
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
|
||||
|
||||
/**
|
||||
* Role CRUD service
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Service
|
||||
public class RolePersistService extends PersistService {
|
||||
|
||||
|
||||
public Page<RoleInfo> getRoles(int pageNo, int pageSize) {
|
||||
|
||||
PaginationHelper<RoleInfo> helper = new PaginationHelper<>();
|
||||
|
||||
String sqlCountRows = "select count(*) from (select distinct role from roles) roles where ";
|
||||
String sqlFetchRows
|
||||
= "select role,username from roles where ";
|
||||
|
||||
String where = " 1=1 ";
|
||||
|
||||
try {
|
||||
Page<RoleInfo> pageInfo = helper.fetchPage(jt, sqlCountRows
|
||||
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||
pageSize, ROLE_INFO_ROW_MAPPER);
|
||||
if (pageInfo == null) {
|
||||
pageInfo = new Page<>();
|
||||
pageInfo.setTotalCount(0);
|
||||
pageInfo.setPageItems(new ArrayList<>());
|
||||
}
|
||||
return pageInfo;
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public Page<RoleInfo> getRolesByUserName(String username, int pageNo, int pageSize) {
|
||||
|
||||
PaginationHelper<RoleInfo> helper = new PaginationHelper<>();
|
||||
|
||||
String sqlCountRows = "select count(*) from roles where ";
|
||||
String sqlFetchRows
|
||||
= "select role,username from roles where ";
|
||||
|
||||
String where = " username='" + username + "' ";
|
||||
|
||||
if (StringUtils.isBlank(username)) {
|
||||
where = " 1=1 ";
|
||||
}
|
||||
|
||||
try {
|
||||
return helper.fetchPage(jt, sqlCountRows
|
||||
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||
pageSize, ROLE_INFO_ROW_MAPPER);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void addRole(String role, String userName) {
|
||||
|
||||
String sql = "INSERT into roles (role, username) VALUES (?, ?)";
|
||||
|
||||
try {
|
||||
jt.update(sql, role, userName);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRole(String role) {
|
||||
String sql = "DELETE from roles WHERE role=?";
|
||||
try {
|
||||
jt.update(sql, role);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRole(String role, String username) {
|
||||
String sql = "DELETE from roles WHERE role=? and username=?";
|
||||
try {
|
||||
jt.update(sql, role, username);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RoleInfoRowMapper implements
|
||||
RowMapper<RoleInfo> {
|
||||
@Override
|
||||
public RoleInfo mapRow(ResultSet rs, int rowNum)
|
||||
throws SQLException {
|
||||
RoleInfo roleInfo = new RoleInfo();
|
||||
roleInfo.setRole(rs.getString("role"));
|
||||
roleInfo.setUsername(rs.getString("username"));
|
||||
return roleInfo;
|
||||
}
|
||||
}
|
||||
|
||||
private static final RoleInfoRowMapper ROLE_INFO_ROW_MAPPER = new RoleInfoRowMapper();
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.auth;
|
||||
|
||||
import com.alibaba.nacos.config.server.model.Page;
|
||||
import com.alibaba.nacos.config.server.model.User;
|
||||
import com.alibaba.nacos.config.server.service.PersistService;
|
||||
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.CannotGetJdbcConnectionException;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
|
||||
/**
|
||||
* User CRUD service
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Service
|
||||
public class UserPersistService extends PersistService {
|
||||
|
||||
public void createUser(String username, String password) {
|
||||
String sql = "INSERT into users (username, password, enabled) VALUES (?, ?, ?)";
|
||||
|
||||
try {
|
||||
jt.update(sql, username, password, true);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteUser(String username) {
|
||||
String sql = "DELETE from users WHERE username=?";
|
||||
try {
|
||||
jt.update(sql, username);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUserPassword(String username, String password) {
|
||||
try {
|
||||
jt.update(
|
||||
"UPDATE users SET password = ? WHERE username=?",
|
||||
password, username);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public User findUserByUsername(String username) {
|
||||
String sql = "SELECT username,password FROM users WHERE username=? ";
|
||||
try {
|
||||
return this.jt.queryForObject(sql, new Object[]{username}, USER_ROW_MAPPER);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
fatalLog.error("[db-other-error]" + e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Page<User> getUsers(int pageNo, int pageSize) {
|
||||
|
||||
PaginationHelper<User> helper = new PaginationHelper<>();
|
||||
|
||||
String sqlCountRows = "select count(*) from users where ";
|
||||
String sqlFetchRows
|
||||
= "select username,password from users where ";
|
||||
|
||||
String where = " 1=1 ";
|
||||
|
||||
try {
|
||||
Page<User> pageInfo = helper.fetchPage(jt, sqlCountRows
|
||||
+ where, sqlFetchRows + where, new ArrayList<String>().toArray(), pageNo,
|
||||
pageSize, USER_ROW_MAPPER);
|
||||
if (pageInfo == null) {
|
||||
pageInfo = new Page<>();
|
||||
pageInfo.setTotalCount(0);
|
||||
pageInfo.setPageItems(new ArrayList<>());
|
||||
}
|
||||
return pageInfo;
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UserRowMapper implements
|
||||
RowMapper<User> {
|
||||
@Override
|
||||
public User mapRow(ResultSet rs, int rowNum)
|
||||
throws SQLException {
|
||||
User info = new User();
|
||||
info.setUsername(rs.getString("username"));
|
||||
info.setPassword(rs.getString("password"));
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
private static final UserRowMapper USER_ROW_MAPPER = new UserRowMapper();
|
||||
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
@ -28,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;
|
||||
@ -90,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)
|
||||
@ -166,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)
|
||||
@ -186,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,
|
||||
@ -202,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, //
|
||||
@ -231,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);
|
||||
@ -249,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,
|
||||
@ -264,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);
|
||||
@ -289,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,
|
||||
@ -308,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,
|
||||
@ -337,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,
|
||||
@ -363,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,
|
||||
@ -385,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,
|
||||
@ -405,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,
|
||||
@ -444,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,
|
||||
|
@ -3237,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) {
|
||||
@ -3572,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();
|
||||
|
||||
@ -3605,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;
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
}
|
69
core/src/main/java/com/alibaba/nacos/core/env/ReloadableConfigs.java
vendored
Normal file
69
core/src/main/java/com/alibaba/nacos/core/env/ReloadableConfigs.java
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.core.env;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Reload application.properties
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Component
|
||||
public class ReloadableConfigs {
|
||||
|
||||
private Properties properties;
|
||||
|
||||
@Value("${spring.config.location:}")
|
||||
private String path;
|
||||
|
||||
private static final String FILE_PREFIX = "file:";
|
||||
|
||||
@Scheduled(fixedRate = 5000)
|
||||
public void reload() throws IOException {
|
||||
Properties properties = new Properties();
|
||||
InputStream inputStream = null;
|
||||
if (StringUtils.isNotBlank(path) && path.contains(FILE_PREFIX)) {
|
||||
String[] paths = path.split(",");
|
||||
path = paths[paths.length - 1].substring(FILE_PREFIX.length());
|
||||
}
|
||||
try {
|
||||
inputStream = new FileInputStream(new File(path + "application.properties"));
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
if (inputStream == null) {
|
||||
inputStream = getClass().getResourceAsStream("/application.properties");
|
||||
}
|
||||
properties.load(inputStream);
|
||||
inputStream.close();
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public final Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
@ -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;
|
@ -34,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">
|
||||
|
@ -104,7 +104,7 @@ else
|
||||
fi
|
||||
|
||||
JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb,${BASE_DIR}/plugins/mysql"
|
||||
|
||||
JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"
|
||||
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
|
||||
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar"
|
||||
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
|
||||
|
@ -1,23 +1,71 @@
|
||||
# spring
|
||||
|
||||
server.contextPath=/nacos
|
||||
#*************** Spring Boot Related Configurations ***************#
|
||||
### Default web context path:
|
||||
server.servlet.contextPath=/nacos
|
||||
### Default web server port:
|
||||
server.port=8848
|
||||
|
||||
#*************** Network Related Configurations ***************#
|
||||
### If prefer hostname over ip for Nacos server addresses in cluster.conf:
|
||||
# nacos.inetutils.prefer-hostname-over-ip=false
|
||||
|
||||
### Specify local server's IP:
|
||||
# nacos.inetutils.ip-address=
|
||||
|
||||
|
||||
|
||||
#*************** Config Module Related Configurations ***************#
|
||||
### If user MySQL as datasource:
|
||||
# spring.datasource.platform=mysql
|
||||
|
||||
### Count of DB:
|
||||
# db.num=1
|
||||
|
||||
### Connect URL of DB:
|
||||
# db.url.0=jdbc:mysql://1.1.1.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
|
||||
# db.user=user
|
||||
# db.password=password
|
||||
|
||||
|
||||
#*************** Naming Module Related Configurations ***************#
|
||||
### Data dispatch task execution period in milliseconds:
|
||||
# nacos.naming.distro.taskDispatchPeriod=200
|
||||
|
||||
### Data count of batch sync task:
|
||||
# nacos.naming.distro.batchSyncKeyCount=1000
|
||||
|
||||
### Retry delay in milliseconds if sync task failed:
|
||||
# nacos.naming.distro.syncRetryDelay=5000
|
||||
|
||||
### If enable data warmup. If set to false, the server would accept request without local data preparation:
|
||||
# nacos.naming.data.warmup=true
|
||||
|
||||
### If enable the instance auto expiration, kind like of health check of instance:
|
||||
# nacos.naming.expireInstance=true
|
||||
|
||||
|
||||
#*************** CMDB Module Related Configurations ***************#
|
||||
### The interval to dump external CMDB in seconds:
|
||||
# nacos.cmdb.dumpTaskInterval=3600
|
||||
|
||||
### The interval of polling data change event in seconds:
|
||||
# nacos.cmdb.eventTaskInterval=10
|
||||
|
||||
### The interval of loading labels in seconds:
|
||||
# nacos.cmdb.labelTaskInterval=300
|
||||
|
||||
### If turn on data loading task:
|
||||
# nacos.cmdb.loadDataAtStart=false
|
||||
|
||||
|
||||
# metrics for prometheus
|
||||
#*************** Metrics Related Configurations ***************#
|
||||
### Metrics for prometheus
|
||||
#management.endpoints.web.exposure.include=*
|
||||
|
||||
# metrics for elastic search
|
||||
### Metrics for elastic search
|
||||
management.metrics.export.elastic.enabled=false
|
||||
#management.metrics.export.elastic.host=http://localhost:9200
|
||||
|
||||
# metrics for influx
|
||||
### Metrics for influx
|
||||
management.metrics.export.influx.enabled=false
|
||||
#management.metrics.export.influx.db=springboot
|
||||
#management.metrics.export.influx.uri=http://localhost:8086
|
||||
@ -25,24 +73,41 @@ management.metrics.export.influx.enabled=false
|
||||
#management.metrics.export.influx.consistency=one
|
||||
#management.metrics.export.influx.compressed=true
|
||||
|
||||
|
||||
#*************** Access Log Related Configurations ***************#
|
||||
### If turn on the access log:
|
||||
server.tomcat.accesslog.enabled=true
|
||||
|
||||
### The access log pattern:
|
||||
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i
|
||||
# default current work dir
|
||||
|
||||
### The directory of access log:
|
||||
server.tomcat.basedir=
|
||||
|
||||
## spring security config
|
||||
### turn off security
|
||||
|
||||
#*************** Access Control Related Configurations ***************#
|
||||
### If enable spring security, this option is deprecated in 1.2.0:
|
||||
#spring.security.enabled=false
|
||||
#management.security=false
|
||||
#security.basic.enabled=false
|
||||
#nacos.security.ignore.urls=/**
|
||||
|
||||
nacos.security.ignore.urls=/,/**/*.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/**
|
||||
### The ignore urls of auth, is deprecated in 1.2.0:
|
||||
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.naming.distro.taskDispatchPeriod=200
|
||||
# nacos.naming.distro.batchSyncKeyCount=1000
|
||||
# nacos.naming.distro.syncRetryDelay=5000
|
||||
# nacos.naming.data.warmup=true
|
||||
# nacos.naming.expireInstance=true
|
||||
### The auth system to use, currently only 'nacos' is supported:
|
||||
nacos.core.auth.system.type=nacos
|
||||
|
||||
### If turn on auth system:
|
||||
nacos.core.auth.enabled=false
|
||||
|
||||
### The token expiration in seconds:
|
||||
nacos.core.auth.default.token.expire.seconds=18000
|
||||
|
||||
### The default token:
|
||||
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
|
||||
|
||||
### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay.
|
||||
nacos.core.auth.caching.enabled=true
|
||||
|
||||
|
||||
#*************** Istio Related Configurations ***************#
|
||||
### If turn on the MCP server:
|
||||
nacos.istio.mcp.server.enabled=false
|
||||
|
@ -376,6 +376,23 @@
|
||||
</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>
|
||||
|
||||
<logger name="com.alibaba.nacos.address.main" additivity="false">
|
||||
<level value="INFO"/>
|
||||
<appender-ref ref="nacos-address"/>
|
||||
@ -461,6 +478,11 @@
|
||||
<appender-ref ref="istio-main"/>
|
||||
</logger>
|
||||
|
||||
<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">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
|
@ -178,17 +178,26 @@ CREATE TABLE `tenant_info` (
|
||||
KEY `idx_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
|
||||
|
||||
CREATE TABLE users (
|
||||
username varchar(50) NOT NULL PRIMARY KEY,
|
||||
password varchar(500) NOT NULL,
|
||||
enabled boolean NOT NULL
|
||||
CREATE TABLE `users` (
|
||||
`username` varchar(50) NOT NULL PRIMARY KEY,
|
||||
`password` varchar(500) NOT NULL,
|
||||
`enabled` boolean NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE roles (
|
||||
username varchar(50) NOT NULL,
|
||||
role varchar(50) NOT NULL
|
||||
CREATE TABLE `roles` (
|
||||
`username` varchar(50) NOT NULL,
|
||||
`role` varchar(50) NOT NULL,
|
||||
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
|
||||
);
|
||||
|
||||
CREATE TABLE `permissions` (
|
||||
`role` varchar(50) NOT NULL,
|
||||
`permission` varchar(512) NOT NULL,
|
||||
`gmt_create` bigint NULL,
|
||||
`gmt_modified` bigint NULL,
|
||||
UNIQUE INDEX `idx_role_resource` (`role` ASC, `permission` ASC) USING BTREE
|
||||
);
|
||||
|
||||
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');
|
||||
|
@ -175,14 +175,22 @@ 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');
|
||||
|
@ -1,119 +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.naming.acl;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.core.utils.WebUtils;
|
||||
import com.alibaba.nacos.naming.core.Service;
|
||||
import com.alibaba.nacos.naming.core.ServiceManager;
|
||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.security.AccessControlException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author nkorange
|
||||
*/
|
||||
@Component
|
||||
public class AuthChecker {
|
||||
|
||||
@Autowired
|
||||
private ServiceManager serviceManager;
|
||||
|
||||
@Autowired
|
||||
private SwitchDomain switchDomain;
|
||||
|
||||
static private String[] APP_WHITE_LIST = {};
|
||||
static private String[] TOKEN_WHITE_LIST = {"traffic-scheduling@midware"};
|
||||
|
||||
public void doRaftAuth(HttpServletRequest req) throws Exception {
|
||||
String token = req.getParameter("token");
|
||||
if (StringUtils.equals(UtilsAndCommons.SUPER_TOKEN, token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String agent = WebUtils.getUserAgent(req);
|
||||
if (StringUtils.startsWith(agent, UtilsAndCommons.NACOS_SERVER_HEADER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalAccessException("illegal access,agent= " + agent + ", token=" + token);
|
||||
}
|
||||
|
||||
public void doAuth(Map<String, String[]> params, HttpServletRequest req) throws Exception {
|
||||
|
||||
String namespaceId = WebUtils.optional(req, CommonParams.NAMESPACE_ID,
|
||||
Constants.DEFAULT_NAMESPACE_ID);
|
||||
String serviceName = WebUtils.optional(req, "name", "");
|
||||
if (StringUtils.isEmpty(serviceName)) {
|
||||
serviceName = WebUtils.optional(req, "serviceName", "");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(serviceName)) {
|
||||
serviceName = WebUtils.optional(req, "tag", "");
|
||||
}
|
||||
|
||||
Service service = serviceManager.getService(namespaceId, serviceName);
|
||||
|
||||
if (service == null) {
|
||||
if (!req.getRequestURI().equals(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.API_SET_ALL_WEIGHTS)) {
|
||||
throw new IllegalStateException("auth failed, service does not exist: " + serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
String token = req.getParameter("token");
|
||||
String auth = req.getParameter("auth");
|
||||
String userName = req.getParameter("userName");
|
||||
if (StringUtils.isEmpty(auth) && StringUtils.isEmpty(token)) {
|
||||
throw new IllegalArgumentException("provide 'authInfo' or 'token' to access this service");
|
||||
}
|
||||
|
||||
// try valid token
|
||||
if ((service != null && StringUtils.equals(service.getToken(), token))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ArrayUtils.contains(TOKEN_WHITE_LIST, token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ArrayUtils.contains(APP_WHITE_LIST, userName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if token failed, try AuthInfo
|
||||
AuthInfo authInfo = AuthInfo.fromString(auth, WebUtils.getAcceptEncoding(req));
|
||||
if (authInfo == null) {
|
||||
throw new IllegalAccessException("invalid token or malformed auth info");
|
||||
}
|
||||
|
||||
if (!ArrayUtils.contains(APP_WHITE_LIST, authInfo.getAppKey())) {
|
||||
throw new AccessControlException("un-registered SDK app");
|
||||
}
|
||||
|
||||
if (!service.getOwners().contains(authInfo.getOperator())
|
||||
&& !switchDomain.getMasters().contains(authInfo.getOperator())) {
|
||||
throw new AccessControlException("dom already exists and you're not among the owners");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,83 +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.naming.acl;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author nkorange
|
||||
*/
|
||||
public class AuthInfo {
|
||||
private String operator;
|
||||
|
||||
private String appKey;
|
||||
|
||||
public AuthInfo() {
|
||||
}
|
||||
|
||||
public AuthInfo(String operator, String appKey) {
|
||||
this.operator = operator;
|
||||
this.appKey = appKey;
|
||||
}
|
||||
|
||||
public static AuthInfo fromString(String auth, String encoding) {
|
||||
try {
|
||||
String[] byteStrs = auth.split(",");
|
||||
byte[] bytes = new byte[byteStrs.length];
|
||||
for (int i = 0; i < byteStrs.length; i++) {
|
||||
bytes[i] = (byte) (~(Short.parseShort(byteStrs[i])));
|
||||
}
|
||||
|
||||
String contentStr = new String(bytes, encoding);
|
||||
String[] params = contentStr.split(":");
|
||||
return new AuthInfo(params[0], params[1]);
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
// very simple encryption is enough
|
||||
byte[] authBytes = (operator + ":" + appKey).getBytes(StandardCharsets.UTF_8);
|
||||
StringBuilder authBuilder = new StringBuilder();
|
||||
for (byte authByte : authBytes) {
|
||||
authBuilder.append((byte) (~((short) authByte))).append(",");
|
||||
}
|
||||
|
||||
return authBuilder.substring(0, authBuilder.length() - 1);
|
||||
} catch (Exception e) {
|
||||
return "Error while encrypt AuthInfo" + e;
|
||||
}
|
||||
}
|
||||
|
||||
public void setOperator(String operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public String getAppKey() {
|
||||
return appKey;
|
||||
}
|
||||
|
||||
public void setAppKey(String appKey) {
|
||||
this.appKey = appKey;
|
||||
}
|
||||
}
|
@ -293,7 +293,7 @@ public class RaftCore {
|
||||
|
||||
local.resetLeaderDue();
|
||||
|
||||
// if data should be persistent, usually this is always true:
|
||||
// if data should be persisted, usually this is true:
|
||||
if (KeyBuilder.matchPersistentKey(datum.key)) {
|
||||
raftStore.write(datum);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import com.alibaba.nacos.naming.core.ServiceManager;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import com.alibaba.nacos.naming.push.ClientInfo;
|
||||
import com.alibaba.nacos.naming.web.CanDistro;
|
||||
import com.alibaba.nacos.naming.web.OverrideParameterRequestWrapper;
|
||||
import com.alibaba.nacos.core.utils.OverrideParameterRequestWrapper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.codehaus.jackson.util.VersionUtil;
|
||||
|
@ -22,6 +22,8 @@ import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.api.naming.pojo.Cluster;
|
||||
import com.alibaba.nacos.api.naming.utils.NamingUtils;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import com.alibaba.nacos.core.utils.WebUtils;
|
||||
import com.alibaba.nacos.naming.core.Instance;
|
||||
import com.alibaba.nacos.naming.core.Service;
|
||||
@ -53,6 +55,7 @@ public class CatalogController {
|
||||
@Autowired
|
||||
protected ServiceManager serviceManager;
|
||||
|
||||
@Secured(action = ActionTypes.READ)
|
||||
@GetMapping("/service")
|
||||
public JSONObject serviceDetail(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,
|
||||
String serviceName) throws NacosException {
|
||||
@ -93,6 +96,7 @@ public class CatalogController {
|
||||
return detailView;
|
||||
}
|
||||
|
||||
@Secured(action = ActionTypes.READ)
|
||||
@RequestMapping(value = "/instances")
|
||||
public JSONObject instanceList(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,
|
||||
@RequestParam String serviceName,
|
||||
@ -134,6 +138,7 @@ public class CatalogController {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Secured(action = ActionTypes.READ)
|
||||
@GetMapping("/services")
|
||||
public Object listDetail(@RequestParam(required = false) boolean withInstances,
|
||||
@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,
|
||||
|
@ -21,6 +21,8 @@ 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.AbstractHealthChecker;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import com.alibaba.nacos.core.utils.WebUtils;
|
||||
import com.alibaba.nacos.naming.core.Cluster;
|
||||
import com.alibaba.nacos.naming.core.Service;
|
||||
@ -49,6 +51,7 @@ public class ClusterController {
|
||||
protected ServiceManager serviceManager;
|
||||
|
||||
@PutMapping
|
||||
@Secured(action = ActionTypes.WRITE)
|
||||
public String update(HttpServletRequest request) throws Exception {
|
||||
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
|
||||
|
@ -19,6 +19,8 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import com.alibaba.nacos.core.utils.WebUtils;
|
||||
import com.alibaba.nacos.naming.boot.RunningConfig;
|
||||
import com.alibaba.nacos.naming.core.Instance;
|
||||
@ -72,6 +74,7 @@ public class HealthController {
|
||||
|
||||
@CanDistro
|
||||
@PutMapping(value = {"", "/instance"})
|
||||
@Secured(action = ActionTypes.WRITE)
|
||||
public String update(HttpServletRequest request) {
|
||||
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
|
||||
|
@ -23,6 +23,8 @@ import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.api.naming.NamingResponseCode;
|
||||
import com.alibaba.nacos.api.naming.utils.NamingUtils;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import com.alibaba.nacos.core.utils.WebUtils;
|
||||
import com.alibaba.nacos.naming.core.Instance;
|
||||
import com.alibaba.nacos.naming.core.Service;
|
||||
@ -36,6 +38,7 @@ import com.alibaba.nacos.naming.push.ClientInfo;
|
||||
import com.alibaba.nacos.naming.push.DataSource;
|
||||
import com.alibaba.nacos.naming.push.PushService;
|
||||
import com.alibaba.nacos.naming.web.CanDistro;
|
||||
import com.alibaba.nacos.naming.web.NamingResourceParser;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -86,8 +89,10 @@ public class InstanceController {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@CanDistro
|
||||
@PostMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public String register(HttpServletRequest request) throws Exception {
|
||||
|
||||
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
|
||||
@ -99,6 +104,7 @@ public class InstanceController {
|
||||
|
||||
@CanDistro
|
||||
@DeleteMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public String deregister(HttpServletRequest request) throws Exception {
|
||||
Instance instance = getIPAddress(request);
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
|
||||
@ -118,6 +124,7 @@ public class InstanceController {
|
||||
|
||||
@CanDistro
|
||||
@PutMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public String update(HttpServletRequest request) throws Exception {
|
||||
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
|
||||
@ -137,6 +144,7 @@ public class InstanceController {
|
||||
|
||||
@CanDistro
|
||||
@PatchMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public String patch(HttpServletRequest request) throws Exception {
|
||||
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
|
||||
@ -180,6 +188,7 @@ public class InstanceController {
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
|
||||
public JSONObject list(HttpServletRequest request) throws Exception {
|
||||
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
|
||||
@ -204,6 +213,7 @@ public class InstanceController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
|
||||
public JSONObject detail(HttpServletRequest request) throws Exception {
|
||||
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
|
||||
@ -247,6 +257,7 @@ public class InstanceController {
|
||||
|
||||
@CanDistro
|
||||
@PutMapping("/beat")
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public JSONObject beat(HttpServletRequest request) throws Exception {
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
|
@ -19,6 +19,8 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import com.alibaba.nacos.core.utils.SystemUtils;
|
||||
import com.alibaba.nacos.naming.cluster.ServerListManager;
|
||||
import com.alibaba.nacos.naming.cluster.ServerStatusManager;
|
||||
@ -30,7 +32,6 @@ import com.alibaba.nacos.naming.core.ServiceManager;
|
||||
import com.alibaba.nacos.naming.misc.*;
|
||||
import com.alibaba.nacos.naming.pojo.ClusterStateView;
|
||||
import com.alibaba.nacos.naming.push.PushService;
|
||||
import com.alibaba.nacos.naming.web.NeedAuth;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -117,7 +118,7 @@ public class OperatorController {
|
||||
return switchDomain;
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@Secured(resource = "naming/switches", action = ActionTypes.WRITE)
|
||||
@PutMapping("/switches")
|
||||
public String updateSwitch(@RequestParam(required = false) boolean debug,
|
||||
@RequestParam String entry, @RequestParam String value) throws Exception {
|
||||
@ -127,6 +128,7 @@ public class OperatorController {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@Secured(resource = "naming/metrics", action = ActionTypes.READ)
|
||||
@GetMapping("/metrics")
|
||||
public JSONObject metrics(HttpServletRequest request) {
|
||||
|
||||
|
@ -34,7 +34,6 @@ import com.alibaba.nacos.naming.core.ServiceManager;
|
||||
import com.alibaba.nacos.naming.misc.NetUtils;
|
||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import com.alibaba.nacos.naming.web.NeedAuth;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -67,7 +66,6 @@ public class RaftController {
|
||||
@Autowired
|
||||
private RaftCore raftCore;
|
||||
|
||||
@NeedAuth
|
||||
@PostMapping("/vote")
|
||||
public JSONObject vote(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
@ -77,7 +75,6 @@ public class RaftController {
|
||||
return JSON.parseObject(JSON.toJSONString(peer));
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@PostMapping("/beat")
|
||||
public JSONObject beat(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
@ -93,7 +90,6 @@ public class RaftController {
|
||||
return JSON.parseObject(JSON.toJSONString(peer));
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@GetMapping("/peer")
|
||||
public JSONObject getPeer(HttpServletRequest request, HttpServletResponse response) {
|
||||
List<RaftPeer> peers = raftCore.getPeers();
|
||||
@ -113,7 +109,6 @@ public class RaftController {
|
||||
return JSON.parseObject(JSON.toJSONString(peer));
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@PutMapping("/datum/reload")
|
||||
public String reloadDatum(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String key = WebUtils.required(request, "key");
|
||||
@ -121,7 +116,6 @@ public class RaftController {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@PostMapping("/datum")
|
||||
public String publish(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
@ -152,7 +146,6 @@ public class RaftController {
|
||||
throw new NacosException(NacosException.INVALID_PARAM, "unknown type publish key: " + key);
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@DeleteMapping("/datum")
|
||||
public String delete(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
@ -163,7 +156,6 @@ public class RaftController {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@GetMapping("/datum")
|
||||
public String get(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
@ -197,7 +189,6 @@ public class RaftController {
|
||||
return result;
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@PostMapping("/datum/commit")
|
||||
public String onPublish(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
@ -229,7 +220,6 @@ public class RaftController {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@NeedAuth
|
||||
@DeleteMapping("/datum/commit")
|
||||
public String onDelete(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
|
@ -24,6 +24,8 @@ import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.api.naming.utils.NamingUtils;
|
||||
import com.alibaba.nacos.api.selector.SelectorType;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import com.alibaba.nacos.core.auth.ActionTypes;
|
||||
import com.alibaba.nacos.core.auth.Secured;
|
||||
import com.alibaba.nacos.core.utils.WebUtils;
|
||||
import com.alibaba.nacos.naming.cluster.ServerListManager;
|
||||
import com.alibaba.nacos.naming.core.*;
|
||||
@ -33,6 +35,7 @@ import com.alibaba.nacos.naming.pojo.Subscriber;
|
||||
import com.alibaba.nacos.naming.selector.LabelSelector;
|
||||
import com.alibaba.nacos.naming.selector.NoneSelector;
|
||||
import com.alibaba.nacos.naming.selector.Selector;
|
||||
import com.alibaba.nacos.naming.web.NamingResourceParser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -64,6 +67,7 @@ public class ServiceController {
|
||||
private SubscribeManager subscribeManager;
|
||||
|
||||
@PostMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public String create(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,
|
||||
@RequestParam String serviceName,
|
||||
@RequestParam(required = false) float protectThreshold,
|
||||
@ -98,6 +102,7 @@ public class ServiceController {
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public String remove(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,
|
||||
@RequestParam String serviceName) throws Exception {
|
||||
|
||||
@ -107,6 +112,7 @@ public class ServiceController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
|
||||
public JSONObject detail(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,
|
||||
@RequestParam String serviceName) throws NacosException {
|
||||
|
||||
@ -138,6 +144,7 @@ public class ServiceController {
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
|
||||
public JSONObject list(HttpServletRequest request) throws Exception {
|
||||
|
||||
int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo"));
|
||||
@ -209,6 +216,7 @@ public class ServiceController {
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
|
||||
public String update(HttpServletRequest request) throws Exception {
|
||||
|
||||
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
|
||||
@ -238,6 +246,7 @@ public class ServiceController {
|
||||
}
|
||||
|
||||
@RequestMapping("/names")
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
|
||||
public JSONObject searchService(@RequestParam(defaultValue = StringUtils.EMPTY) String namespaceId,
|
||||
@RequestParam(defaultValue = StringUtils.EMPTY) String expr,
|
||||
@RequestParam(required = false) boolean responsibleOnly) {
|
||||
@ -350,6 +359,7 @@ public class ServiceController {
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/subscribers")
|
||||
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
|
||||
public JSONObject subscribers(HttpServletRequest request) {
|
||||
|
||||
int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo"));
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.alibaba.nacos.naming.exception;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.core.auth.AccessException;
|
||||
import com.alibaba.nacos.naming.misc.Loggers;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
@ -62,7 +62,6 @@ public class GlobalExecutor {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
private static ScheduledExecutorService dataSyncExecutor =
|
||||
new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
|
||||
@Override
|
||||
|
@ -419,10 +419,7 @@ public class HttpClient {
|
||||
inputStream = new GZIPInputStream(inputStream);
|
||||
}
|
||||
|
||||
HttpResult result = new HttpResult(respCode, IoUtils.toString(inputStream, getCharset(conn)), respHeaders);
|
||||
inputStream.close();
|
||||
|
||||
return result;
|
||||
return new HttpResult(respCode, IoUtils.toString(inputStream, getCharset(conn)), respHeaders);
|
||||
}
|
||||
|
||||
private static String getCharset(HttpURLConnection conn) {
|
||||
|
@ -195,18 +195,6 @@ public class UtilsAndCommons {
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static String getSwitchDomainKey() {
|
||||
return UtilsAndCommons.DOMAINS_DATA_ID_PRE + UtilsAndCommons.SWITCH_DOMAIN_NAME;
|
||||
}
|
||||
|
@ -1,104 +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.naming.web;
|
||||
|
||||
import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.naming.acl.AuthChecker;
|
||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
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;
|
||||
import java.security.AccessControlException;
|
||||
|
||||
/**
|
||||
* @author nkorange
|
||||
*/
|
||||
public class AuthFilter implements Filter {
|
||||
|
||||
private static final String[] NAMESPACE_FORBIDDEN_STRINGS = new String[]{"..", "/"};
|
||||
|
||||
@Autowired
|
||||
private AuthChecker authChecker;
|
||||
|
||||
@Autowired
|
||||
private SwitchDomain switchDomain;
|
||||
|
||||
@Autowired
|
||||
private FilterBase filterBase;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
|
||||
FilterChain filterChain) throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest req = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse resp = (HttpServletResponse) servletResponse;
|
||||
|
||||
try {
|
||||
String path = new URI(req.getRequestURI()).getPath();
|
||||
Method method = filterBase.getMethod(req.getMethod(), path);
|
||||
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
if (method.isAnnotationPresent(NeedAuth.class) && !switchDomain.isEnableAuthentication()) {
|
||||
// leave it empty.
|
||||
}
|
||||
|
||||
// Check namespace:
|
||||
String namespaceId = req.getParameter(CommonParams.NAMESPACE_ID);
|
||||
|
||||
if (StringUtils.isNotBlank(namespaceId)) {
|
||||
|
||||
if (namespaceId.contains(NAMESPACE_FORBIDDEN_STRINGS[0]) || namespaceId.contains(NAMESPACE_FORBIDDEN_STRINGS[1])) {
|
||||
throw new IllegalArgumentException("forbidden namespace: " + namespaceId);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (AccessControlException e) {
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "access denied: " + UtilsAndCommons.getAllExceptionMsg(e));
|
||||
return;
|
||||
} catch (NoSuchMethodException e) {
|
||||
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, "no such api");
|
||||
return;
|
||||
} catch (IllegalArgumentException e) {
|
||||
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, UtilsAndCommons.getAllExceptionMsg(e));
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
"Server failed," + UtilsAndCommons.getAllExceptionMsg(e));
|
||||
return;
|
||||
}
|
||||
filterChain.doFilter(req, resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
@ -19,10 +19,12 @@ import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import com.alibaba.nacos.core.code.ControllerMethodsCache;
|
||||
import com.alibaba.nacos.core.utils.ExceptionUtil;
|
||||
import com.alibaba.nacos.core.utils.OverrideParameterRequestWrapper;
|
||||
import com.alibaba.nacos.naming.core.DistroMapper;
|
||||
import com.alibaba.nacos.naming.misc.HttpClient;
|
||||
import com.alibaba.nacos.naming.misc.Loggers;
|
||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -52,10 +54,7 @@ public class DistroFilter implements Filter {
|
||||
private DistroMapper distroMapper;
|
||||
|
||||
@Autowired
|
||||
private SwitchDomain switchDomain;
|
||||
|
||||
@Autowired
|
||||
private FilterBase filterBase;
|
||||
private ControllerMethodsCache controllerMethodsCache;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
@ -80,7 +79,11 @@ public class DistroFilter implements Filter {
|
||||
if (StringUtils.isBlank(serviceName)) {
|
||||
serviceName = req.getParameter("dom");
|
||||
}
|
||||
Method method = filterBase.getMethod(req.getMethod(), path);
|
||||
|
||||
if (StringUtils.isNotBlank(serviceName)) {
|
||||
serviceName = serviceName.trim();
|
||||
}
|
||||
Method method = controllerMethodsCache.getMethod(req.getMethod(), path);
|
||||
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(req.getMethod() + " " + path);
|
||||
@ -139,14 +142,15 @@ public class DistroFilter implements Filter {
|
||||
requestWrapper.addParameter(CommonParams.SERVICE_NAME, groupedServiceName);
|
||||
filterChain.doFilter(requestWrapper, resp);
|
||||
} catch (AccessControlException e) {
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "access denied: " + UtilsAndCommons.getAllExceptionMsg(e));
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "access denied: " + ExceptionUtil.getAllExceptionMsg(e));
|
||||
return;
|
||||
} catch (NoSuchMethodException e) {
|
||||
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, "no such api: " + e.getMessage());
|
||||
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED,
|
||||
"no such api:" + req.getMethod() + ":" + req.getRequestURI());
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
"Server failed," + UtilsAndCommons.getAllExceptionMsg(e));
|
||||
"Server failed," + ExceptionUtil.getAllExceptionMsg(e));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -47,18 +47,6 @@ public class NamingConfig {
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean authFilterRegistration() {
|
||||
FilterRegistrationBean<AuthFilter> registration = new FilterRegistrationBean<>();
|
||||
|
||||
registration.setFilter(authFilter());
|
||||
registration.addUrlPatterns("/v1/ns/*");
|
||||
registration.setName("authFilter");
|
||||
registration.setOrder(5);
|
||||
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DistroFilter distroFilter() {
|
||||
return new DistroFilter();
|
||||
@ -69,9 +57,4 @@ public class NamingConfig {
|
||||
return new TrafficReviseFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthFilter authFilter() {
|
||||
return new AuthFilter();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.naming.web;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.naming.CommonParams;
|
||||
import com.alibaba.nacos.api.naming.utils.NamingUtils;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Naming resource parser
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class NamingResourceParser implements ResourceParser {
|
||||
|
||||
private static final String AUTH_NAMING_PREFIX = "naming/";
|
||||
|
||||
@Override
|
||||
public String parseName(Object request) {
|
||||
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
|
||||
String namespaceId = req.getParameter(CommonParams.NAMESPACE_ID);
|
||||
String serviceName = req.getParameter(CommonParams.SERVICE_NAME);
|
||||
String groupName = req.getParameter(CommonParams.GROUP_NAME);
|
||||
if (StringUtils.isBlank(groupName)) {
|
||||
groupName = NamingUtils.getGroupName(serviceName);
|
||||
}
|
||||
serviceName = NamingUtils.getServiceName(serviceName);
|
||||
|
||||
if (StringUtils.isBlank(namespaceId)) {
|
||||
namespaceId = Constants.DEFAULT_NAMESPACE_ID;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(namespaceId).append(Resource.SPLITTER);
|
||||
|
||||
if (StringUtils.isBlank(serviceName)) {
|
||||
sb.append("*")
|
||||
.append(Resource.SPLITTER)
|
||||
.append(AUTH_NAMING_PREFIX)
|
||||
.append("*");
|
||||
} else {
|
||||
sb.append(groupName)
|
||||
.append(Resource.SPLITTER)
|
||||
.append(AUTH_NAMING_PREFIX)
|
||||
.append(serviceName);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -16,11 +16,11 @@
|
||||
package com.alibaba.nacos.naming.web;
|
||||
|
||||
import com.alibaba.nacos.common.utils.HttpMethod;
|
||||
import com.alibaba.nacos.core.utils.Constants;
|
||||
import com.alibaba.nacos.core.utils.WebUtils;
|
||||
import com.alibaba.nacos.naming.cluster.ServerStatus;
|
||||
import com.alibaba.nacos.naming.cluster.ServerStatusManager;
|
||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@ -73,7 +73,7 @@ public class TrafficReviseFilter implements Filter {
|
||||
// requests from peer server should be let pass:
|
||||
String agent = WebUtils.getUserAgent(req);
|
||||
|
||||
if (StringUtils.startsWith(agent, UtilsAndCommons.NACOS_SERVER_HEADER)) {
|
||||
if (StringUtils.startsWith(agent, Constants.NACOS_SERVER_HEADER)) {
|
||||
filterChain.doFilter(req, resp);
|
||||
return;
|
||||
}
|
||||
|
23
pom.xml
23
pom.xml
@ -525,6 +525,11 @@
|
||||
<artifactId>nacos-example</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-address</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
@ -539,12 +544,12 @@
|
||||
<version>1.2.58</version>
|
||||
</dependency>
|
||||
|
||||
<!-- javax libs-->
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs</artifactId>
|
||||
<version>2.1</version>
|
||||
</dependency>
|
||||
<!-- <!– javax libs–>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>javax.ws.rs</groupId>-->
|
||||
<!-- <artifactId>javax.ws.rs</artifactId>-->
|
||||
<!-- <version>1.0</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
@ -783,6 +788,12 @@
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.9.11</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
10
test/pom.xml
10
test/pom.xml
@ -75,6 +75,16 @@
|
||||
<artifactId>nacos-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-console</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-address</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
@ -0,0 +1,52 @@
|
||||
package com.alibaba.nacos.test.base;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Http client for test module
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class HttpClient4Test {
|
||||
|
||||
protected URL base;
|
||||
|
||||
@Autowired
|
||||
protected TestRestTemplate restTemplate;
|
||||
|
||||
protected <T> ResponseEntity<T> request(String path, MultiValueMap<String, String> params, Class<T> clazz) {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
HttpEntity<?> entity = new HttpEntity<T>(headers);
|
||||
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(this.base.toString() + path)
|
||||
.queryParams(params);
|
||||
|
||||
return this.restTemplate.exchange(
|
||||
builder.toUriString(), HttpMethod.GET, entity, clazz);
|
||||
}
|
||||
|
||||
protected <T> ResponseEntity<T> request(String path, MultiValueMap<String, String> params, Class<T> clazz, HttpMethod httpMethod) {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
HttpEntity<?> entity = new HttpEntity<T>(headers);
|
||||
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(this.base.toString() + path)
|
||||
.queryParams(params);
|
||||
|
||||
return this.restTemplate.exchange(
|
||||
builder.toUriString(), httpMethod, entity, clazz);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.nacos.test.naming;
|
||||
package com.alibaba.nacos.test.base;
|
||||
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
@ -20,7 +20,7 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.nacos.api.config.ConfigService;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.config.server.Config;
|
||||
import com.alibaba.nacos.test.naming.Params;
|
||||
import com.alibaba.nacos.test.base.Params;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.test.core.auth;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.alibaba.nacos.Nacos;
|
||||
import com.alibaba.nacos.config.server.model.Page;
|
||||
import com.alibaba.nacos.core.auth.Permission;
|
||||
import com.alibaba.nacos.test.base.HttpClient4Test;
|
||||
import com.alibaba.nacos.test.base.Params;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Nacos.class, properties = {"server.servlet.context-path=/nacos", "server.port=7001"},
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class Permission_ITCase extends HttpClient4Test {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
TimeUnit.SECONDS.sleep(5L);
|
||||
String url = String.format("http://localhost:%d/", port);
|
||||
this.base = new URL(url);
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() {
|
||||
|
||||
// Delete permission:
|
||||
ResponseEntity<String> response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("resource", "public:*:*")
|
||||
.appendParam("action", "rw")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Delete permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("resource", "test1:*:*")
|
||||
.appendParam("action", "r")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Delete role:
|
||||
response = request("/nacos/v1/auth/roles",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("username", "username2")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Delete a user:
|
||||
response = request("/nacos/v1/auth/users",
|
||||
Params.newParams()
|
||||
.appendParam("username", "username3")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void login() {
|
||||
|
||||
ResponseEntity<String> response = request("/nacos/v1/auth/users/login",
|
||||
Params.newParams()
|
||||
.appendParam("username", "nacos")
|
||||
.appendParam("password", "nacos")
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
JSONObject json = JSON.parseObject(response.getBody());
|
||||
Assert.assertTrue(json.containsKey("accessToken"));
|
||||
accessToken = json.getString("accessToken");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDeleteQueryPermission() {
|
||||
|
||||
login();
|
||||
|
||||
// Create a user:
|
||||
ResponseEntity<String> response = request("/nacos/v1/auth/users",
|
||||
Params.newParams()
|
||||
.appendParam("username", "username3")
|
||||
.appendParam("password", "password1")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Create role:
|
||||
response = request("/nacos/v1/auth/roles",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("username", "username3")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Create permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("resource", "public:*:*")
|
||||
.appendParam("action", "rw")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Create another permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("resource", "test1:*:*")
|
||||
.appendParam("action", "r")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Query permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("pageNo", "1")
|
||||
.appendParam("pageSize", "10")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
Page<Permission> permissionPage = JSON.parseObject(response.getBody(), new TypeReference<Page<Permission>>() {
|
||||
});
|
||||
|
||||
Assert.assertNotNull(permissionPage);
|
||||
Assert.assertNotNull(permissionPage.getPageItems());
|
||||
|
||||
boolean found1=false,found2=false;
|
||||
for (Permission permission : permissionPage.getPageItems()) {
|
||||
if (permission.getResource().equals("public:*:*") && permission.getAction().equals("rw")) {
|
||||
found1 = true;
|
||||
}
|
||||
if (permission.getResource().equals("test1:*:*") && permission.getAction().equals("r")) {
|
||||
found2 = true;
|
||||
}
|
||||
if (found1 && found2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.assertTrue(found1);
|
||||
Assert.assertTrue(found2);
|
||||
|
||||
// Delete permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("resource", "public:*:*")
|
||||
.appendParam("action", "rw")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Query permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("pageNo", "1")
|
||||
.appendParam("pageSize", "10")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
permissionPage = JSON.parseObject(response.getBody(), new TypeReference<Page<Permission>>() {
|
||||
});
|
||||
|
||||
Assert.assertNotNull(permissionPage);
|
||||
Assert.assertNotNull(permissionPage.getPageItems());
|
||||
|
||||
found1=false;
|
||||
found2=false;
|
||||
|
||||
for (Permission permission : permissionPage.getPageItems()) {
|
||||
if (permission.getResource().equals("public:*:*") && permission.getAction().equals("rw")) {
|
||||
found1 = true;
|
||||
}
|
||||
if (permission.getResource().equals("test1:*:*") && permission.getAction().equals("r")) {
|
||||
found2 = true;
|
||||
}
|
||||
}
|
||||
Assert.assertFalse(found1);
|
||||
Assert.assertTrue(found2);
|
||||
|
||||
// Delete permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("resource", "test1:*:*")
|
||||
.appendParam("action", "r")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
// Query permission:
|
||||
response = request("/nacos/v1/auth/permissions",
|
||||
Params.newParams()
|
||||
.appendParam("role", "role1")
|
||||
.appendParam("pageNo", "1")
|
||||
.appendParam("pageSize", "10")
|
||||
.appendParam("accessToken", accessToken)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
permissionPage = JSON.parseObject(response.getBody(), new TypeReference<Page<Permission>>() {
|
||||
});
|
||||
|
||||
Assert.assertNotNull(permissionPage);
|
||||
Assert.assertNotNull(permissionPage.getPageItems());
|
||||
|
||||
found1=false;
|
||||
found2=false;
|
||||
|
||||
for (Permission permission : permissionPage.getPageItems()) {
|
||||
if (permission.getResource().equals("public:*:*") && permission.getAction().equals("rw")) {
|
||||
found1 = true;
|
||||
}
|
||||
if (permission.getResource().equals("test1:*:*") && permission.getAction().equals("r")) {
|
||||
found2 = true;
|
||||
}
|
||||
}
|
||||
Assert.assertFalse(found1);
|
||||
Assert.assertFalse(found2);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user