【问题标题】:Is it possible to let spring security list every available URL of a webapp and how it is secured?是否可以让 spring security 列出 webapp 的每个可用 URL 以及它是如何保护的?
【发布时间】:2018-01-04 10:29:36
【问题描述】:

我正在为 Spring MVC Web 应用程序添加安全性,该应用程序有大约 100 个您可以访问的不同 URL(例如:GET /users、GET /users/{userId}、...)。

我正在使用 Spring Security 的方法安全表达式添加安全性:

@GetMapping(value = "/load-configurations", produces = "application/json")
@PreAuthorize("@accessChecker.canViewBusinessData(authentication)")
public ResponseEntity<List<SavedCalculationConfiguration>> loadConfigurations() {

@GetMapping(value = "/api/v1/export-all")
@PreAuthorize("@accessChecker.isIT(authentication)")
public ResponseEntity<?> exportAllToXml(Principal principal,

@PreAuthorize 部分是我现在到处添加的部分。

由于这些方法分散在不同的控制器中,我很难检查我是否为每个方法添加了正确的安全注释并且没有遗漏任何一个。

我想知道的是,是否有某种方法可以让 spring 列出每个已知 URL 以及如何保护该 URL。我想用它来检查我是否做对了一切,并不时检查是否有人忘记为新方法添加安全性。

当我启动我的应用程序时,spring 会记录每个已知的 URL,因此我知道它必须将这些数据隐藏在内部类中的某个地方。此外,当我在 spring 安全过滤器中放置断点时,我可以看到一个变量,其中包含我用来配置安全性的表达式,但仅限于我要导航到的 URL,而不是所有的。

我确定某个内部类或打开特定日志记录级别将包含我需要的所有数据,但我找不到它:) 有什么帮助吗?

【问题讨论】:

  • Spring Security 的方法安全性不适用于控制器方法,你应该只在业务类上使用它。所以 AFAIK 没有 Spring MVC 控制器方法和 Spring Security 的方法安全性的耦合。
  • 作为一种解决方法,您可以收集所有控制器方法和所有用@PreAuthorize 注释的方法(有一个 Spring util 类)。然后您可以从控制器方法列表中删除所有匹配的方法,以便该列表仅包含非安全方法。我不熟悉 Spring MVC,也不知道计算方法 URL 的简单方法。

标签: spring spring-mvc spring-security


【解决方案1】:

感谢杜尔的建议。我认为 @PreAuthorize 在单个方法上可以减少忘记为控制器上的新方法添加安全性的可能性(而不是在中心位置管理 url 的安全性)。

最终我采用了您建议的解决方法,从注释中读取数据并手动将所需信息拼凑在一起。

这是我最终想到的类,它列出了它可以从注释中收集的所有映射路径,并检查它是否也有 PreAuthorize 注释。这不是很笼统,但我还是想发布它,也许有人有它的用处。

public class SecurityValidationUtility {

    public static void outputSecurityInfo(ConfigurableApplicationContext context) {

        for (String beanName : context.getBeanDefinitionNames()) {

            Object obj = context.getBean(beanName);
            Class<?> objClz = obj.getClass();
            if (objClz.getAnnotation(Controller.class) != null || objClz.getAnnotation(RestController.class) != null) {

                List<Method> candidateMethods = Arrays.stream(objClz.getDeclaredMethods())
                        .filter(SecurityValidationUtility::isRequestDeclaration)
                        .collect(Collectors.toList());

                for (Method m : candidateMethods) {

                    String url = readUrlFromControllerAnnotation(objClz) + readUrlFromMethodAnnotation(m);

                    if (!StringUtils.isBlank(url)) {
                        if (!m.isAnnotationPresent(PreAuthorize.class)) {
                            System.out.println(StringUtils.rightPad(url, 100, ' ') + ": No Security Configured!");
                        } else {
                            System.out.println(StringUtils.rightPad(url, 100, ' ') + ": " + m.getAnnotation(PreAuthorize.class).value());
                        }
                    }

                }
            }
        }
    }

    private static String readUrlFromControllerAnnotation(Class<?> controllerClass) {
        if (controllerClass.isAnnotationPresent(RequestMapping.class)) {
            String[] values = controllerClass.getAnnotation(RequestMapping.class).value();
            if (values.length > 0) {
                return values[0];
            }
        }
        return "";
    }

    private static boolean isRequestDeclaration(Method m) {
        return m.isAnnotationPresent(GetMapping.class)
                || m.isAnnotationPresent(PostMapping.class)
                || m.isAnnotationPresent(DeleteMapping.class)
                || m.isAnnotationPresent(PutMapping.class)
                || m.isAnnotationPresent(RequestMapping.class);
    }

    private static String readUrlFromMethodAnnotation(Method m) {

        if (m.isAnnotationPresent(GetMapping.class)) {
            GetMapping annotation = m.getAnnotation(GetMapping.class);
            return getAnnotationValue(annotation.value(), annotation.path()) + " - GET";
        } else if (m.isAnnotationPresent(PostMapping.class)) {
            PostMapping annotation = m.getAnnotation(PostMapping.class);
            return getAnnotationValue(annotation.value(), annotation.path()) + " - POST";
        } else if (m.isAnnotationPresent(DeleteMapping.class)) {
            DeleteMapping annotation = m.getAnnotation(DeleteMapping.class);
            return getAnnotationValue(annotation.value(), annotation.path()) + " - DELETE";
        } else if (m.isAnnotationPresent(PutMapping.class)) {
            PutMapping annotation = m.getAnnotation(PutMapping.class);
            return getAnnotationValue(annotation.value(), annotation.path()) + " - PUT";
        } else if (m.isAnnotationPresent(RequestMapping.class)) {
            RequestMapping requestMapping = m.getAnnotation(RequestMapping.class);

            RequestMethod[] method = requestMapping.method();

            if (method.length > 0) {
                String url = getAnnotationValue(requestMapping.value(), requestMapping.path());
                if (StringUtils.isBlank(url)) {
                    return " - " + method[0].name();
                } else {
                    return url + " - " + method[0].name();
                }
            }
        }

        return "";
    }

    private static String getAnnotationValue(String[] value, String[] path) {
        if (value.length > 0) {
            return value[0];
        }

        if(path.length > 0) {
            return path[0];
        }

        return "";
    }
}

该类需要一个应用程序上下文才能工作。我正在使用 spring boot,所以我将它插入到主类中:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplication(Application.class).run(args);
        SecurityValidationUtility.outputSecurityInfo(context);
    }
}

【讨论】:

    猜你喜欢
    • 2011-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    相关资源
    最近更新 更多