diff --git a/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java b/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java index 0731fe630..bc7d13e18 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java @@ -58,7 +58,7 @@ public abstract class AbstractProtocolAuthService implements ProtocolAuthServ } @Override - public boolean validateAuthority(IdentityContext identityContext, Permission permission) { + public boolean validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException { if (!authConfigs.isAuthEnabled()) { return true; } diff --git a/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java b/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java index dde7cae8a..8509de69d 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/AuthPluginService.java @@ -51,8 +51,9 @@ public interface AuthPluginService { * @param identityContext where we can find the user information. * @param permission permission to auth. * @return Boolean if the user has the resource authority. + * @throws AccessException if authentication is failed */ - Boolean validateAuthority(IdentityContext identityContext, Permission permission); + Boolean validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException; /** * AuthPluginService Name which for conveniently find AuthPluginService instance. diff --git a/auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java b/auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java index 3088c38e1..f1de05f92 100644 --- a/auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java +++ b/auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java @@ -43,7 +43,7 @@ public class HttpProtocolAuthService extends AbstractProtocolAuthService(2); identityContextBuilder = new HttpIdentityContextBuilder(authConfigs); diff --git a/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java b/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java index 4bd335fc9..344569f37 100644 --- a/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java +++ b/console/src/main/java/com/alibaba/nacos/console/security/nacos/NacosAuthManager.java @@ -19,8 +19,10 @@ package com.alibaba.nacos.console.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.AuthPluginService; +import com.alibaba.nacos.auth.api.IdentityContext; import com.alibaba.nacos.auth.api.Permission; +import com.alibaba.nacos.auth.exception.AccessException; import com.alibaba.nacos.auth.model.User; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.auth.RoleInfo; @@ -39,6 +41,8 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; +import java.util.Collection; +import java.util.LinkedList; import java.util.List; /** @@ -48,7 +52,21 @@ import java.util.List; * @since 1.2.0 */ @Component -public class NacosAuthManager implements AuthManager { +@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule") +public class NacosAuthManager implements AuthManager, AuthPluginService { + + private static final String AUTH_PLUGIN_TYPE = "nacos"; + + private static final String USER_IDENTITY_PARAM_KEY = "user"; + + private static final List IDENTITY_NAMES = new LinkedList() { + { + add(AuthConstants.AUTHORIZATION_HEADER); + add(Constants.ACCESS_TOKEN); + add(AuthConstants.PARAM_USERNAME); + add(AuthConstants.PARAM_PASSWORD); + } + }; @Autowired private JwtTokenManager tokenManager; @@ -63,34 +81,8 @@ public class NacosAuthManager implements AuthManager { 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(AuthConstants.GLOBAL_ADMIN_ROLE)) { - user.setGlobalAdmin(true); - break; - } - } - } + validate0(token); + NacosUser user = getNacosUser(token); req.getSession().setAttribute(RequestUtil.NACOS_USER_KEY, user); return user; } @@ -99,46 +91,39 @@ public class NacosAuthManager implements AuthManager { 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 roleInfoList = roleService.getRoles(username); - if (roleInfoList != null) { - for (RoleInfo roleInfo : roleInfoList) { - if (roleInfo.getRole().equals(AuthConstants.GLOBAL_ADMIN_ROLE)) { - user.setGlobalAdmin(true); - break; - } - } - } - return user; + validate0(token); + return getNacosUser(token); } @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!"); - } + auth0(permission, user); + } + + @Override + public Collection identityNames() { + return IDENTITY_NAMES; + } + + @Override + public boolean validateIdentity(IdentityContext identityContext) throws AccessException { + String token = resolveToken(identityContext); + validate0(token); + NacosUser user = getNacosUser(token); + identityContext.setParameter(USER_IDENTITY_PARAM_KEY, user); + return true; + } + + @Override + public Boolean validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException { + NacosUser user = (NacosUser) identityContext.getParameter(USER_IDENTITY_PARAM_KEY); + auth0(permission, user); + return true; + } + + @Override + public String getAuthServiceName() { + return AUTH_PLUGIN_TYPE; } /** @@ -173,7 +158,20 @@ public class NacosAuthManager implements AuthManager { String password = request.getHeader(AuthConstants.PARAM_PASSWORD); bearerToken = resolveTokenFromUser(userName, password); } - + return bearerToken; + } + + private String resolveToken(IdentityContext identityContext) throws AccessException { + String bearerToken = identityContext.getParameter(AuthConstants.AUTHORIZATION_HEADER, StringUtils.EMPTY); + if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(AuthConstants.TOKEN_PREFIX)) { + return bearerToken.substring(7); + } + bearerToken = identityContext.getParameter(Constants.ACCESS_TOKEN, StringUtils.EMPTY); + if (StringUtils.isBlank(bearerToken)) { + String userName = (String) identityContext.getParameter(AuthConstants.PARAM_USERNAME); + String password = (String) identityContext.getParameter(AuthConstants.PARAM_PASSWORD); + bearerToken = resolveTokenFromUser(userName, password); + } return bearerToken; } @@ -196,4 +194,48 @@ public class NacosAuthManager implements AuthManager { return tokenManager.createToken(finalName); } + + private void validate0(String token) throws AccessException { + 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!"); + } + } + + private NacosUser getNacosUser(String token) { + 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(AuthConstants.GLOBAL_ADMIN_ROLE)) { + user.setGlobalAdmin(true); + break; + } + } + } + return user; + } + + private void auth0(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!"); + } + } } diff --git a/core/src/main/java/com/alibaba/nacos/core/auth/AuthConfig.java b/core/src/main/java/com/alibaba/nacos/core/auth/AuthConfig.java index daf85f7e6..db43e8a8f 100644 --- a/core/src/main/java/com/alibaba/nacos/core/auth/AuthConfig.java +++ b/core/src/main/java/com/alibaba/nacos/core/auth/AuthConfig.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.core.auth; +import com.alibaba.nacos.auth.config.AuthConfigs; +import com.alibaba.nacos.core.code.ControllerMethodsCache; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,9 +31,9 @@ import org.springframework.context.annotation.Configuration; public class AuthConfig { @Bean - public FilterRegistrationBean authFilterRegistration() { + public FilterRegistrationBean authFilterRegistration(AuthFilter authFilter) { FilterRegistrationBean registration = new FilterRegistrationBean<>(); - registration.setFilter(authFilter()); + registration.setFilter(authFilter); registration.addUrlPatterns("/*"); registration.setName("authFilter"); registration.setOrder(6); @@ -40,7 +42,7 @@ public class AuthConfig { } @Bean - public AuthFilter authFilter() { - return new AuthFilter(); + public AuthFilter authFilter(AuthConfigs authConfigs, ControllerMethodsCache methodsCache) { + return new AuthFilter(authConfigs, methodsCache); } } diff --git a/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java b/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java index d2316dd81..3d94cede3 100644 --- a/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java +++ b/core/src/main/java/com/alibaba/nacos/core/auth/AuthFilter.java @@ -16,20 +16,20 @@ package com.alibaba.nacos.core.auth; -import com.alibaba.nacos.auth.AuthManager; +import com.alibaba.nacos.auth.HttpProtocolAuthService; import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.auth.api.IdentityContext; +import com.alibaba.nacos.auth.api.Permission; import com.alibaba.nacos.auth.api.Resource; import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.auth.exception.AccessException; -import com.alibaba.nacos.auth.api.Permission; import com.alibaba.nacos.auth.parser.ResourceParser; import com.alibaba.nacos.common.utils.ExceptionUtil; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.code.ControllerMethodsCache; -import com.alibaba.nacos.sys.env.Constants; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.core.utils.WebUtils; -import com.alibaba.nacos.common.utils.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; +import com.alibaba.nacos.sys.env.Constants; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -51,16 +51,20 @@ import java.util.concurrent.ConcurrentHashMap; */ public class AuthFilter implements Filter { - @Autowired - private AuthConfigs authConfigs; + private final AuthConfigs authConfigs; - @Autowired - private AuthManager authManager; + private final ControllerMethodsCache methodsCache; - @Autowired - private ControllerMethodsCache methodsCache; + private final HttpProtocolAuthService protocolAuthService; - private Map, ResourceParser> parserInstance = new ConcurrentHashMap<>(); + private final Map, ResourceParser> parserInstance = new ConcurrentHashMap<>(); + + public AuthFilter(AuthConfigs authConfigs, ControllerMethodsCache methodsCache) { + this.authConfigs = authConfigs; + this.methodsCache = methodsCache; + this.protocolAuthService = new HttpProtocolAuthService(authConfigs); + this.protocolAuthService.initialize(); + } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) @@ -114,22 +118,19 @@ public class AuthFilter implements Filter { } Secured secured = method.getAnnotation(Secured.class); + Resource resource = protocolAuthService.parseResource(req, secured); + IdentityContext identityContext = protocolAuthService.parseIdentity(req); + boolean result = protocolAuthService.validateIdentity(identityContext); + if (!result) { + // TODO Get reason of failure + throw new AccessException("Validate Identity failed."); + } String action = secured.action().toString(); - String resource = secured.resource(); - - if (StringUtils.isBlank(resource)) { - ResourceParser parser = getResourceParser(secured.parser()); - resource = parser.parseName(req); + result = protocolAuthService.validateAuthority(identityContext, new Permission(resource, action)); + if (!result) { + // TODO Get reason of failure + throw new AccessException("Validate Authority failed."); } - - if (StringUtils.isBlank(resource)) { - // deny if we don't find any resource: - throw new AccessException("resource name invalid!"); - } - - Resource resourceObj = new Resource(null, null, resource, secured.signType(), null); - authManager.auth(new Permission(resourceObj, action), authManager.login(req)); - } chain.doFilter(request, response); } catch (AccessException e) { @@ -144,14 +145,4 @@ public class AuthFilter implements Filter { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server failed," + e.getMessage()); } } - - private ResourceParser getResourceParser(Class parseClass) - throws IllegalAccessException, InstantiationException { - ResourceParser parser = parserInstance.get(parseClass); - if (parser == null) { - parser = parseClass.newInstance(); - parserInstance.put(parseClass, parser); - } - return parser; - } }