Request Attribute not found after Spring Security added to Spring Boot app - spring

I have a Spring Boot application that is running. As soon as I added Spring Security, the app generated an error.
I have a form that is backed by a bean. When I enable Spring Security, the bean for the form cannot be found. Before I added Spring Security, the bean and form worked.
The error that I receive after making a GET request to the form is
Neither BindingResult nor plain target object for bean name 'orderActive' available as request attribute
The form is using the ThymeLeaf package.
Spring Security Configuration
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("buzz")
.password("{noop}infinity")
.authorities("ROLE_USER");
}
}
Controller Method
#GetMapping("/orders/current")
public String orderForm() {
return "orderForm";
}
Test Class Annotations
#SpringBootTest
#AutoConfigureMockMvc
class DesignTacoControllerTest {
Test Method
#WithMockUser("buzz")
#Test
public void testProcessDesignGet() throws Exception {
mockMvc.perform(get("/orders/current")
.requestAttr("orderActive", new Order()))
.andExpect(status().isOk());
}
orderForm
<form method="POST" th:action="#{/orders}" th:object="${orderActive}">
I have tried adding a RequestAttribute to the controller method.
#GetMapping("/orders/current")
public String orderForm(#RequestAttribute("orderActive") Order orderActive) {
return "orderForm";
}
When I debug, the order has the same ID as the one that was added in the test method. The next step is to render the view. When I continue, the error appears.
Somewhere between the controller method and the view, the request parameter disappears. It has something to do with security, since the code runs without security enabled. The order form is found, so the page is not forbidden. Does security disable the request attributes?

You say it worked before Security, but, do you have a class (DTO) OrderForm with the fields you need in your form? I don't see one. If you don't create one and then add it to the model (that's the Binding part):
#GetMapping("/orders/current")
public String orderForm(Model model) {
model.addAttribute("orderForm", new OrderForm())
return "orderForm";
}

Related

Spring Boot role validation controller using aspect

I have several controller functions separated by role, and instead of doing role validation in each controller method, I found that it seems to be able to get done by using Aspect, however something isn't right in my implementation as the code in Aspect never runs
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ForMerchantOnly {}
Aspect:
#Aspect
#Configuration
public class ForMerchantOnlyAspect {
private static final Logger logger = LogManager.getLogger(ForMerchantOnlyAspect.class);
#Before("#annotation(com.example.api.annotation.ForMerchantOnly) && args(request)")
public void before(HttpServletRequest request) throws ServiceException {
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("request should be HttpServletRequesttype");
}
String domain = request.getServerName();
System.out.println("Aspect showing domain " + domain);
// -- other code
}
}
Controller
#ForMerchantOnly
#GetMapping("/list")
public ResponseEntity<ApiResp> list() {
System.out.println("Show something");
return ResponseEntity.ok().body();
}
I'm assuming when i call controller /list method via chrome browser, it would hit the code in ForMerchantOnlyAspect but it just went into the controller method directly. Am I missing something?
The Aspect was not working as it could not find a matching joinpoint . There are no controller methods that has annotation #ForMerchantOnly and has an argument of type HttpServletRequest
From the documentation :
args: Limits matching to join points (the execution of methods when
using Spring AOP) where the arguments are instances of the given
types.
Following aspect may be used for the requirement . Scoping designator within will set the scope to advice.
#Before("#annotation(com.example.api.annotation.ForMerchantOnly) && within(com.example.api..*)")
public void before() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
System.out.println("Aspect showing domain " + request.getServerName());
}
Also note that an Aspect is better annotated with #Component and #Configuration be used for configurations.
You may also have a look at Method Security of Spring security framework , which lets to secure a method with annotations.
From the documentation
From version 2.0 onwards Spring Security has improved support
substantially for adding security to your service layer methods. It
provides support for JSR-250 annotation security as well as the
framework’s original #Secured annotation. From 3.0 you can also make
use of new expression-based annotations. You can apply security to a
single bean, using the intercept-methods element to decorate the bean
declaration, or you can secure multiple beans across the entire
service layer using the AspectJ style pointcuts.

How to register validator for Spring Data Rest?

There are two ways to register a Validator instance in Spring Data REST: wire it by bean name or register the validator manually. For the
majority of cases, the simple bean name prefix style is sufficient.
In order to tell Spring Data REST you want a particular Validator
assigned to a particular event, prefix the bean name with the event in
question. For example, to validate instances of the Person class
before new ones are saved into the repository, you would declare an
instance of a Validator in your ApplicationContext with a bean
name of beforeCreatePersonValidator. Since the beforeCreate prefix
matches a known Spring Data REST event, that validator is wired to the
correct event.
I tried creating this bean but it doesn't register
#Component("beforeCreateOrderValidator")
public class BeforeCreateOrderValidator extends BaseValidator {
#Override
public boolean supports(Class<?> clazz) {
return Order.class.equals(clazz);
}
#Override
public void validate(Object obj, Errors errors) {
}
}
But if I do manual registration it works
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener v) {
v.addValidator("beforeCreate", new BeforeCreateOrderValidator());
}

Using #Valid annotation on a Spring Component method

I'm trying to build a small POC to check if we can use Spring Validation on our projects (REST Endpoints). The goal is to use the #Valid annotation on some Component's methods, annotate arguments with JSR-303 annotations and build some Validator instances for custom validation logic.
Consider the following scenario:
Account (Getters and Setters ommited)
public class Account {
private int id;
#Min(0) private double amount;
#NonNull private String cardholder;
}
AccountController
#RestController
#RequestMapping("/account")
public class AccountController {
#Autowired private AccountService service;
#RequestMapping(method= RequestMethod.POST)
public void post(#RequestBody Account account) {
service.save(account);
}
}
AccountService
#Component
public class AccountService {
public void save(**#Valid** Account account) {
// Logic ommited
log.info("Account saved!");
}
}
AccountSaveValidator
public class AccountSaveValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) { return Account.class.isAssignableFrom(clazz); }
#Override
public void validate(Object target, Errors errors) {
Account account = (Account) target;
if (**if account does not exist**)
errors.rejectValue("id", "account.not-exists");
}
}
Whenever I POST to /account, the mentioned validations do not run, and the Account saved! message is displayed regardless. However, if I put the #Valid annotation on the AccountController's POST handler instead, the validations are executed.
I was only able to execute only the custom validation (AccountSaveValidator) manually by calling it on the save() method like this:
ValidationUtils.invokeValidator(new AccountSaveValidator(), account, errors);
if (errors.hasErrors()) {
throw new ValidationException(errors);
}
What am I missing here? I've read that these validation components are normally used along with Spring-MVC, but that it could be used without it.
The gradle dependencies I have are the following:
compile "org.springframework:spring-core"
compile "org.springframework:spring-context"
compile "org.springframework:spring-web"
compile "org.springframework.boot:spring-boot"
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-autoconfigure"
compile "javax.validation:validation-api:1.1.0.Final"
compile "org.hibernate:hibernate-validator:5.2.4.Final"
A couple things, I believe that your title on the question here is a bit misrepresentitive of what you are actually asking here. You are not trying to validate a spring Component. You wish to do method parameter validation within a Spring Component, which is different. I believe that your question here is a duplicate of this question: JSR 303. Validate method parameter and throw exception. There are examples there of how to do what you want to do using proxies and a MethodValidationInterceptor.
I will add some additional information here to try to clarify the differences in where JSR-303 validation works and why.
Spring MVC Parameters: Parameters passed into Spring MVC Components are resolved using a combination of user defined and default HandlerParameterResolvers. Part of Spring's default MVC configuration includes hooks for automatic mappings via #RequestParam and #PathVariable to raw types and #RequestBody to Objects all via the afore mentioned HandlerParameterResolver. In the same way that these HandlerParameterResolvers are automatically configured in Spring (mostly by default) there are also Validators that are registered to the DataBinders, which map the data from the request to the params above, JSR-303 validation is automatically configured to tie into these hooks. This of course is a simplified summary of what is going on behind the scenes.
Spring Components/Beans: Spring Beans and Components are validated by a Spring Bean Validator. You can find details on this here: http://docs.spring.io/autorepo/docs/spring/3.2.x/spring-framework-reference/html/validation.html as described in the section called "7.8.2 Configuring a Bean Validation Implementation"

Add interceptor to spring boot mongodb rest example

I am trying to add an interceptor to a simple Spring-boot-mongodb-rest app, as can be seen here : http://spring.io/guides/gs/accessing-mongodb-data-rest/, in order to perform certain actions after the default rest handler is invoked. Here is my MongoRepository, whose CRUD operation is called upon a POST request to the server:
#RepositoryRestResource(collectionResourceRel = "reminder", path = "reminder")
public interface ReminderRepository extends MongoRepository<Reminder, String> {
List<Reminder> findBySendee(#Param("sendee") String sendee);
}
I am trying to register an interceptor for all HTTP requests by extending the WebMvcConfigurerAdapter class like this:
#Configuration
#ComponentScan
public class RemindxWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new RemindxInterceptor());
}
}
As mentioned in the spring boot docs, I have not added the #EnableWebMvc annotation to this. While running the application, the addInterceptors function does get called and adds the interceptor. However, the given interceptor is not called after the POST handler is invoked. I am unable to figure out a way to have spring use this RemindxWebConfig for all MongoRepository http requests. Any inputs are appreciated.

Custom default headers for REST API only using Spring Data REST

I have a use case where my application hosts REST API and web application and we need to add custom header to REST APIs only. REST APIs are enabled through Spring Data REST. Typically we could use Servlet Filter to achieve this but we need code the logic of isolating requests to our REST API and add the custom headers. It would be nice if Spring Data REST API allows to add a default header to all the responses it generates. What are your thoughts? Don't say I am lazy :)
For folks looking for actual implementation details..
Interceptor
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("adding CORS headers.....");
response.addHeader("HEADER-NAME", "HEADER-VALUE");
return true;
}
}
Java Configuration
#Configuration
public class RepositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new CustomInterceptor() });
return mapping;
}
}
As Spring Data REST is built on top of Spring MVC, the easiest way is to configure a custom HandlerInterceptor as described in the reference documentation.
With Spring Data REST the easiest way is to extend RepositoryRestMvcConfiguration and override repositoryExporterHandlerMapping, call the parent method and then invoke ….setInterceptors(…) on it.
Finally I managed to make the setup of custom interceptor working also on spring-data-rest 2.4.1.RELEASE.
#Configuration
public class RestMvcConfig extends RepositoryRestMvcConfiguration {
#Autowired UserInterceptor userInterceptor;
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping() {
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings(), config());
repositoryMapping.setInterceptors(new Object[] { userInterceptor }); // FIXME: not nice way of defining interceptors
repositoryMapping.setJpaHelper(jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
I had to override the restHandlerMapping method, copy-paste it's content and add a line repositoryMapping.setInterceptors for adding custom interceptor, in my case the UserInterceptor.
Is there any better way?

Resources