I'm trying to create a generic controller using spring mvc 3.2.3 and spring security 3.1.3.
What i'm trying to achieve is something like this:
public abstract class DataController<E extends PersistentEntity> {
protected abstract E getEntity(String id);
#RequestMapping(value="/view/{id}", method=RequestMethod.GET)
public String view(#PathVariable("id") String id, ModelMap map) {
E ent = getEntity(id);
map.put("entity", entity);
return "showEntity";
}
}
My extended class will have a specific controller mapping in the class name so that i can access the url by using the controller name:
#Controller
#RequestMapping("/company**")
#Secured("ROLE_ADMIN")
public class CompaniesController extends DataController<Company> {
#Autowired
private AppService appService;
#Override
protected Company getEntity(String id) {
return appService.getCompany(id);
}
}
My problem is that the url /company/view is not secured by ROLE_ADMIN and can be accessed by anyone, (i think) because the /view is not defined in the controller where the #Secured is being used.
This can be fixed by just overriding the view method and define the mapping in my company class:
. . .
#Override
#RequestMapping(value = "/view/{id}", method = RequestMethod.GET)
public String view(String id, ModelMap map) {
return super.view(id, map);
}
. . .
In this case the security works correctly, but i want to know if there is another method. Since i have a lot of methods in my abstract class, this will create a problem and a mess to override all methods just to call the super.
Is there a way to fix this issue?
thanks all for the help :)
I know it's a year later, but I had the same problem and figured out a possible solution for this. It is not 100% annotation based, but works and is somewhat elegant
The abstract superclass:
#PreAuthorize("hasAnyRole(this.roles)")
public abstract class DataController<E extends PersistentEntity>
{
protected abstract E getEntity(String id);
protected abstract String[] getRoles();
#RequestMapping(value="/view/{id}", method=RequestMethod.GET)
public String view(#PathVariable("id") String id, ModelMap map) {
E ent = getEntity(id);
map.put("entity", entity);
return "showEntity";
}
}
On the subclass you simply implement getRoles() to return an array of roles that are required to access this class.
#PreAuthorize is another way to check authentication, that allows you to use SpEL expression. this.roles refers to he getRoles() property on the annotated object.
Related
I am using Spring Boot (1.5.3) to create a Spring REST Web Service. I have added spring-boot-starter-web as the only dependency (as per spring guide). Next I have created UserManagementService interface for my service class.
#RequestMapping("/usermanagement/v1")
public interface UserManagementService {
#RequestMapping(value = "/user/{id}/", method=RequestMethod.GET)
public UserTo getUserById(#PathVariable("id") long id);
#RequestMapping(value = "/users/", method=RequestMethod.GET)
public List<UserTo> getAllUsers();
}
And its implementation UserManagementServiceImpl
#RestController
public class UserManagementServiceImpl implements UserManagementService {
private Map<Integer, UserTo> users;
public UserManagementServiceImpl() {
users = new HashMap<>();
users.put(1, new UserTo(1, "Smantha Barnes"));
users.put(2, new UserTo(2, "Adam Bukowski"));
users.put(3, new UserTo(3, "Meera Nair"));
}
public UserTo getUserById(long id) {
return users.get(id);
}
public List<UserTo> getAllUsers() {
List<UserTo> usersList = new ArrayList<UserTo>(users.values());
return usersList;
}
}
I wanted to created a REST Web Service using Spring Boot with minimum configuration and thought this would work. But on accessing my the Web Service I am getting No Response. What I am missing?
Also, I have seen many projects where annotations are added to the interface rather than the implementation class. Which I think is better than annotating classes. It should work here, right?
As mentioned in the comments, not all annotations are supported on interfaces. The #PathVariable annotation for example won't work, so you'll have to put that on the implementation itself:
public UserTo getUserById(#PathVariable("id") long id) {
return users.get(id);
}
Additionally to that, you have a Map<Integer, UserTo>, but you're retrieving the users using a #PathVariable of type long. This won't work either, so either change the key of users to Long or the id parameter to int:
public UserTo getUserById(#PathVariable("id") int id) {
return users.get(id);
}
The reason for this is that 1L (long) is not the same as 1 (int). So retrieving a map entry wouldn't return any result for a long value.
I have a use case where I need to limit the values that can be passed as the query param.
#Path("/foo")
public interface Foo {
#GET
#Path("/details/id/{id}")
void getFooDetails(#PathParam("id") String id, #QueryParam("sort") String sortDirection);
}
public class FooImpl {
public void getFooDetails(String id, String sortDir) {
//Implementation
}
}
In the above example, I want to restrict the value of query param sort that can be passed via the API to ASC, DESC.
Is there any existing CXF annotation which I can use to restrict the values on a parameter? I haven't found any and so I tried the following solution.
My Approach:
#Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface ValueSet {
String[] allowedValues();
}
The modified interface looks like this.
#Path("/foo")
public interface Foo {
#GET
#PathParam("/details/id/{id}")
void getFooDetails(#PathParam("id") String id, #QueryParam("sort") #ValueSet(allowedValues = {"ASC", "DESC"}) String sortDirection);
}
I wrote a CXF Interceptor which intercepts the API invocation. I used reflection to get a handle on FooImpl.getFooDetails params. But the problem I faced is that the interceptor looks at FooImpl.getFooDetails method and doesn't find the annotations #QueryParam on the method params since #QueryParam is on the base method and the annotation is not inherited.
Interceptor implementation:
#Provider
public class ParamValidationInterceptor extends AbstractPhaseInterceptor<Message> {
public ParamValidationInterceptor() {
super(Phase.PRE_INVOKE);
super.addBefore(someInterceptor);
}
#Override
public void handleMessage(Message message) throws Fault {
UriInfo uriInfo = new UriInfoImpl(message);
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
Method methodToInvoke = (Method) message.get("org.apache.cxf.resource.method");
Parameter[] parameters = methodToInvoke.getParameters();
for (Parameter parameter : parameters) {
if (parameter.isAnnotationPresent(ValueSet.class)) {
ValueSet valueSet = parameter.getAnnotation(ValueSet.class);
QueryParam queryParam = parameter.getAnnotation(QueryParam.class);
Object invokedVal = queryParams.get(queryParam.value());
String[] allowedValues = valueSet.allowedValues();
if (!Arrays.asList(allowedValues).contains(invokedVal)) {
throw new CustomException();
}
}
}
}
}
Can anyone suggest a way forward? It would be great if anyone can suggest an alternative approach.
P.S: I am using CXF as an implementation for JAX-RS and spring is used as a container.
Update:
Like #Cássio Mazzochi Molin and #Andy McCright suggested, I will go with #Pattern annotation. But I am curious to know why the JAX-RS annotations are not inherited from the interface although the spec says they will be inherited.
Annotation inheritance
According to the section §3.6 Annotation Inheritance of the JAX-RS specification, it is recommended to always repeat annotations instead of relying on annotation inheritance.
Refer to this answer for the complete quote.
#QueryParam can be applied to different targets
Bear in mind that the #QueryParam annotation can be applied to:
Resource method parameters
Resource class fields
Resource class bean properties
Hence a manual validation can be tricky.
Use Bean Validation
For validation purposes, you should consider Bean Validation. Consider a #Pattern annotation with the allowed values:
#Pattern(regexp = "ASC|DESC")
And just annotate your resource method parameter:
#GET
#Path("foo")
public Response getFoos(#QueryParam("sort")
#Pattern(regexp = "ASC|DESC") String sortDirection) {
...
}
If you prefer case insensitive values, use:
#Pattern(regexp = "ASC|DESC", flags = Pattern.Flag.CASE_INSENSITIVE)
If the given value is invalid, a ConstraintViolationException will be thrown. To handle such exception and return a customized response, you can use an ExceptionMapper:
#Provider
public class ConstraintViolationExceptionMapper
implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
...
}
}
Perhaps it is just a typo, but CXF may not be recognizing the getFooDetails method (on the interface) because it is annotated with #PathParam instead of #Path.
Instead of using your ValueSet approach, I used BeanValidation, but the following code works for me.
Foo.java
#Path("/foo")
public interface Foo {
#GET
#Path("/details/id/{id}")
Response getFooDetails(
#PathParam("id") #Pattern(regexp="[0-9]*") String id,
#QueryParam("sort") #Pattern(regexp = "ASC|DESC") String sortDirection);
}
FooImpl.java
public class FooImpl implements Foo {
#Override
public Response getFooDetails(String id, String sortDirection) {
Integer idInt = Integer.parseInt(id);
if ("ASC".equals(sortDirection) || sortDirection == null) {
...
} else if ("DESC".equals(sortDirection)) {
...
}
return ...;
}
I've got this working on WebSphere Liberty 17.0.0.2 which is based on CXF 3.1.11.
Hope this helps,
Andy
I know there are validators in spring. However, these validators can only be bound to a single object. Say a Pojo in request body. However, I have a scenario where I have a get request and I want to validate a date range: I have a start date and the end date as #requestparams. How should I validate these?
Also there is a validator applied for the same #restcontroller: for post request, say Employeevalidtor. Can I invoke multiple validators for different objects in the same #restcontroller?
You can use separate validators but they have to me manually instantiated by passing the corresponding objects to be validated.
I assume you are talking about request binding validations. The same validations can be obtained with Spring Validators for #RequestParam and #PathVariables as mentioned in this post
Adding the relevant piece here. The controller will look something like this:
#RestController
#Validated
public class RegistrationController {
#RequestMapping(method = RequestMethod.GET,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.OK)
public Map search(#Email #RequestParam("email") String email) {
return emailMessage(email);
}
}
Note the #Validated method at the class level (which can also be declared at the method level).
Let Spring MVC will map your request parameters to a pojo encapsulating all the related inputs and then add a validator for that.
#RestController
#RequestMapping("/myUrl")
public class MytController {
private final MyIntervalValidator validator;
#InitBinder
public void initBinder(WebDataBinder binder){
binder.setValidator(validator);
}
#GetMapping
public void doSomthing(#Valid #RequestParam MyInterval interval){...}
class MyInterval implements Serializable{
private Date startDate;
private Date endDate;
}
import org.springframework.validation.Validator;
class MyIntervalValidator implements Validator{
#Override
public boolean supports(Class<?> clazz) {
return MyInterval.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
final MyInterval params = (MyInterval) target;
....
}
}
Background
I'm creating RESTful services using Spring MVC. Currently, I have the following structure for a controller:
#RestController
#RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")
public class MyEntityController {
#RequestMapping(path={ "", "/"} , method=RequestMethod.POST)
public ResponseEntity<MyEntity> createMyEntity(
#RequestBody MyEntity myEntity,
#RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT)
public ResponseEntity<MyEntity> updateMyEntity(
#PathVariable Long id,
#RequestBody MyEntity myEntity,
#RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH)
public ResponseEntity<MyEntity> partialUpdateMyEntity(
#PathVariable Long id,
#RequestBody MyEntity myEntity,
#RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
}
As you can see, all these three methods receive the same parameter for the header #RequestHeader("X-Client-Name") String clientName and applies it in the same way on each method: myEntity.setClientName(clientName). I will create similar controllers and for POST, PUT and PATCH operations will contain almost the same code but for other entities. Currently, most entities are designed to support this field vía a super class:
public class Entity {
protected String clientName;
//getters and setters ...
}
public class MyEntity extends Entity {
//...
}
Also, I use an interceptor to verify that the header is set for requests.
Question
How can I avoid repeating the same code through controller classes and methods? Is there a clean way to achieve it? Or should I declare the variable and repeat those lines everywhere?
This question was also asked in the Spanish community. Here's the link.
My suggestion is to store the header value in the request scoped bean inside the Spring interceptor or filter. Then you may autowire this bean wherever you want - service or controller and use the stored client name value.
Code example:
public class ClientRequestInterceptor extends HandlerInterceptorAdapter {
private Entity clientEntity;
public ClientRequestInterceptor(Entity clientEntity) {
this.clientEntity = clientEntity;
}
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String clientName = request.getHeader("X-Client-Name");
clientEntity.setClientName(clientName);
return true;
}
}
In your configuration file:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(clientRequestInterceptor());
}
#Bean(name="clientEntity")
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Entity clientEntity() {
return new Entity();
}
#Bean
public ClientRequestInterceptor clientRequestInterceptor() {
return new ClientRequestInterceptor(clientEntity());
}
}
Then, lets assume we have to use this bean in our controller:
#RestController
#RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")
public class MyEntityController {
#Autowired
private Entity clientEntity; // here you have the filled bean
#RequestMapping(path={ "", "/"} , method=RequestMethod.POST)
public ResponseEntity<MyEntity> createMyEntity(#RequestBody MyEntity myEntity) {
myEntity.setClientName(clientEntity.getClientName());
//rest of method declaration...
}
// rest of your class methods, without #RequestHeader parameters
}
I have not compiled this code, so correct me if I made some mistakes.
I've got an interesting answer in the Spanish site (where I also posted this question) and based on that answer I could generate mine that adapts to this need. Here's my answer on SOes.
Based on #PaulVargas's answer and an idea from #jasilva (use inheritance in controller) I though on a stronger solution for this case. The design consists of two parts:
Define a super class for controllers with this behavior. I call this class BaseController<E extends Entity> because Entity is the super class for almost al my entities (explained in the question). In this class I'll retrieve the value of #RequestBody E entity parameter and assign it into a #ModelAttribute parameter like #PaulVargas explains. Generics power helps a lot here.
My controllers will extend BaseController<ProperEntity> where ProperEntity is the proper entity class I need to handle with that controller. Then, in the methods, instead of injecting #RequestBody and #RequestHeader parameters, I'll only inject the the #ModelAttribute (if needed).
Aquí muestro el código para el diseño descrito:
//1.
public abstract class BaseController<E extends Entity> {
#ModelAttribute("entity")
public E populate(
#RequestBody(required=false) E myEntity,
#RequestHeader("X-Client-Name") String clientName) {
if (myEntity != null) {
myEntity.setCreatedBy(clientName);
}
return myEntity;
}
}
//2.
#RestController
#RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")
public class MyEntityController extends BaseController<MyEntity> {
#RequestMapping(path={ "", "/"} , method=RequestMethod.POST)
public ResponseEntity<MyEntity> createMyEntity(
#ModelAttribute("entity") MyEntity myEntity) {
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT)
public ResponseEntity<MyEntity> updateMyEntity(
#PathVariable Long id,
#ModelAttribute("entity") MyEntity myEntity) {
//rest of method declaration...
}
#RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH)
public ResponseEntity<MyEntity> partialUpdateMyEntity(
#PathVariable Long id,
#ModelAttribute("entity") MyEntity myEntity) {
//rest of method declaration...
}
}
In this way, I don't need to rewrite those lines of code in every method and controller, achieving what I've asked.
You could consider using RequestBodyAdvice. See the javadocs.
The HttpInputMessage object where you can access the http headers, is passed into the interface methods.
I am using Spring MVC with a controller like this:
#RequestMapping(value = "/list", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody List<Service> list() {
return services.list();
}
The model is like this:
public class Service {
private User user;
...
}
public class User {
private String name;
...
}
public class ExtendedUser extends User {
private Location location;
...
}
For sure, an object of type ExtendedUser is created in the application and set in Service. When the controller /list answer the request, an object of type ExtendedUser is serialized despite the reference in Service class is User. I would like to know if there is some way with annotations to only serialize supertype (the referenced type) and avoid the subtype propierties.
Taking the example into account, I want a JSON without the location property to be returned.
Thanks in advance
Try #JsonInclude(Include.NON_NULL) on ExtendedUser
Your statement of "I want a JSON without the location property to be returned" can easily be accomplished using the #JsonIngore annotation:
public class ExtendedUser extends User {
#JsonIgnore
private Location location;
...
}
Is that what you're trying to do, though, just eliminate the location from the response, or does the actual type returned (type id) matter? If I'm off base, please post your expected JSON result and your actual JSON result.
I think this will do the trick:
#JsonSerialize(using=User.class)
See this related answer: https://stackoverflow.com/a/13926740/1292605
I recommend to use as property of #JsonSerialize. BTW, #JsonSerialize can be declared on the fields so that it will not impact the common behavior of serialization of User or ExtendedUser.
public class Service {
#JsonSerialize(as = User.class)
private User user;
...
}