Setting Request scoped bean in spring mvc - spring

I want to capture some request specific data 'under the covers' for every request coming into my spring controllers and store this in a request scoped bean for use at a lower level in the app , like the DAO layer. I've tried using interceptors, but the request scoped bean is always null in that case. This is my latest attempt using RequestContextListener.
#Configuration
#WebListener
public class MyRequestListener extends RequestContextListener {
#Autowired
RequestContext requestContext;
#Override
public void requestInitialized(ServletRequestEvent sre) {
// requestContext.setHttpRequestId(((HttpServletRequest)sre.getServletRequest()).getSession().getId());
System.out.println(requestContext);
}
}
then in some config class I have this (RequestContext is a simple class with one setter/getter for a piece of String data)
#Bean
#RequestScope
public RequestContext requestContext() {
return new RequestContext();
}
Error in the requestInitialized method now is
"No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request."
What's the idiomatic way of achieving this seemingly simple and surely common requirement ?

Related

ScopeNotActiveException in spring boot rest api

I have one simple resource class and autowiring bean from BeanConfig class, which declared one bean.
Whenever request is hit on API, then i am getting an exception.
Code:
#Component
public class BeanConfig {
#Autowired
Environment environment;
#Bean
#RequestScope
public Map<String, String> eventAttributes() {
return new HashMap<>();
}
}
public class SampleResource {
#Autowired
SampleServiceImpl sampleService;
#Autowired
private Map<String, String> eventAttributes; }
Following error:
org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.eventAttributes': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request

Spring authorization at package level; AOP?

we have a spring boot app with a java package that has spring controllers with endpoints for admin-like functionality. right now they all start with the same request mapping.
so one way i could do authorization of every endpoint in the package is by WebSecurityConfigurerAdapter implementation...
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
but i was thinking it would be nice to use AOP somehow to target the package of the admin controllers to PreAuthorize all controller methods in the package. just in case someone on the project creates a new controller in the proejct with a different request mapping it would automatically be projected. also, if we decided to PreAuthorize at the #Service level instead of the controller level then this way could be used as well.
so, is it possible to PreAuthorize at the package level with AOP or some other way?
Spring provides default AOP interceptor for #Secured and #PreAuthorized annotations, but it works only on class or method level, and AFAIK not intended to be expanded to package level.
To put it simply, Spring intercepts calls to certain methods or all class methods with these annotations and check whether SecurityContextHolder holds Authority object and whether its collection of GrantedAuthority matches any of the annotation's value field values. So, you can do the same thing using AOP, for example like this:
#Aspect
#Component
public class AdminServiceAOPAuthorization {
private static final List<String> ALLOWED_ROLES = List.of("ADMIN", "SUPER_ADMIN");
#Pointcut("within(com.example.service.admin.*)") // <- any method in any class of the package
public void adminServiceLayer() {}
#Before("adminServiceLayer()")
public void authorize(JoinPoint jp) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
// throw some exception depending on the logic
}
boolean authorized = authentication.getAuthorities().stream()
.anyMatch(ga -> ALLOWED_ROLES.contains(ga.getAuthority()));
if (!authorized) {
throw new AccessDeniedException("Access denied");
// log or whatever
}
}
}
For better performance I'd advise to use it at the service layer to let Spring create proxy using implemented interface (if you use them, of course) instead of proxying controller class.

How to get the DispatcherServlet from spring WebApplicationContext?

How to get the DispatcherServlet instance from a spring WebApplicationContext object?
I want to perform a direct request to the the front controller servlet (avoiding the network interface) and check the response from a spring application, e.g. WebApplicationContext instance.
Notice, we are not able to use MockMvc to check the HTML rendered by JSP because: "if you use JSPs, you can verify the JSP page to which the request was forwarded, but no HTML is rendered"(according to MockMvc vs End-to-End Tests)
So maybe if we get the DispatcherServlet instance we could perform the request through its doDispatch method and check the content from response.
When you look at
DispatcherServlet.java, there is a constructor with WebApplicationContext as parameter, but the super constructor from FrameworkServlet.java does only hold the field itself, without any callbacks - so the given web application context will not know the dispatcher servlet.
So I don't think you can resolve the dispatcher servlet by the WebApplicationContext. Of course you could try to inject/autowire the DispatcherServlet - maybe this is also possible inside tests, but I am not sure.
I think the most easiest way to check for JSP outputs would be to test those parts with a real server as described at Testing with a running server . This should give you the complete output and web application behaviour inside your test without any workarounds.
Dispatcher Servlet : It acts as front controller for Spring based web applications. It provides a mechanism for request processing where actual work is performed by configurable, delegate components. It is inherited from HttpServlet, HttpServletBean and FrameworkServlet. It also implements ApplicationContextAware hence it is able to set ApplicationContext.
WebApplicationContext : It is an extension of a plain ApplicationContext. It is web aware ApplicationContext i.e it has Servlet Context information. When DispatcherServlet is loaded, it looks for the bean configuration file of WebApplicationContext and initializes it.
So you can set and get the instance of any ApplicationContext from DispatcherServlet but not vice-versa.
dispatcherServlet.getWebApplicationContext();
Although, you can get an instance of DispatcherServlet but I doubt it will serve your purpose as it's scope is protected.
#Autowired
DispatcherServlet dispatcherServlet;
Code inside DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
It is possible to create your own DispatcherServlet and perform actions. You can override the default one created by Spring boot app.
Sample:
public class SampleDispatcherServlet extends DispatcherServlet {
private final Log logger = LogFactory.getLog(getClass());
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// You can use other implementations of HttpServletRequest and
// HttpServletResponse as per your requirement
// e.g. FormContentRequestWrapper , ResourceUrlEncodingResponseWrapper many more
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
if (!(response instanceof ContentCachingResponseWrapper)) {
response = new ContentCachingResponseWrapper(response);
}
HandlerExecutionChain handler = getHandler(request);
try {
// Any task to be done
super.doDispatch(request, response);
} finally {
// Some task to be done
}
}
}
HandlerExecutionChain- Handler mapper is used to match current request to appropriated controller. Interceptors are the objects invoked before and after some of dispatching actions
Then we have to register the DispatcherServlet:
#Bean
public ServletRegistrationBean dispatcherRegistration() {
return new ServletRegistrationBean(dispatcherServlet());
}
//The bean name for a DispatcherServlet that will be mapped to the root URL "/"
#Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new SampleDispatcherServlet();
}

how override spring framework beans?

I want to customize some of the codes of OAuth authorization server provided by spring security. the code responsible for generating /oauth/authorize is a bean named AuthorizationEndpoint. in AuthorizationServerEndpointsConfiguration class the following code creates a bean of AuthorizationEndpoint class:
#Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(tokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
return authorizationEndpoint;
}
I want to override it by a new custom bean. I have created a class which extends AuthorizationEndpoint. for now I have pasted the same code inside this new class.
public class AuthorizationEndpointCustom extends AuthorizationEndpoint {
creating the bean:
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
AuthorizationServerEndpointsConfiguration asec;
#Bean
// #Order(value = Ordered.LOWEST_PRECEDENCE)
#Primary
public AuthorizationEndpoint authorizationEndpoint () {
AuthorizationEndpointCustom authorizationEndpoint = new AuthorizationEndpointCustom();
FrameworkEndpointHandlerMapping mapping = asec.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(asec.getEndpointsConfigurer().getExceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(asec.getEndpointsConfigurer().getTokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(asec.getEndpointsConfigurer().getAuthorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(asec.getEndpointsConfigurer().getOAuth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(asec.getEndpointsConfigurer().getOAuth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(asec.getEndpointsConfigurer().getUserApprovalHandler());
return authorizationEndpoint;
}
private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
String path = mapping.getPath(page);
if (path.contains(":")) {
return path;
}
return "forward:" + path;
}
when I try to create a bean of this new class I encounter the following error:
APPLICATION FAILED TO START
Description:
The bean 'authorizationEndpoint', defined in
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration,
could not be registered. A bean with that name has already been
defined in class path resource
[com/example/demo/AuthorizationServerConfig.class] and overriding is
disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
the error goes away by adding the suggested config to application.properties. but the new bean does not replace the framework bean. in another part of my code I accessed the AuthorizationEndpoint from applicationContext. I called the .getClass() of this object and it is the same bean from the framework:
"org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint"
how can I force spring to use my bean?
You need a Configuration class
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AppConfig {
#Bean
public AuthorizationEndpoint authorizationEndpoint() {
if(...) return new AuthorizationEndpoint();
else return new AuthorizationEndpointCustom();
}
}
I red an article about overriding beans and it seems so messy and unpredictable. read here
it's best to avoid doing so. The solution to disable framework bean lies in excluding the configuration class which creates it. but this means we have to implement the hole thing ourselves.
#SpringBootApplication(exclude=<AuthorizationServerEndpointsConfiguration>.class)
but the solution to overriding the framework endpoints is much easier.all we have to do is create a controller with mapping for /oauth/authorize
Customizing the UI Most of the Authorization Server endpoints are used
primarily by machines, but there are a couple of resource that need a
UI and those are the GET for /oauth/confirm_access and the HTML
response from /oauth/error. They are provided using whitelabel
implementations in the framework, so most real-world instances of the
Authorization Server will want to provide their own so they can
control the styling and content. All you need to do is provide a
Spring MVC controller with #RequestMappings for those endpoints, and
the framework defaults will take a lower priority in the dispatcher.
In the /oauth/confirm_access endpoint you can expect an
AuthorizationRequest bound to the session carrying all the data needed
to seek approval from the user (the default implementation is
WhitelabelApprovalEndpoint so look there for a starting point to
copy). You can grab all the data from that request and render it
however you like, and then all the user needs to do is POST back to
/oauth/authorize with information about approving or denying the
grant. The request parameters are passed directly to a
UserApprovalHandler in the AuthorizationEndpoint so you can interpret
the data more or less as you please. The default UserApprovalHandler
depends on whether or not you have supplied an ApprovalStore in your
AuthorizationServerEndpointsConfigurer (in which case it is an
ApprovalStoreUserApprovalHandler) or not (in which case it is a
TokenStoreUserApprovalHandler). The standard approval handlers accept
the following:
read more here.
there is another question related to this subject: read here

Spring Event and Scope request

I would like to use Spring Event to "speak" with my beans in my web application.
So, for example, my bean which fires event is like this:
#Controller
#Scope("request")
#KeepAlive
public class Controller extends InitializingBean, ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
public void test() {
applicationEventPublisher.publishEvent(new TestEvent(this));
}
}
And my listener event is like this:
#Component
#Scope("request")
#KeepAlive
public class Module implements ApplicationListener<TestEvent> {
#Override
public void onApplicationEvent(TestEvent event) {
}
}
The most important point is these beans are scope request because they need to be initialized at every time the page is called.
But in startup, I get this message:
Caused by: java.lang.IllegalStateException: No thread-bound request
found: Are you referring to request attributes outside of an actual
web request, or processing a request outside of the originally
receiving thread? If you are actually operating within a web request
and still receive this message, your code is probably running outside
of DispatcherServlet/DispatcherPortlet: In this case, use
RequestContextListener or RequestContextFilter to expose the current
request.
Like if Spring try to instantiate my Module bean in startup and as the bean is scope request, it can't do this (the context request is not instantiate)
If I delete event management, everything works fine.
So, my question is:
Is it possible to have event listener is scope request ? And how to do this ?
Thanks
Try to inject a scoped proxy in a Singleton ApplicationListener to handle the TestEvent.
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS, value="request")
public class TestEventHandler {
public void onTestEvent(TestEvent event)
// ...
}
}
public class TestEventApplicationListener implements ApplicationListener<TestEvent> {
#Autowired
private TestEventHandler handler;
#Override
public void onApplicationEvent(TestEvent event) {
handler.onTestEvent(event);
}
}

Resources