Procházet zdrojové kódy

dashboard: Improve support for customizing auth-related implementations (#2059)

* refactor(dashboard): use AuthConfiguration to manage the bean of auth
* refactor(dashboard): change AuthorizationInterceptor to an interface
* refactor(dashboard): change LoginAuthenticationFilter to an interface
* refactor(dashboard): use AuthProperties to manage auth config
* test(dashboard): use NoAuthConfigurationTest to create no auth configuration for testing
master
wxq GitHub před 3 roky
rodič
revize
069f6182ab
V databázi nebyl nalezen žádný známý klíč pro tento podpis ID GPG klíče: 4AEE18F83AFDEB23
11 změnil soubory, kde provedl 348 přidání a 153 odebrání
  1. +2
    -45
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthorizationInterceptor.java
  2. +75
    -0
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultAuthorizationInterceptor.java
  3. +125
    -0
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultLoginAuthenticationFilter.java
  4. +9
    -3
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/FakeAuthServiceImpl.java
  5. +3
    -93
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/LoginAuthenticationFilter.java
  6. +0
    -7
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java
  7. +57
    -0
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/AuthConfiguration.java
  8. +33
    -0
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/AuthProperties.java
  9. +40
    -0
      sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/config/NoAuthConfigurationTest.java
  10. +2
    -3
      sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java
  11. +2
    -2
      sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java

+ 2
- 45
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/AuthorizationInterceptor.java Zobrazit soubor

@@ -15,58 +15,15 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;

import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/**
* The web interceptor for privilege-based authorization.
*
* @author lkxiaolou
* @author wxq
* @since 1.7.1
*/
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {

@Autowired
private AuthService<HttpServletRequest> authService;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
Method method = ((HandlerMethod) handler).getMethod();

AuthAction authAction = method.getAnnotation(AuthAction.class);
if (authAction != null) {
AuthService.AuthUser authUser = authService.getAuthUser(request);
if (authUser == null) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
String target = request.getParameter(authAction.targetName());

if (!authUser.authTarget(target, authAction.value())) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
}
}

return true;
}
public interface AuthorizationInterceptor extends HandlerInterceptor {

private void responseNoPrivilegeMsg(HttpServletResponse response, String message) throws IOException {
Result result = Result.ofFail(-1, message);
response.addHeader("Content-Type", "application/json;charset=UTF-8");
response.getOutputStream().write(JSON.toJSONBytes(result));
}
}

+ 75
- 0
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultAuthorizationInterceptor.java Zobrazit soubor

@@ -0,0 +1,75 @@
/*
* 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.csp.sentinel.dashboard.auth;

import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.fastjson.JSON;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/**
* The web interceptor for privilege-based authorization.
* <p>
* move from old {@link AuthorizationInterceptor}.
*
* @author lkxiaolou
* @author wxq
* @since 1.7.1
*/
public class DefaultAuthorizationInterceptor implements AuthorizationInterceptor {

private final AuthService<HttpServletRequest> authService;

public DefaultAuthorizationInterceptor(AuthService<HttpServletRequest> authService) {
this.authService = authService;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
Method method = ((HandlerMethod) handler).getMethod();

AuthAction authAction = method.getAnnotation(AuthAction.class);
if (authAction != null) {
AuthService.AuthUser authUser = authService.getAuthUser(request);
if (authUser == null) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
String target = request.getParameter(authAction.targetName());

if (!authUser.authTarget(target, authAction.value())) {
responseNoPrivilegeMsg(response, authAction.message());
return false;
}
}
}

return true;
}

private void responseNoPrivilegeMsg(HttpServletResponse response, String message) throws IOException {
Result result = Result.ofFail(-1, message);
response.addHeader("Content-Type", "application/json;charset=UTF-8");
response.getOutputStream().write(JSON.toJSONBytes(result));
}

}

+ 125
- 0
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/DefaultLoginAuthenticationFilter.java Zobrazit soubor

@@ -0,0 +1,125 @@
/*
* 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.csp.sentinel.dashboard.auth;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
* <p>The Servlet filter for authentication.</p>
*
* <p>Note: some urls are excluded as they needn't auth, such as:</p>
* <ul>
* <li>index url: {@code /}</li>
* <li>authentication request url: {@code /login}, {@code /logout}</li>
* <li>machine registry: {@code /registry/machine}</li>
* <li>static resources</li>
* </ul>
* <p>
* The excluded urls and urlSuffixes could be configured in {@code application.properties} file.
*
* @author cdfive
* @since 1.6.0
*/
public class DefaultLoginAuthenticationFilter implements LoginAuthenticationFilter {

private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

private static final String URL_SUFFIX_DOT = ".";

/**
* Some urls which needn't auth, such as /auth/login, /registry/machine and so on.
*/
@Value("#{'${auth.filter.exclude-urls}'.split(',')}")
private List<String> authFilterExcludeUrls;

/**
* Some urls with suffixes which needn't auth, such as htm, html, js and so on.
*/
@Value("#{'${auth.filter.exclude-url-suffixes}'.split(',')}")
private List<String> authFilterExcludeUrlSuffixes;

/**
* Authentication using AuthService interface.
*/
private final AuthService<HttpServletRequest> authService;

public DefaultLoginAuthenticationFilter(AuthService<HttpServletRequest> authService) {
this.authService = authService;
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;

String servletPath = httpRequest.getServletPath();

// Exclude the urls which needn't auth
boolean authFilterExcludeMatch = authFilterExcludeUrls.stream()
.anyMatch(authFilterExcludeUrl -> PATH_MATCHER.match(authFilterExcludeUrl, servletPath));
if (authFilterExcludeMatch) {
chain.doFilter(request, response);
return;
}

// Exclude the urls with suffixes which needn't auth
for (String authFilterExcludeUrlSuffix : authFilterExcludeUrlSuffixes) {
if (StringUtils.isBlank(authFilterExcludeUrlSuffix)) {
continue;
}

// Add . for url suffix so that we needn't add . in property file
if (!authFilterExcludeUrlSuffix.startsWith(URL_SUFFIX_DOT)) {
authFilterExcludeUrlSuffix = URL_SUFFIX_DOT + authFilterExcludeUrlSuffix;
}

if (servletPath.endsWith(authFilterExcludeUrlSuffix)) {
chain.doFilter(request, response);
return;
}
}

AuthService.AuthUser authUser = authService.getAuthUser(httpRequest);

HttpServletResponse httpResponse = (HttpServletResponse) response;
if (authUser == null) {
// If auth fail, set response status code to 401
httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
} else {
chain.doFilter(request, response);
}
}

@Override
public void destroy() {

}
}

+ 9
- 3
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/FakeAuthServiceImpl.java Zobrazit soubor

@@ -15,9 +15,10 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;

import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;

/**
* A fake AuthService implementation, which will pass all user auth checking.
@@ -25,9 +26,14 @@ import org.springframework.stereotype.Component;
* @author Carpenter Lee
* @since 1.5.0
*/
@Component
public class FakeAuthServiceImpl implements AuthService<HttpServletRequest> {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

public FakeAuthServiceImpl() {
this.logger.warn("there is no auth, use {} by implementation {}", AuthService.class, this.getClass());
}

@Override
public AuthUser getAuthUser(HttpServletRequest request) {
return new AuthUserImpl();


+ 3
- 93
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/LoginAuthenticationFilter.java Zobrazit soubor

@@ -15,23 +15,7 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
* <p>The Servlet filter for authentication.</p>
@@ -43,87 +27,13 @@ import java.util.List;
* <li>machine registry: {@code /registry/machine}</li>
* <li>static resources</li>
* </ul>
*
* <p>
* The excluded urls and urlSuffixes could be configured in {@code application.properties} file.
*
* @author cdfive
* @author wxq
* @since 1.6.0
*/
@Component
public class LoginAuthenticationFilter implements Filter {
private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

private static final String URL_SUFFIX_DOT = ".";

/**
* Some urls which needn't auth, such as /auth/login, /registry/machine and so on.
*/
@Value("#{'${auth.filter.exclude-urls}'.split(',')}")
private List<String> authFilterExcludeUrls;

/**
* Some urls with suffixes which needn't auth, such as htm, html, js and so on.
*/
@Value("#{'${auth.filter.exclude-url-suffixes}'.split(',')}")
private List<String> authFilterExcludeUrlSuffixes;

/**
* Authentication using AuthService interface.
*/
@Autowired
private AuthService<HttpServletRequest> authService;

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;

String servletPath = httpRequest.getServletPath();

// Exclude the urls which needn't auth
boolean authFilterExcludeMatch = authFilterExcludeUrls.stream()
.anyMatch(authFilterExcludeUrl -> PATH_MATCHER.match(authFilterExcludeUrl, servletPath));
if (authFilterExcludeMatch) {
chain.doFilter(request, response);
return;
}

// Exclude the urls with suffixes which needn't auth
for (String authFilterExcludeUrlSuffix : authFilterExcludeUrlSuffixes) {
if (StringUtils.isBlank(authFilterExcludeUrlSuffix)) {
continue;
}

// Add . for url suffix so that we needn't add . in property file
if (!authFilterExcludeUrlSuffix.startsWith(URL_SUFFIX_DOT)) {
authFilterExcludeUrlSuffix = URL_SUFFIX_DOT + authFilterExcludeUrlSuffix;
}

if (servletPath.endsWith(authFilterExcludeUrlSuffix)) {
chain.doFilter(request, response);
return;
}
}

AuthService.AuthUser authUser = authService.getAuthUser(httpRequest);

HttpServletResponse httpResponse = (HttpServletResponse) response;
if (authUser == null) {
// If auth fail, set response status code to 401
httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
} else {
chain.doFilter(request, response);
}
}

@Override
public void destroy() {
public interface LoginAuthenticationFilter extends Filter {

}
}

+ 0
- 7
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java Zobrazit soubor

@@ -15,10 +15,6 @@
*/
package com.alibaba.csp.sentinel.dashboard.auth;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@@ -26,9 +22,6 @@ import javax.servlet.http.HttpSession;
* @author cdfive
* @since 1.6.0
*/
@Component
@Primary
@ConditionalOnProperty(name = "auth.enabled", matchIfMissing = true)
public class SimpleWebAuthServiceImpl implements AuthService<HttpServletRequest> {

public static final String WEB_SESSION_KEY = "session_sentinel_admin";


+ 57
- 0
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/AuthConfiguration.java Zobrazit soubor

@@ -0,0 +1,57 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.config;

import com.alibaba.csp.sentinel.dashboard.auth.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.http.HttpServletRequest;

@Configuration
@EnableConfigurationProperties(AuthProperties.class)
public class AuthConfiguration {

private final AuthProperties authProperties;

public AuthConfiguration(AuthProperties authProperties) {
this.authProperties = authProperties;
}

@Bean
@ConditionalOnMissingBean
public AuthService<HttpServletRequest> httpServletRequestAuthService() {
if (this.authProperties.isEnabled()) {
return new SimpleWebAuthServiceImpl();
}
return new FakeAuthServiceImpl();
}

@Bean
@ConditionalOnMissingBean
public LoginAuthenticationFilter loginAuthenticationFilter(AuthService<HttpServletRequest> httpServletRequestAuthService) {
return new DefaultLoginAuthenticationFilter(httpServletRequestAuthService);
}

@Bean
@ConditionalOnMissingBean
public AuthorizationInterceptor authorizationInterceptor(AuthService<HttpServletRequest> httpServletRequestAuthService) {
return new DefaultAuthorizationInterceptor(httpServletRequestAuthService);
}

}

+ 33
- 0
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/AuthProperties.java Zobrazit soubor

@@ -0,0 +1,33 @@
/*
* 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.csp.sentinel.dashboard.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "auth")
public class AuthProperties {

private boolean enabled = true;

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

}

+ 40
- 0
sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/config/NoAuthConfigurationTest.java Zobrazit soubor

@@ -0,0 +1,40 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.config;

import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.FakeAuthServiceImpl;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;

import javax.servlet.http.HttpServletRequest;

/**
* disable auth in test.
*
* @author wxq
*/
@TestConfiguration
@Import(AuthConfiguration.class)
public class NoAuthConfigurationTest {

@Bean
public AuthService<HttpServletRequest> httpServletRequestAuthService() {
return new FakeAuthServiceImpl();
}

}

+ 2
- 3
sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java Zobrazit soubor

@@ -15,9 +15,8 @@
*/
package com.alibaba.csp.sentinel.dashboard.controller.gateway;

import com.alibaba.csp.sentinel.dashboard.auth.AuthorizationInterceptor;
import com.alibaba.csp.sentinel.dashboard.auth.FakeAuthServiceImpl;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.config.NoAuthConfigurationTest;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiPredicateItemEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
@@ -61,7 +60,7 @@ import static org.mockito.BDDMockito.*;
*/
@RunWith(SpringRunner.class)
@WebMvcTest(GatewayApiController.class)
@Import({FakeAuthServiceImpl.class, InMemApiDefinitionStore.class, AppManagement.class, SimpleMachineDiscovery.class, AuthorizationInterceptor.class})
@Import({NoAuthConfigurationTest.class, InMemApiDefinitionStore.class, AppManagement.class, SimpleMachineDiscovery.class})
public class GatewayApiControllerTest {

private static final String TEST_APP = "test_app";


+ 2
- 2
sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java Zobrazit soubor

@@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.dashboard.controller.gateway;
import com.alibaba.csp.sentinel.dashboard.auth.AuthorizationInterceptor;
import com.alibaba.csp.sentinel.dashboard.auth.FakeAuthServiceImpl;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.config.NoAuthConfigurationTest;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
@@ -64,8 +65,7 @@ import static org.mockito.BDDMockito.*;
*/
@RunWith(SpringRunner.class)
@WebMvcTest(GatewayFlowRuleController.class)
@Import({FakeAuthServiceImpl.class, InMemGatewayFlowRuleStore.class, AppManagement.class, SimpleMachineDiscovery.class,
AuthorizationInterceptor.class })
@Import({NoAuthConfigurationTest.class, InMemGatewayFlowRuleStore.class, AppManagement.class, SimpleMachineDiscovery.class})
public class GatewayFlowRuleControllerTest {

private static final String TEST_APP = "test_app";


Načítá se…
Zrušit
Uložit