【问题标题】:How to use @PathVariable to resolve parameter name in Spring Security?如何使用@PathVariable 解析Spring Security 中的参数名称?
【发布时间】:2016-09-22 23:31:50
【问题描述】:

我想使用 @PreAuthorize 注释来保护 Spring REST 控制器中的方法,使用方法参数,例如

@RequestMapping("/something/{myParam}")
@PreAuthorize("@security.check(#myParam)")
public String getSomething(@PathVariable("myParam") Integer myParam) {
  //...
}

Spring Security 需要一种在运行时发现参数名称的方法。当编译的类中没有调试符号时,需要添加一个特殊的注解@P或者Spring Data的@Param。因此,该方法如下所示:

@RequestMapping("/something/{myParam}")
@PreAuthorize("@security.check(#myParam)")
public String getSomething(@PathVariable("myParam") @P("myParam) Integer myParam) {
  //...
}

是否有可能以某种方式提示 Spring Security 使用 @PathVariable 并避免像 @P 这样的附加注释?

根据the documentation从注解中读取参数名称是由AnnotationParameterNameDiscoverer完成的可以自定义支持任何指定注解的value属性。但是,我找不到任何关于如何自定义它。

顺便说一句,我使用的是 Java 7 和 Spring Security 3.2.9。

【问题讨论】:

  • 你用的是什么版本的spring-core?
  • @RomanSandarkin 4.1.6.RELEASE
  • 谢谢。看来我找到了解决办法)

标签: java spring spring-mvc spring-security


【解决方案1】:

简而言之,您需要在 GlobalMethodSecurityConfiguration#createExpressionHandler 方法中覆盖 SecurityExpressionHandler 的创建,以便在自定义 GlobalMethodSecurityConfiguration 中设置您自己的 ParameterNameDiscoverer

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

  @Autowired
  private ApplicationContext context;

  @Override
  protected MethodSecurityExpressionHandler createExpressionHandler() {
    DefaultMethodSecurityExpressionHandler result = new DefaultMethodSecurityExpressionHandler();
    result.setApplicationContext(this.context);
    result.setParameterNameDiscoverer(new AnnotationParameterNameDiscoverer(PathVariable.class.getName()));
    return result;
  }

}

sample project 中,您可以在控制台输出中看到类似这样的结果

2016-06-06 17:09:01.635  INFO 2871 --- [nio-8080-exec-4] c.s.so.q37435824.SecurityService: myParam value from PathVariable equals 1

最好的问候

【讨论】:

  • 这与 vzamanillo 的答案类似,只是没有自定义 ParameterNameDiscoverer。事实上,我已经实现了几乎相同的一个。但是,我没有在表达式处理程序上设置 applicationContext。这里需要吗?
  • @pkalinow,是的,必须设置applicationContext,否则在SpEL表达式求值过程中会得到No bean resolver registered in the context to resolve access to bean 'securityService'SpelEvaluationException
  • 我没有收到这样的错误。也许是因为我还使用另一个 @Configuration 类来实现网络安全 - 扩展 WebSecurityConfigurerAdapter
【解决方案2】:

来自官方 Spring 安全文档GlobalMethodSecurityConfiguration

有时您可能需要执行更复杂的操作 比 @EnableGlobalMethodSecurity 注释更不可能 允许。对于这些情况,您可以扩展 GlobalMethodSecurityConfiguration 确保 @EnableGlobalMethodSecurity 注释存在于您的子类中。 例如,如果您想提供自定义 MethodSecurityExpressionHandler,你可以使用以下 配置:

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        // ... create and return custom MethodSecurityExpressionHandler ...
        return expressionHandler;
    }
}

如上例所示,您可以编写自定义MethodSecurityExpressionHandler 或使用DefaultMethodSecurityExpressionHandlerset 您自定义的ParameterNameDiscoverer 扩展DefaultSecurityParameterNameDiscoverer(或不扩展)

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
        expressionHandler.setParameterNameDiscoverer(new CustomParameterNameDiscoverer());
        return expressionHandler;
    }
}

再举一个例子Spring Security Java Config Preview: Custom Method Security

希望这会有所帮助。

【讨论】:

  • 谢谢!我在我的测试配置中使用了这个解决方案——不是使用自定义参数名称发现器,而是使用AnnotationParameterNameDiscoverer,设置所需的注释以使用:new AnnotationParameterNameDiscoverer(PathVariable.class.getName(), P.class.getName())。它有效。
【解决方案3】:

以下配置未经测试,但基于sources of spring security的研究,因此尝试更改您的Spring Security配置xml如下

<security:global-method-security pre-post-annotations="enabled">
    <security:expression-handler ref="expressionHandler"/>
</security:global-method-security>

<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="parameterNameDiscoverer" ref="parameterNameDiscoverer"/>
</bean>

<bean id="parameterNameDiscoverer" class="org.springframework.security.core.parameters.AnnotationParameterNameDiscoverer">
    <constructor-arg>
        <list>
            <value>org.springframework.web.bind.annotation.PathVariable</value>
        </list>
    </constructor-arg>
</bean>

【讨论】:

  • 谢谢!此解决方案有效,我已在配置中使用它,并进行了一些更改: 1. 构造函数参数应该是一个列表(已编辑您的答案)。 2. 这些 bean 应该在 mvc-dispatcher-servlet.xml 中定义,或者在任何定义 REST 控制器的地方。
【解决方案4】:

来自 Spring 安全官方documentation 请求的用例可以实现。但是要使用你需要升级到spring 4.1.0,我没有尝试过,看起来这是可以实现的

【讨论】:

  • 我知道这种方法,但这并不是我所需要的。我想在控制器上使用方法注释,而不是 URI 模式。
猜你喜欢
  • 2017-09-26
  • 1970-01-01
  • 1970-01-01
  • 2018-02-16
  • 2017-08-01
  • 2016-03-05
  • 1970-01-01
  • 1970-01-01
  • 2021-02-03
相关资源
最近更新 更多