i've tried in various ways to replicate the #PreAuthorize behaviour, so spel expression with a Method Invocation context:
-I started with configuring httpSecurity in my WebSecurityConfigurerAdapter extending class with an access string written in spel, only to figure out the context was on Filter Invocation , so I had no access on the request body(which i need);
-implementing and adding a custom HandlerInterceptor to the InterceptorRegistry, but again the endpoint arguments were not accessible;
-extending ConfigGlobalMethodSecurity to create a custom expressionHandler, but I seems it is only triggered by Method-level annotations;
Can someone explain me if what i've trying to do is simply impossible or is there a way?
I'd like to have the same evaluationContext as #PreAuthorize, so having access the method arguments(I mean the value they assume) using spel expressions and be able to configure it without having to annotate every single class or method.
EDIT
for reference these are the two annotations i'm using(and they work fine) the use i'm tring to replicate not by annotations but by configuration:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole(#privilegeManager.privilegedRoles) or (#privilegeManager.verify(#id, this.getType()))")
public #interface PathVariableRestriction {
}
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole(#privilegeManager.privilegedRoles) or #dto.getOwnerId() == #myService.getCurrentId()")
public #interface RequestBodyRestriction {
}
i'd like to authorize request not based on roles but on ids: the id of the object being subject to the crud operation provided either as #PathVariable or #RequestBody(depends on if it is a get, post, put or delete) and the id of the current user retrieved through his Authentication
This is one common use case for #PostAuthorize.
For example if you do:
#PostAuthorize("returnObject.username == authentication.name")
#GetMapping("/resource/{id}")
public MyResource getResource(String id) {
// look up
return myResource;
}
This will only return resources that belong to the currently authenticated user.
This is the recommended route.
able to configure it without having to annotate every single class or method
Alternatively, you can build your own custom authorization method security from scratch using Spring Security's underlying components.
Method Security is built on top of Spring AOP. This means that you can define your own pointcut instead of using Spring Security's annotation-based one. For example, you can do:
#EnableMethodSecurity
class SecurityConfiguration {
#Bean
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor myMethodSecurityAdvisor() {
Pointcut pointcut = myCustomPointcut();
AuthorizationManager<MethodInvocationResult> rules = myRules();
AuthorizationManagerAfterMethodInterceptor interceptor =
new AuthorizationManagerAfterMethodInterceptor(
pointcut, rules);
interceptor.setOrder(AuthorizationInterceptorsOrder.
POST_AUTHORIZE.getOrder() + 1);
return interceptor;
}
}
Related
I am trying to get a #PreAuthorized annotation on a controller class to work in conjunction with a #PreAuthorized annotation on methods (endpoints) of the same class.
The overview of the class looks something like this:
#RestController
#RequestMapping("/api")
#PreAuthorized("hasRole('CLASS_LEVEL_ROLE')")
public class foo {
#GetMapping("/test")
#PreAuthorized("hasRole('METHOD_LEVEL_ROLE')")
#Timed
public ResponseEntity<String> bar() {
return ResponseEntity.ok().body("entered method successfully");
}
}
Currently what is happening is only the method level annotation is being taken into account.
Ideally what would happen is only users with role 'CLASS_LEVEL_ROLE' and 'METHOD_LEVEL_ROLE' would be allowed access to bar().
I'm aware I could use
#PreAuthorized("hasRole('CLASS_LEVEL_ROLE') and hasRole('METHOD_LEVEL_ROLE')")
but I have some controllers where all endpoints would have to have the same 'CLASS_LEVEL_ROLE' and it would be more convenient to have a generalized class annotation.
#PreAuthorize allows a class level annotation. The way it is supposed to work is that if a method level annotation exists, it will override the class level annotation. You can't do a union of both. So a class level annotation can be seen as a fallback when a method level annotation is not present.
Is it possible to create a Java annotations equivalent to a Spring MVC #RequestMapping with a predefined set of parameters?
For instance, something that allows me to simply use:
#PostJson("/input")
public Output myMethod(Input input) {
instead of:
#RequestMapping(value = "/input",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public Output myMethod(Input input) {
UPDATE:
Let me try to dig a little deeper. AFAIK, Spring seems to be able to handle meta-annotations when scanning for beans. For instance, if I create an annotation, lets say #MyComponent, and annotate it with #Component:
#Component
public #interface MyComponent {
String value() default "";
}
Spring seems able to find beans with #MyComponent and to recognize the parameters (value in this case) in #MyComponent as if they were from #Component
#MyComponent("MyBean")
public class SomeClass {
I've tryed similar tactic with #RequestMapping
#RequestMapping(method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public #interface PostJson {
String value() default "";
}
The fixed parameters (method an produces) seems to be correct, nonetheless, the variable parameter (value) is ignored.
I am hoping that this is not a #Component specific feature and that I could use it with #RequestMapping.
Not easily, no. The #RequestMapping annotation is tied to RequestMappingHandlerMapping and RequestMappingHandlerAdapter. These are two elements of the MVC stack registered by default when you provide <mvc:annotation-driven /> or #EnabledWebMvc. They only scan for #Controller and #RequestMapping, nothing else.
To make your own annotation work, you'd have to override (and register) these two or create new ones from scratch which provide the scanning and handling of handler methods. You can get some inspiration from those classes and other HandlerMapping implementations, but it really isn't a simple task.
You might, alternatively, want to look into Java Restful Web Services which can integrate quite well with Spring (not necessarily Spring MVC though). It can provide some less bloated mapping annotations when you know exactly what you want.
While this isn't currently supported out of the box but will be (thanks for creating https://jira.spring.io/browse/SPR-12296!), it isn't hard to do. If you look in RequestMappingHandlerMapping the protected method getMappingForMethod accepts a method and returns a RequestMappingInfo populated with information from the type + method-level #RequestMapping annotations. You can however populate this RequestMappingInfo from anything, e.g. your own annotation, or some other source (external routes configuration). Just follow the example of the code there.
I'm trying to implement a #Restricted annotation, to secure controller methods in a way that users can only access them, when they are logged in and have a certain role. I'm on Tomcat 7 using JSF and CDI, so no EJB. The interceptor gets called as long as the annotation interface does not specify any parameters. As soon as I add a #Nonbinding Role value() default Role.ADMIN; parameter, neither the interceptor nor the controller method execute. No errors or exceptions either. Here is my code, I really don't know what's wrong with it:
Annotation:
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface Restricted {
#Nonbinding Role value() default Role.ADMIN; // ###
}
Interceptor:
#Interceptor
#Restricted
public class RoleBasedRestrictingInterceptor implements Serializable {
#Inject
ISecurityManager security;
#AroundInvoke
public Object intercept(final InvocationContext ctx) throws Exception {
final Restricted annotation = ctx.getClass().getAnnotation(Restricted.class);
log.info("Intercepted, required role is: {}", annotation.value()); // ###
log.info("User is logged in: {}", security.isLoggedIn());
return ctx.proceed();
}
}
Controller:
#Named("manageUsers")
#SessionScoped
public class ManageUsersBacking extends implements Serializable {
#Restricted(Role.ADMIN) // ###
public void testRestricted() {
log.info("testRestricted()");
}
}
The ### occurrences mark what has to be changed or removed to make it work again. The interceptor is properly defined in WEB-INF/beans.xml, since it works without the role parameter in my annotation.
16:04:33.772 [http-apr-8080-exec-11] INFO c.m.s.RoleBasedRestrictingInterceptor - User is logged in: true
16:04:33.772 [http-apr-8080-exec-11] INFO c.m.c.admin.ManageUsersBacking - testRestricted()
Today I revisited this particular problem and noticed it had nothing to do with CDI:
ctx.getClass().getAnnotation(Restricted.class)
Obviously, there is no class level annotation in my example. So getAnnotation() returns null. Instead I should have used the following:
ctx.getMethod().getAnnotation(Restricted.class)
Though I don't know why there where no exceptions whatsoever. Maybe some other things were going on, that I can no longer reproduce because I migrated my application to TomEE.
if you switch to TomEE you'll don't need to depend (maven) on implementations, just api (use org.apache.openejb:javaee-api:6.0-4 with a provided scope
It sounds like you have things setup correct (beans.xml and interceptor). Which CDI implementation are you using? If you're using Tomcat have you looked at using TomEE?
I have seen few examples where customized annotations were used. example
#SimpleAnnotation
class SampleBean {
#SimpleAnnotation
public String getName() {
return "AAA";
}
public void setName(String name) {
}
public int getHeight() {
return 201;
}
}
#Target( { ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#interface SimpleAnnotation {
}
Can anyone tell why we use this?
Spring supports for many Annotation the concept of "meta-annotation". (I am not sure if it is for all.)
This mean that you can build your own annotation and annotate the annotation with one of springs "core" annotations.
For example:
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Service
public #interface MyService {
}
Then you can use this annotation instead of #Service. (Btw: #Service, #Repository, #Controller use the same technique to "inherit" from #Component)
One example that make heavy use of this is "inherit" from #Qualifier.
For an example and some explanation have a look at Spring Reference Chapter: 3.9.3 Fine-tuning annotation-based autowiring with qualifiers (The Example with #Genre is at the end of the chapter.)
One very usefull construct that can be done with that technique is, that it enables you to combine several Annotations to a (in your use case) more meaning full. So instead of writing at every class of some type allways the same two annotations, for example: #Service and #Qualifiyer("someting") (the org.springframework.beans.factory.annotation.Qualifier). You can create your custom annotation that is annotated with this two annotations, and then use in your beans only this one custom annotation. (#See Avoid Spring Annotation Code Smell Use Spring 3 Custom Annotations)
If you want to see how powerfull this technique can be use, you can have a look at Context and Dependency Injection Framework.
Question from the comment:
The #interface also has some variables defined inside it, what does that signify?
The Annotations (defined by #Interface) work a bit like beans. This Fields are the properties that can/must be define if you use the annotations. The values can be later on be read via reflection API.
For example the #Controller Annotation in Spring:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Controller {
String value() default "";
}
The field with name value is that field that can be used without explicit name it: (#Controller("myUrl") is the same #Controller(value="myUrl"))
You can create your own meta-annotations that collect several other Spring annotations to reduce meta-boilerplate in your code:
#Service
#Scope(value = "prototype")
#Transactional(readOnly = true, rollbackFor = RuntimeException.class)
public #interface ReadOnlyService {}
And then you can simply write:
#ReadOnlyService
public class RoleService {
}
Spring will find the #ReadOnlyService and semantically replace it with:
#Service
#Scope(value = "prototype")
#Transactional(readOnly = true, rollbackFor = RuntimeException.class)
public class RoleService {
}
Of course having custom annotations pays of when you have tons of services annotated with the same set of Spring annotations that can be replaced with one, well named annotation.
Examples taken from: Avoid Spring Annotation Code Smell: Use Spring 3 Custom Annotations
Custom annotations do not do anything on their own. They are simple markers in code. Their real power comes from tools that look for specific annotations. Like some of the other answers mention, Spring has several uses for annotations and now mechanisms for defining your own component types. Pretty neat. Another example, a few weeks ago I used AOP and a few custom annotations to provide simple method level result caching. Now that I have the caching engine in place, and the appropriate AOP hooks defined, if I want to cache a method, I simply add that annotation. Some people simply use the annotations as fancy metadata to improve readability.
At the end of the day, they are a fairly simple tool that you can use for a great number of things.
The best part of using custom annotations is that you don't have to make any configuration, Spring will auto detect that these beans are service components and everything will work fine. Custom Annotations are a very small feature added in Spring but are very useful.For details take a look at this
http://java.dzone.com/articles/avoid-spring-annotation-code-smell-use-spring3-custom-annotations
Two options:
you need the #Component annotation on your custom annotation. That way you can use your custom annotation to mark classes as beans. In addition, you can add a default scope and other meta-information
qualifiers - you can use qualifier annotations (annotated with the #Qualifier meta-annotation) to distinguish between implementations of the same interface.
A common pattern is also to use annotations in AOP pointcuts. Not specifically Spring, but often employed when making use of Spring AOP.
I'm trying to protect a Controller with the #PreAuthorize annotation at type level and try to override that behavior by annotating some methods with a different #PreAuthorize. The Problem is however, that Spring is evaluating the method annotation first (grants access) and is then evaluating the class annotation (denies access).
Is there any way to reverse that order? I couldn't figure it out yet.
Edit:
On the method level, I want to grant access to non-registered Users only:
#PreAuthorize("isAnonymous()")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String renderCreateEntity(ModelMap model) {
return userService.renderCreateEntity(model);
}
The standard for this Controller however, should be to allow fully authenticated users only:
#Controller
#RequestMapping(value = "/user")
#PreAuthorize("isFullyAuthenticated()")
public class UserController { [...] }
When debug-stepping through the app, I see that isAnonymous() is evaluated first and then isFullyAuthenticated() thus resulting in an grant of access right and immediately denying access again.
Thanks for all your replys.
The answer however, was something totally different :)
I put this here in case anyone else has the same problems.
I registered a custom validator in an #InitBinder annotated method. This binding method is called AFTER the method call requested on the controller. And since this binding method was not annotated with #PreAuthorize, the request was denied.
The solution was to annotate the binding method like this:
#InitBinder
#PreAuthorize("permitAll")
public void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
And then, the method calls from my OP evaluated like expected.
The problem is not that you need to change the order of grant and deny. The problem is simple that that method level annotations override the class level annotations.
PrePostAnnotationSecurityMetadataSource Java Doc:
Annotations may be specified on classes or methods, and method-specific annotations will take precedence.
The concrete implementation of this logic is done in the method findAnnotation of class PrePostAnnotationSecurityMetadataSource. (Unfortunately this method is private.)
So you can write your own MethodSecurityMetadataSource, if you have a look at the code of PrePostAnnotationSecurityMetadataSource, you will see how easy it is.
But one warning at the end: the end: difficult task is not rewriting the method, the difficult task is to "inject" the new MethodSecurityMetadataSource into the security system. I belive you can not do it with the spring security namespace configuration, so you need to replace spring security namespace by explicit bean declaration.