是否可以让spring security列出webapp的每个可用URL以及如何保护它?

时间:2018-01-04 10:29:37

标签: spring spring-mvc spring-security

我在弹簧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部分是我现在到处添加的内容。

由于这些方法分散在不同的控制器中,因此我很难检查是否为每种方法添加了适当的安全注释,并且没有错过任何方法。

我想知道的是,是否有某种方法可以使每个已知URL的弹出列表+如何保护该URL。我想用这个作为检查,看看我是否把一切都做对了,偶尔检查一下,如果没有人忘记为新方法添加安全性。

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

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

1 个答案:

答案 0 :(得分:0)

感谢dur的建议。我认为@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 "";
    }
}

该类需要一个应用程序上下文来处理。我使用弹簧靴,所以我将它插入主类:

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