用于验证标头的spring-boot自定义批注

时间:2019-02-26 11:12:31

标签: java spring spring-boot spring-mvc spring-security

我正在使用spring-boot-1.5.10,并且在我的应用程序中使用spring-security。我想创建一个自定义批注,并且应该使用securityContextholder ...让我用示例代码详细说明我的问题。

  

卷曲-X GET -H“角色:读取” -H“内容类型:应用程序/ json” -H   “接受:application / json” -H“应用名称:sample” -H“应用ID:sample”   -H“客户编号:123” -H“市场:EN” -H“国家/地区代码:EN” -H“接受语言:应用程序/ json” -H“缓存控制:无缓存”   “ http://localhost:9992/api/v1/apps

控制器

@GetMapping("/apps")
@PreAuthorize("hasAnyAuthority('ROLE_READ', 'ROLE_WRITE')")
public ResponseEntity<List<Apps>> getApps(@AuthenticationPrincipal AppAuthentication appAuthentication) {
    if(appAuthentication.isGrantedAnyOf("ROLE_READ") && isBlank(appAuthentication.getAppContext().customerId())) {
        throw new IllegalArgumentException("Missing header customerId");
    }
    if(appAuthentication.isGrantedAnyOf("ROLE_WRITE") && isBlank(appAuthentication.getAppContext().customerId()) && isBlank(appAuthentication.getAppContext().appId())) {
        throw new IllegalArgumentException("Missing header customerId & AppId");
    }
    //write business logic here
}

spring-security preAuthorize 仅会检查角色是否允许。此外,我可以增强preAuthorize批注,但这对于许多微服务来说是常见的,而且我也没有接触安全领域的权限。因此,我想创建一个自定义注释。我们应该配置角色和标题以验证特定角色。像下面一样

@GetMapping("/apps")
@PreAuthorize("hasAnyAuthority('ROLE_READ', 'ROLE_WRITE')")
@ValidateHeaders("role=ROLE_READ",value={"customerId","app-id"})
public ResponseEntity<List<Apps>> getApps(@AuthenticationPrincipal AppAuthentication appAuthentication) {
    //write business logic here
}

任何提示都是很有意义的。

1 个答案:

答案 0 :(得分:0)

Disclamer-我正在使用Spring Boot 2,因此并非所有内容都适合您

这是我前一段时间实现的简化版本。 我建议为角色和价值观实现枚举。

您不能从@before注释之前进行重定向,因此您必须引发异常并使用全局ex处理程序捕获它,然后从那里进行重定向。

还要考虑在注释中添加可选字段-如果有多个角色,则匹配全部或一个, 重定向路径,在没有访问权限的情况下要调用的异常类型。然后,您可以在异常处理程序中根据调用的异常进行重定向。由于您正在从gloabl ex处理程序进行重定向,因此,如果添加重定向路径,则必须将其与抛出的异常捆绑在一起,这意味着您将需要自定义异常。

注释

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidateHeaders {

    Roles[] roles();
    String[] values();
}

方面类

@Aspect
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) //Autowired annotated lombok generated constructor
public class ValidateHeadersAspect {


    private final @NonNull HttpServletRequest request; //Inject request to have header access
    private final @NonNull UserService userService;//Your user service here

    //Aspect can be placed on clas or method
    @Before("within(@com.org.package.ValidateHeaders *) || @annotation(com.org.package.ValidateHeaders)") 
    public void validateAspect(JoinPoint joinPoint) throws Throwable {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        HasAccess validateHeaders = method.getAnnotation(ValidateHeaders.class);

        if(validateHeaders == null) { //If null it was a class level annotation
            Class annotatedClass = joinPoint.getSignature().getDeclaringType();
            validateHeaders = (ValidateHeaders)annotatedClass.getAnnotation(ValidateHeaders.class);
        }

        Roles[] roles = validateHeaders.roles(); //Roles listed in annotation
        String[] values = validateHeaders.values(); //Values listed in 


        //Validate request here ... determine isAuthorised


        if( !isAuthorized ){
            throw new HeaderAuthrizationException()
        }
    }

}

异常处理程序

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(HeaderAuthrizationException.class)
    public RedirectView HeaderAuthrizationException(HeaderAuthrizationException ex) {
        return new RedirectView("/redirect");
    }

}