pkalinow
pkalinow

Reputation: 1741

How to use @PathVariable to resolve parameter name in Spring Security?

I would like to use @PreAuthorize annotations to secure methods in Spring REST controller, using method parameters, e.g.

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

Spring Security needs a way to discover param names in runtime. When there is no debugging symbols in the compiled class, it is necessary to add a special annotation @P or Spring Data's @Param. So, the method would look like this:

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

Is it possible to somehow hint Spring Security to use @PathVariable instead and avoid additional annotations like @P?

According to the documentation reading parameter names from annotations is done by AnnotationParameterNameDiscoverer which can be customized to support the value attribute of any specified annotation. However, I could not find any information on how to customize it.

BTW, I'm using Java 7 and Spring Security 3.2.9.

Upvotes: 9

Views: 6631

Answers (4)

Ilya
Ilya

Reputation: 2167

Following configuration was not tested, but based on research of sources of spring security, so try to change your Spring Security configuration xml as follows

<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>

Upvotes: 2

vzamanillo
vzamanillo

Reputation: 10404

From the official Spring security docs GlobalMethodSecurityConfiguration

Sometimes you may need to perform operations that are more complicated than are possible with the @EnableGlobalMethodSecurity annotation allow. For these instances, you can extend the GlobalMethodSecurityConfiguration ensuring that the @EnableGlobalMethodSecurity annotation is present on your subclass. For example, if you wanted to provide a custom MethodSecurityExpressionHandler, you could use the following configuration:

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

As in the above example you can write your custom MethodSecurityExpressionHandler or use the DefaultMethodSecurityExpressionHandler and set your custom ParameterNameDiscoverer extending the DefaultSecurityParameterNameDiscoverer (or not)

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

One more example Spring Security Java Config Preview: Custom Method Security

Hope this helps.

Upvotes: 3

rajadilipkolli
rajadilipkolli

Reputation: 3601

From Spring security official documentation the requested usecase can be achieved . But to use you need to upgrade to spring 4.1.0, I didn't tried by looks like this is achievable

Upvotes: 0

Roman Sandarkin
Roman Sandarkin

Reputation: 416

In short, you need to override creating of SecurityExpressionHandler in the method GlobalMethodSecurityConfiguration#createExpressionHandler so that set your own ParameterNameDiscoverer in custom GlobalMethodSecurityConfiguration.

@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;
  }

}

In the sample project you can see result in console output something like this

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

Best regards

Upvotes: 5

Related Questions