Is it possible to extend Spring #RequestMapping with an own annotation? - java

I have a custome annotation:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Controller
#RequestMapping("auth/firebase")
public #interface FirebaseAuth {
#AliasFor(annotation = RequestMapping.class, attribute = "path")
String[] path() default {};
}
I want to achive, that a controller which is annotated with:
#FirebaseAuth(path = "user")
public class UserController {
Will end up with a path like this: auth/firebase/user, to secure the resource
How is it possible to extend the path with auth/firebase before "user" with an annotation (not with a superclass)?
Thanks! :)

Related

Custom PreAuthorize annotation in Spring Security

I'm trying to understand Spring Security and I'm wondering about creating my own annotations with authorities I've created. I've got something like this:
#PreAuthorize("hasAuthority('STANDARD')")
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface StandardRole {
}
#PreAuthorize("hasAuthority('ADMIN')")
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AdminRole {
}
and actually it looks like:
#AdminRole
#StandardRole
#GetMapping(path = "user", produces = "application/json")
public ResponseEntity<User> getUser(#RequestParam String login) {
...
}
but only first annotation works, second one is ommited. I want to do something like #AllowRoles() annotation, for example #Allow({UserType.ADMIN, UserType.STANDARD}) or #Allow({UserType.ADMIN}).
How can I do this? Thanks.
It's a pity to force the door open.
I used jsr250 annotation in my SecurityConfig class:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#RolesAllowed in my controller:
#RolesAllowed({UserType.TYPE_1, UserType.TYPE_2})
#GetMapping(path = "user", produces = "application/json")
public ResponseEntity<User> getUser() {
Finally, at my CustomDetails implementing UserDetails:
private static final String PREFIX = "ROLE_";
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(
new SimpleGrantedAuthority(
PREFIX + user.getUserType()));
}
I forgot about "ROLE_" prefix. Code is much cleaner.

Is it possible to combine spring boot #Value with javax.validation.constraints?

I would like to validate values in application.properties.
I use #Value to inject and #NotBlank to validate the method parameter in a class annotated with #Configuration.
createSslContext(
#Value("#{systemProperties['javax.net.ssl.trustStore']}")
#NotBlank(message = "Truststore is needed")
String path)
I have included the hibernate-validator and the hibernate-validator-annotation-processor properties. Using Spring 2.2.0.RELEASE.
It doesn't work. The value is injected, but not validated. What am I missing?
Add #Validated to your configuration class.
#Configuration
#Validated
public class MyConfig {
#Bean
MyClass createSslContext(
#Value("#{systemProperties['javax.net.ssl.trustStore']}")
#NotBlank(message = "Truststore is needed") final
String path) {
...
}
}

How to validate #PathVariable with custom validator annotation containing repository bean

I know how to validate #PathVariable from https://stackoverflow.com/a/35404423/4800811
and it worked as expected with standard annotations but not with the customized one using a Repository bean. Maybe the bean is not initialized and I end up with NullPointerException when accessing the end point has #PathVariable validated. So how to get that work?
My Controller:
#RestController
#Validated
public class CustomerGroupController {
#PutMapping(value = "/deactive/{id}")
public HttpEntity<UpdateResult> deactive(#PathVariable #CustomerGroupEmpty String id) {
}
}
My custom validator:
public class CustomerGroupEmptyValidator implements ConstraintValidator<CustomerGroupEmpty, String>{
#Autowired
private CustomerRepository repository;
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// NullPointerException here (repository == null)
if (value!=null && !repository.existsByCustomerGroup(value)) {
return false;
}
return true;
}
}
My Custom Annotation:
#Documented
#Constraint(validatedBy = CustomerGroupEmptyValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomerGroupEmpty {
String message() default "The customer group is not empty.";
Class<?>[] groups() default {};
Class<? extends Payload> [] payload() default {};
}
code in this post is correct, only mistake is that validator need to override initialize method as well. Probably user123 incorrect configure repository bean, the simply way to check this is define it manually in configuration class

Custom Spring (Boot) annotation: #ConditionalOnProperty with default key name

How to create a custom Spring (Boot) annotation #Country that "inherits" #ConditionalOnProperty, but predefines the property key?
Given a some services that share a common interface
interface WizardService {
void doMagic()
}
and a set of country specific implementations that are selected via #ConditionalOnProperty(name = "country", havingValue = "[iso-code]"), i.e. the implementation is selected based on the value of the country property
#ConditionalOnProperty(name = "country", havingValue = "de")
#Service
class WizardServiceGermany {
#Override void doMagic() { System.out.println("Simsalabim!"); }
}
#ConditionalOnProperty(name = "country", havingValue = "en")
#Service
class WizardServiceGreatBritain {
#Override void doMagic() { System.out.println("Wingardium Leviosa!"); }
}
is it possible to define a custom spring annotation which always sets the name property to "country" by default so that I can have an #Country annotation? For instance:
#Country("en")
#Service
class WizardServiceGreatBritain {
#Override void doMagic() { System.out.println("Wingardium Leviosa!"); }
}
I tried creating a meta-annotation, but it gets ignored (while replacing it with its #ConditionalOnProperty equivalent works fine):
#ConditionalOnProperty(name = "country")
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
public #interface Country {
#AliasFor(annotation = ConditionalOnProperty.class, attribute = "havingValue")
String havingValue() default "";
}

How to retrieve attribute from ControllerAdvice selector in ControllerAdvice class

I can define a Spring ControllerAdvice that is selectively used by a subset of controllers using a custom annotation:
#RestController
#UseAdviceA
#RequestMapping("/myapi")
class ApiController {
...
}
#ControllerAdvice(annotations = UseAdviceA.class)
class AdviceA {
...
}
But is it possible to pass in an attribute via the custom annotation where the advice class can pick up from the annotation? For e.g.:
#RestController
#UseAdviceA("my.value")
#RequestMapping("/myapi")
class ApiController {
...
}
#ControllerAdvice(annotations = UseAdviceA.class)
class AdviceA {
// Some way to get the string "myvalue" from the instance of UseAdviceA
...
}
Any other way to achieve the same outcome, which is to be able to define a custom configuration at the Controller method which can be passed to the ControllerAdvice would be much appreciated too.
Here is a solution.
Given
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface UseAdviceA {
public String myValue();
}
Controller
#RestController
#UseAdviceA(myValue = "ApiController")
#RequestMapping("/myapi")
class ApiController {
...
}
Your Controller Advice should be like
#ControllerAdvice(annotations = {UseAdviceA.class})
class AdviceA {
#ExceptionHandler({SomeException.class})
public ResponseEntity<String> handleSomeException(SomeException pe, HandlerMethod handlerMethod) {
String value = handlerMethod.getMethod().getDeclaringClass().getAnnotation(UseAdviceA.class).myValue();
//value will be ApiController
return new ResponseEntity<>("SomeString", HttpStatus.BAD_REQUEST);
}

Categories

Resources