如何向 Spring MVC 控制器方法添加自定义安全注释

2022-09-04 20:03:33

我正在使用Java 8,Spring MVC 4,Spring Boot和Gradle作为我的REST应用程序。

我想通过某些Spring MVC 4控制器中的自定义方法注释为我的REST应用程序添加安全性。

下面是一个基本示例。

主控制器.java

package myapp;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
@RequestMapping("/")
public class HomeController {

    @RequestMapping("/")
    public String index() {
        return "<h1>Hello, World!</h1><p>Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum</p>";
    }

    @CustomSecurityAnnotation
    @RequestMapping("/secure")
    public String secure() {
        return "<h1>Secured!</h1><p>Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum</p>";
    }
}

我想运行一个自定义方法,该方法将检查传入REST请求的标头,查看HTTP标头,提取提供的值(如果提供了值),并针对该值运行代码以验证请求,然后再允许该方法继续。如果授权无效,注释应能够覆盖响应并提供 HTTP 401 或 403,并在我决定注释自定义方法成功通过时允许注释下的方法运行。CustomSecurityAnnotationAuthorization

我意识到我可以对其他MVC注释做类似的事情,但我正在考虑将自定义逻辑打包到一个方法中,放在一个注释中,以便在我选择的任何控制器上的任何方法上使用。PreAuthorize

谢谢!


答案 1

我们还在项目中创建了一个自定义注释。你需要完成这个任务的是一些面向方面的编程。

首先,您需要创建自己的注释来标记方法,如下所示:

public @interface CustomSecurityAnnotation {
}

然后,您必须编写在执行方法时触发的逻辑。你为此写了一个方面

@Aspect
@Component
public class CustomSecurityAspect {
    @Pointcut("@annotation(my.package.CustomSecurityAnnotation)")
    private void customSecurityAnnotation() {
    }

    @Around("my.package.CustomSecurityAspect.customSecurityAnnotation()")
    public Object doSomething(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest req = getRequest();
        // Check header values
        // Throw Spring's AccessDeniedException if needed
        return pjp.proceed();
    }

    private HttpServletRequest getRequest() {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return sra.getRequest();
    }
}

如您所见,我已经包含了一个检索当前HTTP请求的方法,因此您可以轻松检索要检查的标头。

如果是 ,Spring 会自动将响应状态代码设置为 HTTP 403。AccessDeniedException

不要忘记在类上启用以启用方面。@EnableAspectJAutoProxy@Configuration


答案 2

如果您不想创建自己的Aspect(或者您想依靠普通的Spring-Security),您可以采用Spring Security的机制并创建自己的自定义,如下所示:https://www.baeldung.com/spring-security-create-new-custom-security-expression#2-custom-expression-handlerSecurityExpressionRoot

精彩的部分是你有框架提供的方法调用,通过反射获取你的注释,并将其传递给你的.在 Kotlin 中,如下所示:CustomSecurityExpressionRoot

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
class MethodAuthConfig: GlobalMethodSecurityConfiguration() {
override fun createExpressionHandler(): MethodSecurityExpressionHandler = object: DefaultMethodSecurityExpressionHandler() {
    override fun createSecurityExpressionRoot(authentication: Authentication, invocation: MethodInvocation): MethodSecurityExpressionOperations =
        SecuredTargetExpressionRoot(
            authentication,
            // This is the important part to find your own annotation and pass it to your method security resolver
            invocation.method.annotations.mapNotNull { it as? SecuredTarget }.firstOrNull()
        ).apply {
            setThis(invocation.getThis())
            setPermissionEvaluator(permissionEvaluator)
            setTrustResolver(trustResolver)
            setRoleHierarchy(roleHierarchy)
        }
    }
}

唯一缺少的是将SpringSecurity注释添加到您自己的注释中,例如:

@Target(FUNCTION)
@Retention(RUNTIME)
// This is the important line to let Spring security kick in action
@PreAuthorize("canAccessTarget()")
annotation class SecuredTarget (
    // maybe any variables your want to specify at this annotation
)

并查看您的自定义SpEl弹簧安全:

class SecuredTargetExpressionRoot(
    authentication: Authentication,
    private val securityInformation: SecuredTarget?
): SecurityExpressionRoot(authentication), MethodSecurityExpressionOperations {
    fun canAccessTarget(): Boolean {
        return // Your code of verification goes here :)
    }
}

(我在我的 kotlin 项目中创建了这个。但我想你把这些行转换成Java代码没有问题。您需要执行的大部分操作都在链接的教程中。我只是指出从哪里获取自定义注释信息;)从好的方面来说:你可以使用所有的Spring-Securitys机制,而不必编写自己的逻辑来捕获异常等。