My project has Open session in view enabled and we cannot disable it at this point. We are using spring boot.
I have a method that does db read calls and rest calls. I do NOT need transactional boundary over this method. How do I disable the transaction in this method. I have tried using
#Transaction(propagation=Propagation.NEVER
and
#Transaction(propagation=Propagation.NOT_SUPPORTED)
But the transaction still seems to exist. I know this because there are some lazily loaded relationships that gets loaded if I access them with in this method.
public void doSomething() {
makeDbCall();
makeRestCall();
makeDbCallAgain();
}
The easiest and safest way that I can think of is to customize the OpenEntityManagerInViewInterceptor to just exclude the request that you do not want to apply OSIV on it. Something like :
public class MyOpenEntityManagerInViewInterceptor extends OpenEntityManagerInViewInterceptor {
private boolean isExcludeOsiv(WebRequest request){
//refer to https://stackoverflow.com/questions/51921635/how-to-get-requests-uri-from-webrequest-in-spring for getting the URL path from the WebRequest for checking
}
#Override
public void preHandle(WebRequest request) throws DataAccessException {
if(isExcludeOsiv(request)){
return ;
}
super.preHandle(requset);
}
#Override
public void afterCompletion(WebRequest request, #Nullable Exception ex) throws DataAccessException {
if(isExcludeOsiv(request)){
return ;
}
super.afterCompletion(requset,ex);
}
}
And use the following configuration to disable the default OSIV setting provided by spring-boot but use this customised OpenEntityManagerInViewInterceptor instead :
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addWebRequestInterceptor(new MyOpenEntityManagerInViewInterceptor());
}
}
Related
I'm developing a jar library and trying to inject an interceptor from external jar library to Application.
For example:
External Lib
MyExternalInterceptor.java
public class MyExternalInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Do something
}
}
I tried to using AOP in external libs but it's not working.
InterceptorAspect.java
#Around("execution(* org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.addInterceptors(..))")
public Object aspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// Tried to inject MyExternalInterceptor here
Object result = proceedingJoinPoint.proceed();
return result;
}
In Application using that lib:
Application
MyConfiguration.java
#Configuration
public MyConfiguration extends WebMvcConfigurationSupport {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SpringTestInterceptor()); // client's own interceptor
/* Add MyExternalInterceptor not explicitly but implicitly using AOP or other things */
}
}
Is there any way to inject an interceptor from external lib to App?
I know the question is very obscure (sorry for that), but could you give me any advice or hint to make it work?
Thank you for anyone who read my question :)
(I updated few more details for clarification)
Summary
Use WebMvcConfigurer in both Client and library side instead of WebMvcConfigurationSupport
AoP is not needed
I use WebMvcConfigurer instead of WebMvcConfigurationSupport and change some codes like below:
External Lib
MyExternalInterceptor.java
Same as before
InterfaceAspect.java
Don't needed it anymore
MyExternalLibConfiguration.java
#Configuration
public class MyExternalLibConfiguration implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyExternalInterceptor());
}
}
Application (client)
MyConfiguration.java
#Configuration
public MyConfiguration implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SpringTestInterceptor()); // client's own interceptor
/* No need to add MyExternalInterceptor in here */
}
}
That's all! Everything is working well as M. Deinum said in comment.
Thank you again Deinum!
Let's say I have the following controller. (Assume that Order.customer is the customer the order belongs to and only they should be able to access it.)
#RestController
#RequestMapping("/orders")
public class OrderController {
#GetMapping
#PostAuthorize("returnObject.customer == authentication.principal")
public Order getOrderById(long id) {
/* Look up the order and return it */
}
}
After looking up the order, #PostAuthorize is used to make sure it belongs to the authenticated customer. If it is not, Spring responds with a 403 Forbidden.
Such an implementation has a problem: Clients can distinguish between orders that do not exist and orders they have no access to. Ideally, 404 should be returned in both cases.
While this could be solved by injecting the Authentication into the handler method and implementing custom logic there, is there any way to achieve this using #PostAuthorize or a similar, declarative API?
You can specify a custom AccessDeniedHandler in your Spring Security configuration.
In the following example, the handler will return a 404 Not Found on an access denied failure.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler(accessDeniedHandler())
);
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
}
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.sendError(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.getReasonPhrase());
}
}
You could try a ControllerAdvice to catch and transform the AccessDeniedException, which PostAuthorize throws.
#RestControllerAdvice
public class ExceptionHandlerController {
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(AccessDeniedException.class)
public String handleAccessDenied(AccessDeniedException e) {
return "nothing here"; // or a proper object
}
}
I see that with Spring boot is really simple create filters. Just follow post like this one https://www.baeldung.com/spring-boot-add-filter
What I have not able to find, is how to create annotations that subscribe specifics endpoints in the controller to one filter.
Something like in Jax-RS it would looks like
#GET
#Path("jax-rs-single")
#Reactive(ttlRequest = 2000)
#Produces(MediaType.APPLICATION_JSON)
public Single getSingle() {
return Single.just("Hello world single");
}
Where #Reactive it would trigger the ReactiveFilter implementation per request.
I also saw the #WebFlow annotation, but it's not what I want. I want to create a library where the consumers decide which filter use, just adding the annotation in the controller.
Any idea how to do something similar with Spring boot/MVC ?
Regards
I will try to describe here more about Custom annotation and the processor in Spring.
I don't know what you want or what you need, but I will give an generic example.
You have 2 options:
BeanProcessor
HandlerInterceptor
BeanProcessor
You need to build 3 things basically: Annotaton, BeanProcessor and a Callback to execute your logic if annotated. Here is an example of it and how it works:
1 - Create the annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Documented
public #interface Reactive {
Integer ttlRequest;
}
2 - Implement a BeanPostProcessor
#Component
public class ReactiveAnnotationProcessor implements BeanPostProcessor {
private ConfigurableListableBeanFactory configurableBeanFactory;
#Autowired
public ReactiveAnnotationProcessor(ConfigurableListableBeanFactory beanFactory) {
this.configurableBeanFactory = beanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
this.scanReactiveAnnotation(bean, beanName);
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
protected void scanReactiveAnnotation(Object bean, String beanName) {
this.configureMethodInjection(bean);
}
private void configureMethodInjection(Object bean) {
Class<?> managedBeanClass = bean.getClass();
MethodCallback methodCallback =
new ReactiveMethodCallback(configurableBeanFactory, bean);
ReflectionUtils.doWithMethod(managedBeanClass, methodCallback);
}
}
3 - Create the method callback (here is the logic to execute)
public ReactiveMethodCallback implements MethodCallback {
private ConfigurableListableBeanFactory configurableBeanFactory;
private Object bean;
public ReactiveMethodCallback(ConfigurableListableBeanFactory bf, Object bean) {
configurableBeanFactory = bf;
this.bean = bean;
}
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if (!method.isAnnotationPresent(Reactive.class)){
return;
}
//YOUR LOGIC HERE
}
}
Here is a good source about annotation processing, it is about FieldProcessing but you can just change the interfaces to implement what you need if you have doubts: https://www.baeldung.com/spring-annotation-bean-pre-processor
[UPDATED] You can also create a HandlerInterceptor instead:
HandlerInterceptor
public class ReactiveFilterHandlerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Test if the controller-method is annotated with #CustomFilter
Reactive filter = handlerMethod.getMethod().getAnnotation(Reactive.class);
if (filter != null) {
// ... do the filtering, or call the Component for filtering
}
}
return true;
}
}
And register your handler:
#Configuration
public class WebMvcConfig extends WebMvcConfigurer {
#Autowired
ReactiveFilterHandlerInterceptor reactiveFilterHandlerInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(reactiveFilterHandlerInterceptor);
}
}
If I understand what you want correctly the main problem is how to apply filter based on custom annotation.
So first of all, yes you can use a regular Spring filter (WebFilter in case of Spring Webflux or Filter in case of Spring MVC), but you'll need to write some custom logic.
To do filtering based on annotation you should:
Use RequestMappingHandlerMapping#getHandlerInternal() method to retrieve a reference to the method that handles the request (the getSingle() in your case)
When you manage to retrieve the HandlerMethod then you can check if that method has your custom annotation applied with hasMethodAnnotation(Class<A> annotationType) method.
When you know that, then you can react accordingly: either chain.doFilter(request, response) without performing any actions, or apply your custom logic, and then trigger the rest of the filter chain.
I have a Spring boot application and I am implementing and interceptor in order to log some data.
The problem is that is not getting called, I have tried:
#Interceptor
public class LoggerInterceptor{
#AroundInvoke
public Object collectBasicLoggingInformation(InvocationContext context) throws Exception {
Logger logger = LoggerFactory.getLogger(context.getClass());
logger.info("Method Called: " + context.getMethod()
.getName());
logger.info("Parameters: " + Arrays.toString(context.getParameters()));
return context.proceed();
}
}
And then I've applied to methods or classes and in both of them doesn't work:
#GetMapping
#Interceptors(LoggerInterceptor.class)
public List getAllFilingNumber(){
logger.info("This is a test");
return filingNumberService.findAll();
}
Or
#RestController
#RequestMapping(FilingNumberController.BASE_URL)
#Interceptors(LoggerInterceptor.class)
public class FilingNumberController{
#GetMapping
public List getAllFilingNumber(){
logger.info("This is a test");
return filingNumberService.findAll();
}
}
Does someone knows what I am doing wrong?
Thanks
If you are having a springboot application in order to intercept the request to a controller , you have to take a different approach altogethor.
Interceptors are used in conjunction with Java EE managed classes to
allow developers to invoke interceptor methods on an associated target
class, in conjunction with method invocations or lifecycle events.
Common uses of interceptors are logging, auditing, and profiling.
Reference Doc
You are trying to use Java EE annotation with spring , which won't work.In spring-boot you will have to register the interceptors like :
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
}
}
The interceptor itself have to be a class which extends the HandlerInterceptorAdapter and implements the methods as follows.
From Spring DOCS :
All HandlerMapping implementations support handler interceptors that
are useful when you want to apply specific functionality to certain
requests — for example, checking for a principal. Interceptors must
implement HandlerInterceptor from the org.springframework.web.servlet
package with three methods that should provide enough flexibility to
do all kinds of pre-processing and post-processing:
preHandle(..): Before the actual handler is executed
postHandle(..): After the handler is executed
afterCompletion(..): After the complete request has finished
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
System.out.println("we are Intercepting the Request");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object object, ModelAndView model)
throws Exception {
System.out.println("request processing "
+ "completed by #RestController");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object object, Exception arg3)
throws Exception {
System.out.println("afterCompletion Request Completed");
}
}
I'm building a Spring Boot application to provide a stateless REST API. For security, we're using OAuth 2. My app receives a bearer-only token.
The user's information is stored in our database. I can look it up using the injected Principal in the controller:
#RequestMapping(...)
public void endpoint(Principal p) {
MyUser user = this.myUserRepository.findById(p.getName());
...
}
To avoid this extra line of boilerplate, I would like to be able to inject the MyUser object directly into my controller method. How can I achieve this? (The best I've come up with so far is to create a Lazy, Request-scoped #Bean...but I haven't been able to get it working...)
The Idiomatic Way
The idiomatic way in Spring Security is to use a UserDetailsService or implement your own:
public class MyUserDetailsService implements UserDetailsService {
#Autowired
MyUserRepository myUserRepository;
public UserDetails loadUserByUsername(String username) {
return this.myUserRepository.findById(username);
}
}
And then there are several spots in the Spring Security DSL where this can be deposited, depending on your needs.
Once integrated with the authentication method you are using (in this case OAuth 2.0), then you'd be able to do:
public void endpoint(#AuthenticationPrincipal MyUser myuser) {
}
The Quick, but Less-Flexible Way
It's generally better to do this at authentication time (when the Principal is being ascertained) instead of at method-resolution time (using an argument resolver) as it makes it possible to use it in more authentication scenarios.
That said, you could also use the #AuthenticationPrincipal argument resolver with any bean that you have registered, e.g.
public void endpoint(
#AuthenticationPrincipal(expression="#myBean.convert(#this)") MyUser user)
{
}
...
#Bean
public Converter<Principal, MyUser> myBean() {
return principal -> this.myUserRepository.findById(p.getName())
}
The tradeoff is that this conversion will be performed each time this method is invoked. Since your app is stateless, this might not be an issue (since the lookup needs to be performed on each request anyway), but it would mean that this controller could likely not be reused in other application profiles.
You can achieve this by implementing HandlerMethodArgumentResolver.
For example:
Custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface Version {
}
Implementation:
public class HeaderVersionArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterAnnotation(Version.class) != null;
}
#Override
public Object resolveArgument(
MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request
= (HttpServletRequest) nativeWebRequest.getNativeRequest();
return request.getHeader("Version");
}
}
When you implement this you should add this as argument resolver:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new HeaderVersionArgumentResolver());
}
}
Now we can use it as argument
public ResponseEntity findByVersion(#PathVariable Long id, #Version String version)