diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/config/ConsoleConfig.java b/pigx-register/src/main/java/com/alibaba/nacos/console/config/ConsoleConfig.java
new file mode 100755
index 000000000..0a47b45b6
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/config/ConsoleConfig.java
@@ -0,0 +1,63 @@
+/*
+ * 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.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.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;
+
+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() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowCredentials(true);
+ config.addAllowedOrigin("*");
+ config.addAllowedHeader("*");
+ config.setMaxAge(18000L);
+ config.addAllowedMethod("*");
+ source.registerCorsConfiguration("/**", config);
+ return new CorsFilter(source);
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/controller/PermissionController.java b/pigx-register/src/main/java/com/alibaba/nacos/console/controller/PermissionController.java
new file mode 100755
index 000000000..71339d4c5
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/controller/PermissionController.java
@@ -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!");
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/controller/RoleController.java b/pigx-register/src/main/java/com/alibaba/nacos/console/controller/RoleController.java
new file mode 100755
index 000000000..69d720f5c
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/controller/RoleController.java
@@ -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
+ *
+ * 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!");
+ }
+
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/controller/UserController.java b/pigx-register/src/main/java/com/alibaba/nacos/console/controller/UserController.java
new file mode 100755
index 000000000..2daef8690
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/controller/UserController.java
@@ -0,0 +1,232 @@
+/*
+ * 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.auth.RoleInfo;
+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.roles.NacosRoleServiceImpl;
+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;
+import java.util.List;
+
+/**
+ * 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 NacosRoleServiceImpl roleService;
+
+ @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) {
+ List roleInfoList = roleService.getRoles(username);
+ if (roleInfoList != null) {
+ for (RoleInfo roleInfo : roleInfoList) {
+ if (roleInfo.getRole().equals(NacosRoleServiceImpl.GLOBAL_ADMIN_ROLE)) {
+ throw new IllegalArgumentException("cannot delete admin: " + 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
+ *
+ * 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 rr = new RestResult();
+ 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 updatePassword(@RequestParam(value = "oldPassword") String oldPassword,
+ @RequestParam(value = "newPassword") String newPassword) {
+
+ RestResult rr = new RestResult();
+ 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;
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/exception/ConsoleExceptionHandler.java b/pigx-register/src/main/java/com/alibaba/nacos/console/exception/ConsoleExceptionHandler.java
new file mode 100755
index 000000000..9d9d285a3
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/exception/ConsoleExceptionHandler.java
@@ -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 handleAccessException(AccessException e) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
+ }
+
+ @ExceptionHandler(IllegalArgumentException.class)
+ private ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.toString());
+ }
+
+ @ExceptionHandler(Exception.class)
+ private ResponseEntity handleException(Exception e) {
+ logger.error("CONSOLE", e);
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.toString());
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/CustomAuthenticationProvider.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/CustomAuthenticationProvider.java
new file mode 100755
index 000000000..c54db33f7
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/CustomAuthenticationProvider.java
@@ -0,0 +1,56 @@
+/*
+ * 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.console.security.nacos.users.NacosUserDetailsServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+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.stereotype.Component;
+
+/**
+ * auth provider
+ *
+ * @author wfnuser
+ */
+@Component
+public class CustomAuthenticationProvider implements AuthenticationProvider {
+
+ @Autowired
+ private NacosUserDetailsServiceImpl userDetailsService;
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+
+ String username = (String) authentication.getPrincipal();
+ String password = (String) authentication.getCredentials();
+ UserDetails userDetails = userDetailsService.loadUserByUsername(username);
+
+ if (!password.equals(userDetails.getPassword())) {
+ return new UsernamePasswordAuthenticationToken(username, null, null);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean supports(Class> aClass) {
+ return aClass.equals(UsernamePasswordAuthenticationToken.class);
+ }
+
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/JwtAuthenticationEntryPoint.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/JwtAuthenticationEntryPoint.java
new file mode 100755
index 000000000..dad99758c
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/JwtAuthenticationEntryPoint.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * jwt auth fail point
+ *
+ * @author wfnuser
+ */
+@Component
+public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+ private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
+
+ @Override
+ public void commence(HttpServletRequest request,
+ HttpServletResponse response,
+ AuthenticationException e) throws IOException, ServletException {
+ logger.error("Responding with unauthorized error. Message:{}, url:{}", e.getMessage(), request.getRequestURI());
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/JwtTokenManager.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/JwtTokenManager.java
new file mode 100755
index 000000000..0ef8fce1f
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/JwtTokenManager.java
@@ -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 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);
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthConfig.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthConfig.java
new file mode 100755
index 000000000..78fe636b5
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthConfig.java
@@ -0,0 +1,146 @@
+/*
+ * 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.console.filter.JwtAuthenticationTokenFilter;
+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.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.BeanIds;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsUtils;
+
+/**
+ * Spring security config
+ *
+ * @author Nacos
+ */
+@Configuration
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
+
+ public static final String AUTHORIZATION_HEADER = "Authorization";
+
+ public static final String SECURITY_IGNORE_URLS_SPILT_CHAR = ",";
+
+ public static final String LOGIN_ENTRY_POINT = "/v1/auth/login";
+
+ public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/v1/auth/**";
+
+ 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;
+
+ @Value("${nacos.security.ignore.urls}")
+ private String ignoreURLs;
+
+ @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
+ @Override
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
+
+ @Override
+ public void configure(WebSecurity web) {
+
+ 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
+ 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());
+
+ // disable cache
+ http.headers().cacheControl();
+
+ http.addFilterBefore(new JwtAuthenticationTokenFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
+ }
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java
new file mode 100755
index 000000000..bd6d12293
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java
@@ -0,0 +1,136 @@
+/*
+ * 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 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()) {
+ 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);
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/roles/NacosRoleServiceImpl.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/roles/NacosRoleServiceImpl.java
new file mode 100755
index 000000000..0799c1f31
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/roles/NacosRoleServiceImpl.java
@@ -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.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.apache.mina.util.ConcurrentHashSet;
+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 = "ROLE_ADMIN";
+
+ @Autowired
+ private AuthConfigs authConfigs;
+
+ @Autowired
+ private RolePersistService rolePersistService;
+
+ @Autowired
+ private NacosUserDetailsServiceImpl userDetailsService;
+
+ @Autowired
+ private PermissionPersistService permissionPersistService;
+
+ private Set roleSet = new ConcurrentHashSet<>();
+
+ private Map> roleInfoMap = new ConcurrentHashMap<>();
+
+ private Map> permissionInfoMap = new ConcurrentHashMap<>();
+
+ @Scheduled(initialDelay = 5000, fixedDelay = 15000)
+ private void reload() {
+ try {
+ Page roleInfoPage = rolePersistService.getRolesByUserName(StringUtils.EMPTY, 1, Integer.MAX_VALUE);
+ if (roleInfoPage == null) {
+ return;
+ }
+ Set tmpRoleSet = new HashSet<>(16);
+ Map> 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);
+ tmpRoleSet.add(roleInfo.getRole());
+ }
+
+ Map> tmpPermissionInfoMap = new ConcurrentHashMap<>(16);
+ for (String role : tmpRoleSet) {
+ Page permissionInfoPage = permissionPersistService.getPermissions(role, 1, Integer.MAX_VALUE);
+ tmpPermissionInfoMap.put(role, permissionInfoPage.getPageItems());
+ }
+
+ roleSet = tmpRoleSet;
+ roleInfoMap = tmpRoleInfoMap;
+ permissionInfoMap = tmpPermissionInfoMap;
+ } catch (Exception e) {
+ Loggers.AUTH.warn("[LOAD-ROLES] load failed", e);
+ }
+ }
+
+ /**
+ * Determine if the user has permission of the resource.
+ *
+ * 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 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 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 getRoles(String username) {
+ List roleInfoList = roleInfoMap.get(username);
+ if (!authConfigs.isCachingEnabled()) {
+ Page roleInfoPage = getRolesFromDatabase(username, 1, Integer.MAX_VALUE);
+ if (roleInfoPage != null) {
+ roleInfoList = roleInfoPage.getPageItems();
+ }
+ }
+ return roleInfoList;
+ }
+
+ public Page getRolesFromDatabase(String userName, int pageNo, int pageSize) {
+ Page roles = rolePersistService.getRolesByUserName(userName, pageNo, pageSize);
+ if (roles == null) {
+ return new Page<>();
+ }
+ return roles;
+ }
+
+ public List getPermissions(String role) {
+ List permissionInfoList = permissionInfoMap.get(role);
+ if (!authConfigs.isCachingEnabled()) {
+ Page permissionInfoPage = getPermissionsFromDatabase(role, 1, Integer.MAX_VALUE);
+ if (permissionInfoPage != null) {
+ permissionInfoList = permissionInfoPage.getPageItems();
+ }
+ }
+ return permissionInfoList;
+ }
+
+ public Page 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!");
+ }
+ if (GLOBAL_ADMIN_ROLE.equals(role)) {
+ throw new IllegalArgumentException("role '" + GLOBAL_ADMIN_ROLE + "' is not permitted to create!");
+ }
+ rolePersistService.addRole(role, username);
+ roleSet.add(role);
+ }
+
+ public void deleteRole(String role, String userName) {
+ rolePersistService.deleteRole(role, userName);
+ }
+
+ public void deleteRole(String role) {
+ rolePersistService.deleteRole(role);
+ roleSet.remove(role);
+ }
+
+ public Page getPermissionsFromDatabase(String role, int pageNo, int pageSize) {
+ Page pageInfo = permissionPersistService.getPermissions(role, pageNo, pageSize);
+ if (pageInfo == null) {
+ return new Page<>();
+ }
+ return pageInfo;
+ }
+
+ public void addPermission(String role, String resource, String action) {
+ if (!roleSet.contains(role)) {
+ throw new IllegalArgumentException("role " + role + " not found!");
+ }
+ permissionPersistService.addPermission(role, resource, action);
+ }
+
+ public void deletePermission(String role, String resource, String action) {
+ permissionPersistService.deletePermission(role, resource, action);
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUser.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUser.java
new file mode 100755
index 000000000..f2f519c58
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUser.java
@@ -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);
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUserDetails.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUserDetails.java
new file mode 100755
index 000000000..3390a2ce2
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUserDetails.java
@@ -0,0 +1,73 @@
+/*
+ * 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.model.User;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+
+/**
+ * custem user
+ *
+ * @author wfnuser
+ */
+public class NacosUserDetails implements UserDetails {
+
+ private User user;
+
+ public NacosUserDetails(User user) {
+ this.user = user;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ // TODO: get authorities
+ return AuthorityUtils.commaSeparatedStringToAuthorityList("");
+ }
+
+ @Override
+ public String getPassword() {
+ return user.getPassword();
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getUsername();
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+}
diff --git a/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUserDetailsServiceImpl.java b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUserDetailsServiceImpl.java
new file mode 100755
index 000000000..7c9b66ceb
--- /dev/null
+++ b/pigx-register/src/main/java/com/alibaba/nacos/console/security/nacos/users/NacosUserDetailsServiceImpl.java
@@ -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 userMap = new ConcurrentHashMap<>();
+
+ @Autowired
+ private UserPersistService userPersistService;
+
+ @Autowired
+ private AuthConfigs authConfigs;
+
+ @Scheduled(initialDelay = 5000, fixedDelay = 15000)
+ private void reload() {
+ try {
+ Page users = getUsersFromDatabase(1, Integer.MAX_VALUE);
+ if (users == null) {
+ return;
+ }
+
+ Map 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 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);
+ }
+}
diff --git a/pigx-register/src/main/resources/static/console-fe/src/layouts/menu.js b/pigx-register/src/main/resources/static/console-fe/src/layouts/menu.js
new file mode 100755
index 000000000..0143f0003
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/layouts/menu.js
@@ -0,0 +1,75 @@
+import { isJsonString } from '../utils/nacosutil';
+
+const configurationMenu = {
+ key: 'configurationManagementVirtual',
+ children: [
+ {
+ key: 'configurationManagement',
+ url: '/configurationManagement',
+ },
+ {
+ key: 'historyRollback',
+ url: '/historyRollback',
+ },
+ {
+ key: 'listeningToQuery',
+ url: '/listeningToQuery',
+ },
+ ],
+};
+/**
+ * 权限控制相关
+ */
+const authorityControlMenu = {
+ key: 'authorityControl',
+ children: [
+ {
+ key: 'userList',
+ url: '/userManagement',
+ },
+ {
+ key: 'roleManagement',
+ url: '/rolesManagement',
+ },
+ {
+ key: 'privilegeManagement',
+ url: '/permissionsManagement',
+ },
+ ],
+};
+
+export default function(model) {
+ const { token = '{}' } = localStorage;
+ const { globalAdmin } = isJsonString(token) ? JSON.parse(token) || {} : {};
+
+ return [
+ model === 'naming' ? undefined : configurationMenu,
+ {
+ key: 'serviceManagementVirtual',
+ children: [
+ {
+ key: 'serviceManagement',
+ url: '/serviceManagement',
+ },
+ {
+ key: 'subscriberList',
+ url: '/subscriberList',
+ },
+ ],
+ },
+ {
+ key: 'clusterManagementVirtual',
+ children: [
+ {
+ key: 'clusterManagement',
+ url: '/clusterManagement',
+ },
+ ],
+ },
+ globalAdmin ? authorityControlMenu : undefined,
+ {
+ key: 'namespace',
+ url: '/namespace',
+ },
+ ].filter(item => item);
+}
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/NewPermissions.js b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/NewPermissions.js
new file mode 100755
index 000000000..a54d0f6f4
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/NewPermissions.js
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Field, Form, Input, Select, Dialog, ConfigProvider } from '@alifd/next';
+import { connect } from 'react-redux';
+import { getNamespaces } from '../../../reducers/namespace';
+
+const FormItem = Form.Item;
+const { Option } = Select;
+
+const formItemLayout = {
+ labelCol: { fixedSpan: 4 },
+ wrapperCol: { span: 19 },
+};
+
+@connect(state => ({ namespaces: state.namespace.namespaces }), { getNamespaces })
+@ConfigProvider.config
+class NewPermissions extends React.Component {
+ static displayName = 'NewPermissions';
+
+ field = new Field(this);
+
+ static propTypes = {
+ locale: PropTypes.object,
+ visible: PropTypes.bool,
+ getNamespaces: PropTypes.func,
+ onOk: PropTypes.func,
+ onCancel: PropTypes.func,
+ namespaces: PropTypes.array,
+ };
+
+ componentDidMount() {
+ this.props.getNamespaces();
+ }
+
+ check() {
+ const { locale } = this.props;
+ const errors = {
+ role: locale.roleError,
+ resource: locale.resourceError,
+ action: locale.actionError,
+ };
+ const vals = Object.keys(errors).map(key => {
+ const val = this.field.getValue(key);
+ if (!val) {
+ this.field.setError(key, errors[key]);
+ }
+ return val;
+ });
+ if (vals.filter(v => v).length === 3) {
+ return vals;
+ }
+ return null;
+ }
+
+ render() {
+ const { getError } = this.field;
+ const { visible, onOk, onCancel, locale, namespaces } = this.props;
+ return (
+ <>
+
+ >
+ );
+ }
+}
+
+export default NewPermissions;
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.js b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.js
new file mode 100755
index 000000000..614c42adf
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.js
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next';
+import { connect } from 'react-redux';
+import { getPermissions, createPermission, deletePermission } from '../../../reducers/authority';
+import { getNamespaces } from '../../../reducers/namespace';
+import RegionGroup from '../../../components/RegionGroup';
+import NewPermissions from './NewPermissions';
+
+import './PermissionsManagement.scss';
+
+@connect(
+ state => ({
+ permissions: state.authority.permissions,
+ namespaces: state.namespace.namespaces,
+ }),
+ { getPermissions, getNamespaces }
+)
+@ConfigProvider.config
+class PermissionsManagement extends React.Component {
+ static displayName = 'PermissionsManagement';
+
+ static propTypes = {
+ locale: PropTypes.object,
+ permissions: PropTypes.object,
+ namespaces: PropTypes.object,
+ getPermissions: PropTypes.func,
+ getNamespaces: PropTypes.func,
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: true,
+ pageNo: 1,
+ pageSize: 9,
+ createPermission: false,
+ };
+ }
+
+ componentDidMount() {
+ this.getPermissions();
+ this.props.getNamespaces();
+ }
+
+ getPermissions() {
+ const { pageNo, pageSize } = this.state;
+ this.props
+ .getPermissions({ pageNo, pageSize })
+ .then(() => {
+ if (this.state.loading) {
+ this.setState({ loading: false });
+ }
+ })
+ .catch(() => this.setState({ loading: false }));
+ }
+
+ colseCreatePermission() {
+ this.setState({ createPermissionVisible: false });
+ }
+
+ getActionText(action) {
+ const { locale } = this.props;
+ return {
+ r: `${locale.readOnly} (r)`,
+ w: `${locale.writeOnly} (w)`,
+ rw: `${locale.readWrite} (rw)`,
+ }[action];
+ }
+
+ render() {
+ const { permissions, namespaces = [], locale } = this.props;
+ const { loading, pageSize, pageNo, createPermissionVisible } = this.state;
+ return (
+ <>
+
+
+ {permissions.totalCount > pageSize && (
+ this.setState({ pageNo }, () => this.getPermissions())}
+ />
+ )}
+
+ createPermission(permission).then(res => {
+ this.setState({ pageNo: 1 }, () => this.getPermissions());
+ return res;
+ })
+ }
+ onCancel={() => this.colseCreatePermission()}
+ />
+ >
+ );
+ }
+}
+
+export default PermissionsManagement;
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.scss b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.scss
new file mode 100755
index 000000000..03175a7be
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/PermissionsManagement.scss
@@ -0,0 +1,12 @@
+/*
+ * 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.
+ */
\ No newline at end of file
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/index.js b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/index.js
new file mode 100755
index 000000000..cfd9f7839
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/PermissionsManagement/index.js
@@ -0,0 +1,16 @@
+/*
+ * 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.
+ */
+
+import PermissionsManagement from './PermissionsManagement';
+
+export default PermissionsManagement;
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/README.md b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/README.md
new file mode 100755
index 000000000..ba2cd2c97
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/README.md
@@ -0,0 +1,7 @@
+# 权限控制
+
+> AuthorityControl
+
+1. UserManagement => 用户管理
+2. RolesManagement => 角色管理
+3. PermissionsManagement => 权限管理
\ No newline at end of file
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/RolesManagement/NewRole.js b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/RolesManagement/NewRole.js
new file mode 100755
index 000000000..4628377af
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/RolesManagement/NewRole.js
@@ -0,0 +1,90 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Field, Form, Input, Dialog, ConfigProvider } from '@alifd/next';
+
+const FormItem = Form.Item;
+
+const formItemLayout = {
+ labelCol: { fixedSpan: 4 },
+ wrapperCol: { span: 19 },
+};
+
+@ConfigProvider.config
+class NewRole extends React.Component {
+ static displayName = 'NewRole';
+
+ field = new Field(this);
+
+ static propTypes = {
+ locale: PropTypes.object,
+ visible: PropTypes.bool,
+ onOk: PropTypes.func,
+ onCancel: PropTypes.func,
+ };
+
+ check() {
+ const { locale } = this.props;
+ const errors = {
+ role: locale.roleError,
+ username: locale.usernameError,
+ };
+ const vals = Object.keys(errors).map(key => {
+ const val = this.field.getValue(key);
+ if (!val) {
+ this.field.setError(key, errors[key]);
+ }
+ return val;
+ });
+ if (vals.filter(v => v).length === 2) {
+ return vals;
+ }
+ return null;
+ }
+
+ render() {
+ const { locale } = this.props;
+ const { getError } = this.field;
+ const { visible, onOk, onCancel } = this.props;
+ return (
+ <>
+
+ >
+ );
+ }
+}
+
+export default NewRole;
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/RolesManagement/RolesManagement.js b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/RolesManagement/RolesManagement.js
new file mode 100755
index 000000000..5fca1f7c5
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/RolesManagement/RolesManagement.js
@@ -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.
+ */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next';
+import { connect } from 'react-redux';
+import { getRoles, createRole, deleteRole } from '../../../reducers/authority';
+import RegionGroup from '../../../components/RegionGroup';
+import NewRole from './NewRole';
+
+import './RolesManagement.scss';
+
+@connect(state => ({ roles: state.authority.roles }), { getRoles })
+@ConfigProvider.config
+class RolesManagement extends React.Component {
+ static displayName = 'RolesManagement';
+
+ static propTypes = {
+ locale: PropTypes.object,
+ roles: PropTypes.object,
+ getRoles: PropTypes.func,
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: true,
+ pageNo: 1,
+ pageSize: 9,
+ };
+ }
+
+ componentDidMount() {
+ this.getRoles();
+ }
+
+ getRoles() {
+ const { pageNo, pageSize } = this.state;
+ this.props
+ .getRoles({ pageNo, pageSize })
+ .then(() => {
+ if (this.state.loading) {
+ this.setState({ loading: false });
+ }
+ })
+ .catch(() => this.setState({ loading: false }));
+ }
+
+ colseCreateRole() {
+ this.setState({ createRoleVisible: false });
+ }
+
+ render() {
+ const { roles, locale } = this.props;
+ const { loading, pageSize, pageNo, createRoleVisible, passwordResetUser } = this.state;
+ return (
+ <>
+
+
+ {users.totalCount > pageSize && (
+ this.setState({ pageNo }, () => this.getUsers())}
+ />
+ )}
+
+ createUser(user).then(res => {
+ this.setState({ pageNo: 1 }, () => this.getUsers());
+ return res;
+ })
+ }
+ onCancel={() => this.colseCreateUser()}
+ />
+
+ passwordReset(user).then(res => {
+ this.getUsers();
+ return res;
+ })
+ }
+ onCancel={() => this.setState({ passwordResetUser: undefined })}
+ />
+ >
+ );
+ }
+}
+
+export default UserManagement;
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/UserManagement/UserManagement.scss b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/UserManagement/UserManagement.scss
new file mode 100755
index 000000000..20cf13723
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/UserManagement/UserManagement.scss
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+@import '../authority.scss';
+
+.users-pagination {
+ float: right;
+ margin-top: 20px;
+}
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/UserManagement/index.js b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/UserManagement/index.js
new file mode 100755
index 000000000..4df885840
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/UserManagement/index.js
@@ -0,0 +1,16 @@
+/*
+ * 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.
+ */
+
+import UserManagement from './UserManagement';
+
+export default UserManagement;
diff --git a/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/authority.scss b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/authority.scss
new file mode 100755
index 000000000..a1112a3ef
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/pages/AuthorityControl/authority.scss
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+.filter-panel {
+ text-align: right;
+ padding: 10px 0;
+}
diff --git a/pigx-register/src/main/resources/static/console-fe/src/reducers/authority.js b/pigx-register/src/main/resources/static/console-fe/src/reducers/authority.js
new file mode 100755
index 000000000..0ee7ef288
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/reducers/authority.js
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+import { Message } from '@alifd/next';
+import request from '../utils/request';
+import { UPDATE_USER, SIGN_IN, USER_LIST, ROLE_LIST, PERMISSIONS_LIST } from '../constants';
+
+const initialState = {
+ users: {
+ totalCount: 0,
+ pageNumber: 1,
+ pagesAvailable: 1,
+ pageItems: [],
+ },
+ roles: {
+ totalCount: 0,
+ pageNumber: 1,
+ pagesAvailable: 1,
+ pageItems: [],
+ },
+ permissions: {
+ totalCount: 0,
+ pageNumber: 1,
+ pagesAvailable: 1,
+ pageItems: [],
+ },
+};
+
+const successMsg = res => {
+ if (res.code === 200) {
+ Message.success(res.message);
+ }
+ return res;
+};
+
+/**
+ * 用户列表
+ * @param {*} params
+ */
+const getUsers = params => dispatch =>
+ request.get('v1/auth/users', { params }).then(data => dispatch({ type: USER_LIST, data }));
+
+/**
+ * 创建用户
+ * @param {*} param0
+ */
+const createUser = ([username, password]) =>
+ request.post('v1/auth/users', { username, password }).then(res => successMsg(res));
+
+/**
+ * 删除用户
+ * @param {*} username
+ */
+const deleteUser = username =>
+ request.delete('v1/auth/users', { params: { username } }).then(res => successMsg(res));
+
+/**
+ * 重置密码
+ * @param {*} param0
+ */
+const passwordReset = ([username, newPassword]) =>
+ request.put('v1/auth/users', { username, newPassword }).then(res => successMsg(res));
+
+/**
+ * 角色列表
+ * @param {*} params
+ */
+
+const getRoles = params => dispatch =>
+ request.get('v1/auth/roles', { params }).then(data => dispatch({ type: ROLE_LIST, data }));
+
+/**
+ * 创建角色
+ * @param {*} param0
+ */
+const createRole = ([role, username]) =>
+ request.post('v1/auth/roles', { role, username }).then(res => successMsg(res));
+
+/**
+ * 删除角色
+ * @param {*} param0
+ */
+const deleteRole = role =>
+ request.delete('v1/auth/roles', { params: role }).then(res => successMsg(res));
+
+/**
+ * 权限列表
+ * @param {*} params
+ */
+const getPermissions = params => dispatch =>
+ request
+ .get('v1/auth/permissions', { params })
+ .then(data => dispatch({ type: PERMISSIONS_LIST, data }));
+
+/**
+ * 给角色添加权限
+ * @param {*} param0
+ */
+const createPermission = ([role, resource, action]) =>
+ request.post('v1/auth/permissions', { role, resource, action }).then(res => successMsg(res));
+
+/**
+ * 删除权限
+ * @param {*} param0
+ */
+const deletePermission = permission =>
+ request.delete('v1/auth/permissions', { params: permission }).then(res => successMsg(res));
+
+export default (state = initialState, action) => {
+ switch (action.type) {
+ case USER_LIST:
+ return { ...state, users: { ...action.data } };
+ case ROLE_LIST:
+ return { ...state, roles: { ...action.data } };
+ case PERMISSIONS_LIST:
+ return { ...state, permissions: { ...action.data } };
+ default:
+ return state;
+ }
+};
+
+export {
+ getUsers,
+ createUser,
+ deleteUser,
+ passwordReset,
+ getRoles,
+ createRole,
+ deleteRole,
+ getPermissions,
+ createPermission,
+ deletePermission,
+};
diff --git a/pigx-register/src/main/resources/static/console-fe/src/reducers/configuration.js b/pigx-register/src/main/resources/static/console-fe/src/reducers/configuration.js
new file mode 100755
index 000000000..8e5eacf05
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/reducers/configuration.js
@@ -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.
+ */
+
+import request from '../utils/request';
+import { GET_CONFIGURATION } from '../constants';
+
+const initialState = {
+ configurations: [],
+};
+
+const getConfigs = params => dispatch =>
+ request
+ .get('v1/cs/configs', { params })
+ .then(data => dispatch({ type: GET_CONFIGURATION, data }));
+
+export default (state = initialState, action) => {
+ switch (action.type) {
+ case GET_CONFIGURATION:
+ return { ...state, configurations: action.data };
+ default:
+ return state;
+ }
+};
+
+export { getConfigs };
diff --git a/pigx-register/src/main/resources/static/console-fe/src/reducers/namespace.js b/pigx-register/src/main/resources/static/console-fe/src/reducers/namespace.js
new file mode 100755
index 000000000..895bf0f22
--- /dev/null
+++ b/pigx-register/src/main/resources/static/console-fe/src/reducers/namespace.js
@@ -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.
+ */
+
+import request from '../utils/request';
+import { GET_NAMESPACES } from '../constants';
+
+const initialState = {
+ namespaces: [],
+};
+
+const getNamespaces = params => dispatch =>
+ request.get('v1/console/namespaces', { params }).then(response => {
+ const { code, data } = response;
+ dispatch({
+ type: GET_NAMESPACES,
+ data: code === 200 ? data : [],
+ });
+ });
+
+export default (state = initialState, action) => {
+ switch (action.type) {
+ case GET_NAMESPACES:
+ return { ...state, namespaces: action.data };
+ default:
+ return state;
+ }
+};
+
+export { getNamespaces };