如何在 Spring Security 中自定义权限表达式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在 Spring Security 中自定义权限表达式相关的知识,希望对你有一定的参考价值。

参考技术A 在前面的文章中,松哥已经和小伙伴们聊了 Spring Security 中的权限表达式了,还没看过的小伙伴们可以先看下,本文将在前文的基础上继续完善:

经过上篇文章的学习,小伙伴们已经知道了,在 Spring Security 中,@PreAuthorize、@PostAuthorize 等注解都是支持 SpEL 表达式的。

在 SpEL 表达式中,如果上来就直接写要执行的方法名,那么就说明这个方法是 RootObject 对象中的方法,如果要执行其他对象的方法,那么还需要写上对象的名字,例如如下两个例子:

上面这个例子中,表达式中的方法是 hasAuthority,没有写对象名,那么就说明这个方法是 SpEL 中 RootObject 对象中的方法。

上面这个例子中,权限注解中的表达式方法是 @ss.hasPermi('monitor:operlog:list') ,其中 ss 是指 Spring 容器中的一个对象名,hasPermi 则是这个对象中的方法。

好啦,经过前面文章的学习,这些基本知识大家都已经掌握了。

其实上面给出来的第二个例子就是一个自定义的例子。

不过,这种自定义方式太自由了,自由到没有在 Spring Security 架构内完成这件事。所以,今天我想和小伙伴们聊一聊,如何在不使用第三方对象的情况下,来自定义一个权限判断的表达式。

首先小伙伴们知道,我们在 @PreAuthorize 注解中使用的不用加对象名就能调用的权限方法,如 hasAuthority 、 hasPermission 、 hasRole 、 hasAnyRole 等,基本上都是由 SecurityExpressionRoot 及其子类提供的,准确来说是由 MethodSecurityExpressionRoot 类提供的。

MethodSecurityExpressionRoot 类实际上继承自 SecurityExpressionRoot,只不过增加了过滤对象以及返回值对象。我们来看下 MethodSecurityExpressionRoot 的方法摘要:

再来看看 SecurityExpressionRoot 中的方法:

这些就是 RootObject 对象中的所有方法了,也是我们能够在 @PreAuthorize 注解中使用的所有方法了。

那么现在想在已有方法上继续扩展新方法,那么我们可以通过自定义类继承自 SecurityExpressionRoot 对象,扩展这个 RootObject 对象,在该对象中继续添加新的方法,进而实现自定义权限表达式。

好啦,说干就干,开搞!

本文的案例在前文的基础上继续完成,所以这里我就不从头开始写了。

首先我们自定义一个类继承自 SecurityExpressionRoot 并实现 MethodSecurityExpressionOperations 接口(本来直接继承自 MethodSecurityExpressionRoot 即可,但是因为这个类不是 public 的,没法继承,所以我们就实现 MethodSecurityExpressionOperations 接口即可):

加了 @Override 注解的方法,都是普普通通的常规方法,没啥好说的。我们自己主要实现了三个方法,分别是:

这里边的逻辑我就不啰嗦了,都是基本的 Java 语法而已。

另外,用 AntPathMatcher 做比对是为了支持通配符,这个在上篇文章中已经说过了,这里不再赘述。

Spring Security 中,MethodSecurityExpressionRoot 的配置是通过 DefaultMethodSecurityExpressionHandler 来完成的,现在我们自定义了 CustomSecurityExpressionRoot,那也得有一个 Handler 来配置 CustomSecurityExpressionRoot,所以,再来一个类继承自 DefaultMethodSecurityExpressionHandler,如下:

在 createSecurityExpressionRoot 方法中创建一个 CustomSecurityExpressionRoot 对象,对象的 TrustResolver、权限评估器以及角色层级等,统统都用默认的方案即可。

配置完成后,再配置一下 CustomMethodSecurityExpressionHandler 这个 Bean 即可,如下:

好啦,这就注入成功了。

接下来,我们就可以在权限注解中使用这个自定义的方法了:

这个自定义权限表达式的思路,说到底还是在 Spring Security 体系中玩,个人感觉这种方式更合理一些。

在 TienChin 项目中,松哥也将按照这种思路去改造 RuoYi-Vue 脚手架。届时在 TienChin 项目视频中,我再和大伙细聊,对视频感兴趣的小伙伴,戳这里:TienChin 项目配套视频来啦。

如何在 Spring Boot 1.4 中自定义 Jackson

【中文标题】如何在 Spring Boot 1.4 中自定义 Jackson【英文标题】:How to customise Jackson in Spring Boot 1.4 【发布时间】:2017-01-08 20:48:48 【问题描述】:

我一直无法找到如何在 spring boot 1.4 中使用 Jackson2ObjectMapperBuilderCustomizer.java 来自定义 Jackson 的功能的示例。

在 boot 1.4 中自定义 Jackson 的文档 - https://docs.spring.io/spring-boot/docs/1.4.x/reference/htmlsingle/#howto-customize-the-jackson-objectmapper

我的配置有效,但我不确定这是否是使用 Jackson2ObjectMapperBuilderCustomizer.java

自定义对象映射器的正确方法
@Configuration
public class JacksonAutoConfiguration 

    @Autowired
    private Environment env;

    @Bean
    public Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder(
        List<Jackson2ObjectMapperBuilderCustomizer> customizers) 
        Jackson2ObjectMapperBuilder builder = configureObjectMapper();
        customize(builder, customizers);
        return builder;
    

    private void customize(Jackson2ObjectMapperBuilder builder,
                           List<Jackson2ObjectMapperBuilderCustomizer> customizers) 
        for (Jackson2ObjectMapperBuilderCustomizer customizer : customizers) 
            customizer.customize(builder);
        
    

    private Jackson2ObjectMapperBuilder configureObjectMapper() 
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        List<String> activeProfiles = asList(env.getActiveProfiles());
        if (activeProfiles.contains(SPRING_PROFILE_DEVELOPMENT)) 
            builder.featuresToEnable(SerializationFeature.INDENT_OUTPUT);
        
        return builder;
    

为了提供一些上下文,这个类位于我自己的 REST 服务 spring 启动项目中,它只是自动配置一些东西,比如 ControllerAdvice 和一些像上面这样的琐碎功能。

所以我的目标是扩展 Jackson 配置,而不是覆盖 boot 或其他包提供的任何配置。

【问题讨论】:

【参考方案1】:

要自定义 Spring Boot 已经预配置的 Jackson ObjectMapper,我能够做到这一点(这里的示例是添加自定义反序列化器)。

配置类:

@SpringBootConfiguration
public class Application 

    @Autowired
    private BigDecimalDeserializer bigDecimalDeserializer;

    ...

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization() 
        return new Jackson2ObjectMapperBuilderCustomizer() 

            @Override
            public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) 
                jacksonObjectMapperBuilder.deserializerByType(BigDecimal.class, bigDecimalDeserializer);
            

        ;
    

    ...


还有我的自定义反序列化器,以展示 Spring 是如何拾取它的:

@Component
public class BigDecimalDeserializer extends StdDeserializer<BigDecimal> 

    public BigDecimalDeserializer() 
        super(BigDecimal.class);
    

    @Override
    public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException 
        ...
    

    ...


【讨论】:

看准了!这是我最初尝试做的,但一定是做错了什么,谢谢。【参考方案2】:

这取决于你想要做什么。

如果除了默认执行的自定义之外,您还想进行一些自定义,那么您应该创建自己的 Jackson2ObjectMapperBuilderCustomizer 实现并将其公开为 bean。您目前拥有的是一个更复杂的版本。与其注入定制器然后自己调用它们,不如创建自己的定制器 bean,Boot 会为您调用它。

如果您想完全控制并关闭所有 Boot 的自定义设置,请创建一个 Jackson2ObjectMapperBuilderObjectMapper bean 并根据需要进行配置。构建器方法是首选,因为此构建器还用于配置由其他组件(如 Spring Data REST)创建的 ObjectMapper。

查看您的代码并退后一步,您可以通过使用特定于配置文件的配置文件(例如 application-dev.properties)来启用 Jackson 输出的缩进,从而更简单地进行配置。您可以阅读更多关于 here 的信息。

【讨论】:

谢谢这是我最初所做的,但它似乎没有效果。我明天会发布我的代码,也许你可以告诉我哪里没有错。至于您通过属性文件配置它的建议,我已经更新了我的问题,以提供更多关于我如何使用此配置类的上下文。【参考方案3】:

只需创建一个ObjectMapper bean:

@Bean
ObjectMapper objectMapper() 
    return Jackson2ObjectMapperBuilder
            .json()
            .featuresToEnable(MapperFeature.DEFAULT_VIEW_INCLUSION)
            .build();

【讨论】:

抱歉,我可能更清楚地表明我实际上是在扩展对象映射器功能。而您的建议实际上会覆盖任何 ObjectMapper bean,例如默认的一个 spring boot 配置 这对于 Spring Boot 来说不是很好的方法,因为可以有多个 jackson 定制器,并且返回构建的对象映射器实例使它们完全无用。

以上是关于如何在 Spring Security 中自定义权限表达式的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Security 中自定义权限表达式

如何在 Spring Security 中自定义权限表达式

如何在 Spring Security 中自定义权限表达式

使用 AuthenticationFailureHandler 在 Spring Security 中自定义身份验证失败响应

在 Spring Security 中自定义 CSRF 错误页面

Gateway 整合 Spring Security鉴权