⬆️ #2838 up nacos 2.0.2

This commit is contained in:
lbw committed 2021-06-25 16:58:41 +08:00
1 parent 864e0948a3
commit c85bdab9be
154 files changed
+808 -142

No files matched your search

@@ -16,7 +16,10 @@
package com.alibaba.nacos;
import cn.hutool.core.lang.Validator;
import cn.hutool.system.SystemUtil;
import com.alibaba.nacos.config.ConfigConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -26,15 +29,32 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* <p>
* nacos console 源码运行,方便开发 生产建议从官网下载最新版配置运行
*/
@Slf4j
@EnableScheduling
@SpringBootApplication
public class PigxNacosApplication {
public static void main(String[] args) {
if (initEnv()) {
SpringApplication.run(PigxNacosApplication.class, args);
}
}
/**
* 初始化运行环境
*/
private static boolean initEnv() {
// 特殊处理 window 且用户名包含中文的用户,避免RocksDB 初始化失败
// System.setProperty(SystemUtil.USER_HOME, "不包含中文的路径");
if (SystemUtil.getOsInfo().isWindows() && Validator.hasChinese(SystemUtil.getUserInfo().getHomeDir())) {
log.error("路径包含中文,需要打开以上注释配置指定不包含中文目录");
return false;
}
System.setProperty(ConfigConstants.STANDALONE_MODE, "true");
System.setProperty(ConfigConstants.AUTH_ENABLED, "false");
System.setProperty(ConfigConstants.LOG_BASEDIR, "logs");
SpringApplication.run(PigxNacosApplication.class, args);
return true;
}
}
+19 -5
View File
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.config;
import javax.annotation.PostConstruct;
package com.alibaba.nacos.config;
import com.alibaba.nacos.core.code.ControllerMethodsCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableScheduling;
@@ -27,7 +27,12 @@ import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import javax.annotation.PostConstruct;
import java.time.ZoneId;
/**
* Console config.
*
* @author yshen
* @author nkorange
* @since 1.2.0
@@ -40,24 +45,33 @@ public class ConsoleConfig {
@Autowired
private ControllerMethodsCache methodsCache;
/**
* Init.
*/
@PostConstruct
public void init() {
methodsCache.initClassMethod("com.alibaba.nacos.core.controller");
methodsCache.initClassMethod("com.alibaba.nacos.naming.controllers");
methodsCache.initClassMethod("com.alibaba.nacos.controller");
methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller");
methodsCache.initClassMethod("com.alibaba.nacos.controller");
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setMaxAge(18000L);
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(ZoneId.systemDefault().toString());
}
}
+9 -8
View File
@@ -16,19 +16,19 @@
package com.alibaba.nacos.controller;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.nacos.config.server.service.repository.PersistService;
import com.alibaba.nacos.naming.controllers.OperatorController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* Health Controller.
*
@@ -57,7 +57,7 @@ public class HealthController {
* equal to 500 indicates that Nacos is in broken states.
*/
@GetMapping("/liveness")
public ResponseEntity liveness() {
public ResponseEntity<String> liveness() {
return ResponseEntity.ok().body("OK");
}
@@ -67,7 +67,7 @@ public class HealthController {
* 500 indicates that Nacos is not ready.
*/
@GetMapping("/readiness")
public ResponseEntity readiness(HttpServletRequest request) {
public ResponseEntity<String> readiness(HttpServletRequest request) {
boolean isConfigReadiness = isConfigReadiness();
boolean isNamingReadiness = isNamingReadiness(request);
@@ -76,14 +76,15 @@ public class HealthController {
}
if (!isConfigReadiness && !isNamingReadiness) {
return ResponseEntity.status(500).body("Config and Naming are not in readiness");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Config and Naming are not in readiness");
}
if (!isConfigReadiness) {
return ResponseEntity.status(500).body("Config is not in readiness");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Config is not in readiness");
}
return ResponseEntity.status(500).body("Naming is not in readiness");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Naming is not in readiness");
}
private boolean isConfigReadiness() {
+40 -32
View File
@@ -16,31 +16,26 @@
package com.alibaba.nacos.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.common.ActionTypes;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.config.server.model.TenantInfo;
import com.alibaba.nacos.config.server.service.repository.PersistService;
import com.alibaba.nacos.enums.NamespaceTypeEnum;
import com.alibaba.nacos.model.Namespace;
import com.alibaba.nacos.model.NamespaceAllInfo;
import com.alibaba.nacos.security.nacos.NacosAuthConfig;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* namespace service.
@@ -58,6 +53,20 @@ public class NamespaceController {
private static final int NAMESPACE_ID_MAX_LENGTH = 128;
private static final String DEFAULT_NAMESPACE = "public";
private static final int DEFAULT_QUOTA = 200;
private static final String DEFAULT_CREATE_SOURCE = "nacos";
private static final String DEFAULT_NAMESPACE_SHOW_NAME = "Public";
private static final String DEFAULT_NAMESPACE_DESCRIPTION = "Public Namespace";
private static final String DEFAULT_TENANT = "";
private static final String DEFAULT_KP = "1";
/**
* Get namespace list.
* @param request request
@@ -66,21 +75,19 @@ public class NamespaceController {
*/
@GetMapping
public RestResult<List<Namespace>> getNamespaces(HttpServletRequest request, HttpServletResponse response) {
RestResult<List<Namespace>> rr = new RestResult<List<Namespace>>();
rr.setCode(200);
// TODO 获取用kp
List<TenantInfo> tenantInfos = persistService.findTenantByKp("1");
Namespace namespace0 = new Namespace("", "public", 200, persistService.configInfoCount(""), 0);
List<TenantInfo> tenantInfos = persistService.findTenantByKp(DEFAULT_KP);
Namespace namespace0 = new Namespace("", DEFAULT_NAMESPACE, DEFAULT_QUOTA,
persistService.configInfoCount(DEFAULT_TENANT), NamespaceTypeEnum.GLOBAL.getType());
List<Namespace> namespaces = new ArrayList<Namespace>();
namespaces.add(namespace0);
for (TenantInfo tenantInfo : tenantInfos) {
int configCount = persistService.configInfoCount(tenantInfo.getTenantId());
Namespace namespaceTmp = new Namespace(tenantInfo.getTenantId(), tenantInfo.getTenantName(), 200,
configCount, 2);
Namespace namespaceTmp = new Namespace(tenantInfo.getTenantId(), tenantInfo.getTenantName(), DEFAULT_QUOTA,
configCount, NamespaceTypeEnum.CUSTOM.getType());
namespaces.add(namespaceTmp);
}
rr.setData(namespaces);
return rr;
return RestResultUtils.success(namespaces);
}
/**
@@ -95,14 +102,15 @@ public class NamespaceController {
@RequestParam("namespaceId") String namespaceId) {
// TODO 获取用kp
if (StringUtils.isBlank(namespaceId)) {
return new NamespaceAllInfo(namespaceId, "Public", 200, persistService.configInfoCount(""), 0,
"Public Namespace");
return new NamespaceAllInfo(namespaceId, DEFAULT_NAMESPACE_SHOW_NAME, DEFAULT_QUOTA,
persistService.configInfoCount(DEFAULT_TENANT), NamespaceTypeEnum.GLOBAL.getType(),
DEFAULT_NAMESPACE_DESCRIPTION);
}
else {
TenantInfo tenantInfo = persistService.findTenantByKp("1", namespaceId);
TenantInfo tenantInfo = persistService.findTenantByKp(DEFAULT_KP, namespaceId);
int configCount = persistService.configInfoCount(namespaceId);
return new NamespaceAllInfo(namespaceId, tenantInfo.getTenantName(), 200, configCount, 2,
tenantInfo.getTenantDesc());
return new NamespaceAllInfo(namespaceId, tenantInfo.getTenantName(), DEFAULT_QUOTA, configCount,
NamespaceTypeEnum.CUSTOM.getType(), tenantInfo.getTenantDesc());
}
}
@@ -135,8 +143,8 @@ public class NamespaceController {
return false;
}
}
persistService.insertTenantInfoAtomic("1", namespaceId, namespaceName, namespaceDesc, "nacos",
System.currentTimeMillis());
persistService.insertTenantInfoAtomic(DEFAULT_KP, namespaceId, namespaceName, namespaceDesc,
DEFAULT_CREATE_SOURCE, System.currentTimeMillis());
return true;
}
@@ -166,7 +174,7 @@ public class NamespaceController {
@RequestParam("namespaceShowName") String namespaceShowName,
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
// TODO 获取用kp
persistService.updateTenantNameAtomic("1", namespace, namespaceShowName, namespaceDesc);
persistService.updateTenantNameAtomic(DEFAULT_KP, namespace, namespaceShowName, namespaceDesc);
return true;
}
@@ -181,7 +189,7 @@ public class NamespaceController {
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response,
@RequestParam("namespaceId") String namespaceId) {
persistService.removeTenantInfoAtomic("1", namespaceId);
persistService.removeTenantInfoAtomic(DEFAULT_KP, namespaceId);
return true;
}
@@ -18,7 +18,7 @@ package com.alibaba.nacos.controller;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.common.ActionTypes;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.security.nacos.NacosAuthConfig;
import com.alibaba.nacos.security.nacos.roles.NacosRoleServiceImpl;
import org.apache.commons.lang3.StringUtils;
@@ -68,7 +68,7 @@ public class PermissionController {
@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!");
return RestResultUtils.success("add permission ok!");
}
/**
@@ -83,7 +83,7 @@ public class PermissionController {
public Object deletePermission(@RequestParam String role, @RequestParam String resource,
@RequestParam String action) {
nacosRoleService.deletePermission(role, resource, action);
return new RestResult<>(200, "delete permission ok!");
return RestResultUtils.success("delete permission ok!");
}
}
+3 -3
View File
@@ -18,7 +18,7 @@ package com.alibaba.nacos.controller;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.common.ActionTypes;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.security.nacos.NacosAuthConfig;
import com.alibaba.nacos.security.nacos.roles.NacosRoleServiceImpl;
import org.apache.commons.lang3.StringUtils;
@@ -84,7 +84,7 @@ public class RoleController {
@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!");
return RestResultUtils.success("add role ok!");
}
/**
@@ -103,7 +103,7 @@ public class RoleController {
else {
roleService.deleteRole(role, username);
}
return new RestResult<>(200, "delete role of user " + username + " ok!");
return RestResultUtils.success("delete role of user " + username + " ok!");
}
}
@@ -40,8 +40,8 @@ public class ServerStateController {
* @return state json.
*/
@GetMapping("/state")
public ResponseEntity serverState() {
Map<String, String> serverState = new HashMap<>(3);
public ResponseEntity<Map<String, String>> serverState() {
Map<String, String> serverState = new HashMap<>(4);
serverState.put("standalone_mode",
EnvUtil.getStandaloneMode() ? EnvUtil.STANDALONE_MODE_ALONE : EnvUtil.STANDALONE_MODE_CLUSTER);
+25 -36
View File
@@ -16,12 +16,6 @@
package com.alibaba.nacos.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.common.ActionTypes;
@@ -29,6 +23,7 @@ import com.alibaba.nacos.auth.common.AuthConfigs;
import com.alibaba.nacos.auth.common.AuthSystemTypes;
import com.alibaba.nacos.auth.exception.AccessException;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.Objects;
import com.alibaba.nacos.config.server.auth.RoleInfo;
@@ -42,8 +37,8 @@ import com.alibaba.nacos.security.nacos.users.NacosUser;
import com.alibaba.nacos.security.nacos.users.NacosUserDetailsServiceImpl;
import com.alibaba.nacos.utils.PasswordEncoderUtil;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -58,6 +53,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* User related methods entry.
*
@@ -103,7 +103,7 @@ public class UserController {
throw new IllegalArgumentException("user '" + username + "' already exist!");
}
userDetailsService.createUser(username, PasswordEncoderUtil.encode(password));
return new RestResult<>(200, "create user ok!");
return RestResultUtils.success("create user ok!");
}
/**
@@ -124,7 +124,7 @@ public class UserController {
}
}
userDetailsService.deleteUser(username);
return new RestResult<>(200, "delete user ok!");
return RestResultUtils.success("delete user ok!");
}
/**
@@ -153,7 +153,7 @@ public class UserController {
userDetailsService.updateUserPassword(username, PasswordEncoderUtil.encode(newPassword));
return new RestResult<>(200, "update user ok!");
return RestResultUtils.success("update user ok!");
}
private boolean hasPermission(String username, HttpServletRequest request) {
@@ -202,42 +202,39 @@ public class UserController {
public Object login(@RequestParam String username, @RequestParam String password, HttpServletResponse response,
HttpServletRequest request) throws AccessException {
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())
|| AuthSystemTypes.LDAP.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
NacosUser user = (NacosUser) authManager.login(request);
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER, NacosAuthConfig.TOKEN_PREFIX + user.getToken());
ObjectNode result = JacksonUtils.createEmptyJsonNode();
// 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());
result.put(Constants.USERNAME, user.getUserName());
return result;
}
// 通过用户名和密码创建一个 Authentication 认证对象,实现类为 UsernamePasswordAuthenticationToken
// create Authentication class through username and password, the implement class
// is UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,
password);
RestResult<String> rr = new RestResult<String>();
try {
// 通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证
// Authentication 对象
// use the method authenticate of AuthenticationManager(default implement is
// ProviderManager) to valid Authentication
Authentication authentication = authenticationManager.authenticate(authenticationToken);
// 将 Authentication 绑定到 SecurityContext
// bind SecurityContext to Authentication
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成Token
// generate Token
String token = jwtTokenManager.createToken(authentication);
// 将Token写入到Http头部
// write Token to Http header
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER, "Bearer " + token);
rr.setCode(200);
rr.setData("Bearer " + token);
return rr;
return RestResultUtils.success("Bearer " + token);
}
catch (BadCredentialsException authentication) {
rr.setCode(401);
rr.setMessage("Login failed");
return rr;
return RestResultUtils.failed(HttpStatus.UNAUTHORIZED.value(), null, "Login failed");
}
}
@@ -252,8 +249,6 @@ public class UserController {
@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);
@@ -263,19 +258,13 @@ public class UserController {
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");
return RestResultUtils.success("Update password success");
}
return RestResultUtils.failed(HttpStatus.UNAUTHORIZED.value(), "Old password is invalid");
}
catch (Exception e) {
rr.setCode(500);
rr.setMessage("Update userpassword failed");
return RestResultUtils.failed(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Update userpassword failed");
}
return rr;
}
/**
@@ -0,0 +1,66 @@
/*
* Copyright 1999-2021 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.enums;
/**
* the enum of namespace. 0 : Global configuration, 1 : Default private namespace ,2 :
* Custom namespace.
*
* @author chenglu
* @date 2021-05-25 17:01
*/
public enum NamespaceTypeEnum {
/**
* Global configuration.
*/
GLOBAL(0, "Global configuration"),
/**
* Default private namespace.
*/
PRIVATE(1, "Default private namespace"),
/**
* Custom namespace.
*/
CUSTOM(2, "Custom namespace");
/**
* the namespace type.
*/
private final int type;
/**
* the description.
*/
private final String description;
NamespaceTypeEnum(int type, String description) {
this.type = type;
this.description = description;
}
public int getType() {
return type;
}
public String getDescription() {
return description;
}
}
File mode changed.
@@ -65,7 +65,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
return bearerToken.substring(7);
return bearerToken.substring(TOKEN_PREFIX.length());
}
String jwt = request.getParameter(Constants.ACCESS_TOKEN);
if (StringUtils.isNotBlank(jwt)) {
+1 -1
View File
@@ -32,7 +32,7 @@ public class Namespace {
private int configCount;
/**
* 0 : Global configuration, 1 : Default private namespace ,2 : Custom namespace.
* see {@link com.alibaba.nacos.console.enums.NamespaceTypeEnum}.
*/
private int type;
File mode changed.
File mode changed.
File mode changed.
File mode changed.
@@ -0,0 +1,172 @@
/*
* 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.security.nacos;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.config.server.auth.RoleInfo;
import com.alibaba.nacos.config.server.model.User;
import com.alibaba.nacos.security.nacos.roles.NacosRoleServiceImpl;
import com.alibaba.nacos.security.nacos.users.NacosUserDetails;
import com.alibaba.nacos.security.nacos.users.NacosUserDetailsServiceImpl;
import com.alibaba.nacos.utils.PasswordEncoderUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.Hashtable;
import java.util.List;
import static com.alibaba.nacos.security.nacos.roles.NacosRoleServiceImpl.GLOBAL_ADMIN_ROLE;
/**
* LDAP auth provider.
*
* @author zjw
*/
@Component
public class LdapAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOG = LoggerFactory.getLogger(LdapAuthenticationProvider.class);
private static final String FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
private static final String TIMEOUT = "com.sun.jndi.ldap.connect.timeout";
private static final String DEFAULT_PASSWORD = "nacos";
private static final String LDAP_PREFIX = "LDAP_";
private static final String DEFAULT_SECURITY_AUTH = "simple";
@Autowired
private NacosUserDetailsServiceImpl userDetailsService;
@Autowired
private NacosRoleServiceImpl nacosRoleService;
@Value(("${nacos.core.auth.ldap.url:ldap://localhost:389}"))
private String ldapUrl;
@Value(("${nacos.core.auth.ldap.timeout:3000}"))
private String time;
@Value(("${nacos.core.auth.ldap.userdn:cn={0},ou=user,dc=company,dc=com}"))
private String userNamePattern;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
if (isAdmin(username)) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (PasswordEncoderUtil.matches(password, userDetails.getPassword())) {
return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
}
else {
return null;
}
}
if (!ldapLogin(username, password)) {
return null;
}
UserDetails userDetails;
try {
userDetails = userDetailsService.loadUserByUsername(LDAP_PREFIX + username);
}
catch (UsernameNotFoundException exception) {
String nacosPassword = PasswordEncoderUtil.encode(DEFAULT_PASSWORD);
userDetailsService.createUser(LDAP_PREFIX + username, nacosPassword);
User user = new User();
user.setUsername(LDAP_PREFIX + username);
user.setPassword(nacosPassword);
userDetails = new NacosUserDetails(user);
}
return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
}
private boolean isAdmin(String username) {
List<RoleInfo> roleInfos = nacosRoleService.getRoles(username);
if (CollectionUtils.isEmpty(roleInfos)) {
return false;
}
for (RoleInfo roleinfo : roleInfos) {
if (GLOBAL_ADMIN_ROLE.equals(roleinfo.getRole())) {
return true;
}
}
return false;
}
private boolean ldapLogin(String username, String password) throws AuthenticationException {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
env.put(Context.PROVIDER_URL, ldapUrl);
env.put(Context.SECURITY_AUTHENTICATION, DEFAULT_SECURITY_AUTH);
env.put(Context.SECURITY_PRINCIPAL, userNamePattern.replace("{0}", username));
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(TIMEOUT, time);
LdapContext ctx = null;
try {
ctx = new InitialLdapContext(env, null);
}
catch (CommunicationException e) {
throw new RuntimeException("LDAP Service connect timeout");
}
catch (Exception e) {
LOG.warn("Exception cause by:{}", e.getMessage());
return false;
}
finally {
closeContext(ctx);
}
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
private void closeContext(DirContext ctx) {
if (ctx != null) {
try {
ctx.close();
}
catch (Exception e) {
LOG.error("Exception closing context", e);
}
}
}
}
+24 -17
View File
@@ -59,6 +59,10 @@ public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
public static final String UPDATE_PASSWORD_ENTRY_POINT = CONSOLE_RESOURCE_NAME_PREFIX + "user/password";
private static final String DEFAULT_ALL_PATH_PATTERN = "/**";
private static final String PROPERTY_IGNORE_URLS = "nacos.security.ignore.urls";
@Autowired
private Environment env;
@@ -71,6 +75,9 @@ public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
@Autowired
private NacosUserDetailsServiceImpl userDetailsService;
@Autowired
private LdapAuthenticationProvider ldapAuthenticationProvider;
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
@@ -82,10 +89,13 @@ public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
String ignoreUrls = null;
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
ignoreUrls = "/**";
ignoreUrls = DEFAULT_ALL_PATH_PATTERN;
}
else if (AuthSystemTypes.LDAP.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
ignoreUrls = DEFAULT_ALL_PATH_PATTERN;
}
if (StringUtils.isBlank(authConfigs.getNacosAuthSystemType())) {
ignoreUrls = env.getProperty("nacos.security.ignore.urls", "/**");
ignoreUrls = env.getProperty(PROPERTY_IGNORE_URLS, DEFAULT_ALL_PATH_PATTERN);
}
if (StringUtils.isNotBlank(ignoreUrls)) {
for (String each : ignoreUrls.trim().split(SECURITY_IGNORE_URLS_SPILT_CHAR)) {
@@ -96,27 +106,24 @@ public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
else if (AuthSystemTypes.LDAP.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
auth.authenticationProvider(ldapAuthenticationProvider);
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
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());
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();
@@ -17,15 +17,16 @@
package com.alibaba.nacos.security.nacos;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.remote.request.Request;
import com.alibaba.nacos.auth.AuthManager;
import com.alibaba.nacos.auth.exception.AccessException;
import com.alibaba.nacos.auth.model.Permission;
import com.alibaba.nacos.auth.model.User;
import com.alibaba.nacos.config.server.auth.RoleInfo;
import com.alibaba.nacos.config.server.utils.RequestUtil;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.security.nacos.roles.NacosRoleServiceImpl;
import com.alibaba.nacos.security.nacos.users.NacosUser;
import com.alibaba.nacos.core.utils.Loggers;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -50,6 +51,10 @@ public class NacosAuthManager implements AuthManager {
private static final String TOKEN_PREFIX = "Bearer ";
private static final String PARAM_USERNAME = "username";
private static final String PARAM_PASSWORD = "password";
@Autowired
private JwtTokenManager tokenManager;
@@ -97,6 +102,43 @@ public class NacosAuthManager implements AuthManager {
return user;
}
@Override
public User loginRemote(Object request) throws AccessException {
Request req = (Request) 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);
if (roleInfoList != null) {
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()) {
@@ -118,8 +160,26 @@ public class NacosAuthManager implements AuthManager {
}
bearerToken = request.getParameter(Constants.ACCESS_TOKEN);
if (StringUtils.isBlank(bearerToken)) {
String userName = request.getParameter("username");
String password = request.getParameter("password");
String userName = request.getParameter(PARAM_USERNAME);
String password = request.getParameter(PARAM_PASSWORD);
bearerToken = resolveTokenFromUser(userName, password);
}
return bearerToken;
}
/**
* Get token from header.
*/
private String resolveToken(Request request) throws AccessException {
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
return bearerToken.substring(7);
}
bearerToken = request.getHeader(Constants.ACCESS_TOKEN);
if (StringUtils.isBlank(bearerToken)) {
String userName = request.getHeader(PARAM_USERNAME);
String password = request.getHeader(PARAM_PASSWORD);
bearerToken = resolveTokenFromUser(userName, password);
}
@@ -127,17 +187,25 @@ public class NacosAuthManager implements AuthManager {
}
private String resolveTokenFromUser(String userName, String rawPassword) throws AccessException {
String finalName;
Authentication authenticate;
try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName,
rawPassword);
authenticationManager.authenticate(authenticationToken);
authenticate = authenticationManager.authenticate(authenticationToken);
}
catch (AuthenticationException e) {
throw new AccessException("unknown user!");
}
return tokenManager.createToken(userName);
if (null == authenticate || StringUtils.isBlank(authenticate.getName())) {
finalName = userName;
}
else {
finalName = authenticate.getName();
}
return tokenManager.createToken(finalName);
}
}
@@ -23,9 +23,9 @@ 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.core.utils.Loggers;
import com.alibaba.nacos.security.nacos.NacosAuthConfig;
import com.alibaba.nacos.security.nacos.users.NacosUserDetailsServiceImpl;
import com.alibaba.nacos.core.utils.Loggers;
import io.jsonwebtoken.lang.Collections;
import org.apache.commons.lang3.StringUtils;
import org.apache.mina.util.ConcurrentHashSet;
@@ -52,6 +52,8 @@ public class NacosRoleServiceImpl {
public static final String GLOBAL_ADMIN_ROLE = "ROLE_ADMIN";
private static final int DEFAULT_PAGE_NO = 1;
@Autowired
private AuthConfigs authConfigs;
@@ -73,7 +75,7 @@ public class NacosRoleServiceImpl {
@Scheduled(initialDelay = 5000, fixedDelay = 15000)
private void reload() {
try {
Page<RoleInfo> roleInfoPage = rolePersistService.getRolesByUserName(StringUtils.EMPTY, 1,
Page<RoleInfo> roleInfoPage = rolePersistService.getRolesByUserName(StringUtils.EMPTY, DEFAULT_PAGE_NO,
Integer.MAX_VALUE);
if (roleInfoPage == null) {
return;
@@ -90,7 +92,7 @@ public class NacosRoleServiceImpl {
Map<String, List<PermissionInfo>> tmpPermissionInfoMap = new ConcurrentHashMap<>(16);
for (String role : tmpRoleSet) {
Page<PermissionInfo> permissionInfoPage = permissionPersistService.getPermissions(role, 1,
Page<PermissionInfo> permissionInfoPage = permissionPersistService.getPermissions(role, DEFAULT_PAGE_NO,
Integer.MAX_VALUE);
tmpPermissionInfoMap.put(role, permissionInfoPage.getPageItems());
}
@@ -158,7 +160,7 @@ public class NacosRoleServiceImpl {
public List<RoleInfo> getRoles(String username) {
List<RoleInfo> roleInfoList = roleInfoMap.get(username);
if (!authConfigs.isCachingEnabled()) {
Page<RoleInfo> roleInfoPage = getRolesFromDatabase(username, 1, Integer.MAX_VALUE);
Page<RoleInfo> roleInfoPage = getRolesFromDatabase(username, DEFAULT_PAGE_NO, Integer.MAX_VALUE);
if (roleInfoPage != null) {
roleInfoList = roleInfoPage.getPageItems();
}
@@ -177,7 +179,8 @@ public class NacosRoleServiceImpl {
public List<PermissionInfo> getPermissions(String role) {
List<PermissionInfo> permissionInfoList = permissionInfoMap.get(role);
if (!authConfigs.isCachingEnabled()) {
Page<PermissionInfo> permissionInfoPage = getPermissionsFromDatabase(role, 1, Integer.MAX_VALUE);
Page<PermissionInfo> permissionInfoPage = getPermissionsFromDatabase(role, DEFAULT_PAGE_NO,
Integer.MAX_VALUE);
if (permissionInfoPage != null) {
permissionInfoList = permissionInfoPage.getPageItems();
}
File mode changed.
File mode changed.
@@ -25,10 +25,6 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
*/
public class PasswordEncoderUtil {
public static void main(String[] args) {
System.out.println(new BCryptPasswordEncoder().encode("nacos"));
}
public static Boolean matches(String raw, String encoded) {
return new BCryptPasswordEncoder().matches(raw, encoded);
}