Spring: how to pass objects from filters to controllers - spring

I'm trying to add a Filter that creates an object that is then to be used inside a controller in a Spring Boot application.
The idea is to use the Filter as a "centralized" generator of this object - that is request-specific and useful only in a controller.
I've tried to use the HttpServletRequest request.getSession().setAttribute method: I can access my object in the controller, but then it will be (clearly) added to the session.
Are the Filters the right way to do so? If yes, where can I keep the temporary object generated by the filter to be used by the controllers?

Why Don't you use a Bean with the #Scope('request')
#Component
#Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
class UserInfo {
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private String password;
}
and then you can Autowireed this bean in both filter and controller to do setting and getting of data.
lifecycle of this UserInfo bean is only exisits within the request so once the http request is done then it terminates the instance as well

you can use ServletRequest.setAttribute(String name, Object o);
for example
#RestController
#EnableAutoConfiguration
public class App {
#RequestMapping("/")
public String index(HttpServletRequest httpServletRequest) {
return (String) httpServletRequest.getAttribute(MyFilter.passKey);
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Component
public static class MyFilter implements Filter {
public static String passKey = "passKey";
private static String passValue = "hello world";
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setAttribute(passKey, passValue);
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
}

An addition to wcong's answer.
Since Spring 4.3 after setting the attribute by using request.setAttribute(passKey, passValue);, you can access the attribute in your controller by simply annotating it with #RequestAttribute.
ex.
#RequestMapping("/")
public String index(#RequestAttribute passKey) {
return (String) passKey;
}

I dont know actually what is the scenario but If you really want to create an object in a filter and then use it somewhere in the code then you may use ThreadLocal class to do so.
To get know how this work see the most voted answer from that question Purpose of ThreadLocal?
In general using ThreadLocal you will be able to create a class that can store objects available ONLY for the current thread.
Sometimes for optimization reasons the same thread can be used to serve subsequent request as well so it will be nice to clean the threadLocal value after the request is processed.
class MyObjectStorage {
static private ThreadLocal threadLocal = new ThreadLocal<MyObject>();
static ThreadLocal<MyObject> getThreadLocal() {
return threadLocal;
}
}
in the filter
MyObjectStorage.getThreadLocal().set(myObject);
and in the Controller
MyObjectStorage.getThreadLocal().get();
Instead of filter you can use also #ControllerAdvice and pass objects to specified Controllers by using model.
#ControllerAdvice(assignableTypes={MyController.class})
class AddMyObjectAdvice {
// if you need request parameters
private #Inject HttpServletRequest request;
#ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("myObject", myObject);
}
}
#Controller
public class MyController{
#RequestMapping(value = "/anyMethod", method = RequestMethod.POST)
public String anyMethod(Model model) {
MyObjecte myObject = model.getAttribute("myObject");
return "result";
}
}

Related

Spring Boot - Store current user in global variable and initialise from API call when #service bean is created

I am creating a microservice architectured project with Zuul as gateway. I have all authentication handled in a service called common-service. I have exposed a API from common-service to return current logged in user. This is working fine.
Now, I have another microservice called inventory. In service class of inventory, I want to use current loggedin username in multiple methods. So, I am making a webclient call to common-service and getting current username. This is working fine but I am making a webclient API call to common service everytime I require username. Example - if I add a new entry, doing API call, then on update again API call etc. this seems not to be an optimised way
so problem is - I want to make this API call at global level. i.e. whenever my service bean is autowired, this API call should be made and username should be store somewhere which I can use across methods in my service call.
I tried #PostConstruct and #SessionAttributes but not able to get exact problem solved.
Can somebody help me with best suited solution or concept for handling this issue.
Below are code snippets
public class LeadService
{
#Autowired
WebClient.Builder webClientBuilder;
#Autowired
UserDetailsService userDetailsService;
//more autowiring
private void setLeadFields(Lead lead, #Valid LeadCreateData payload,String type)
{
//some logic
if(type.equalsIgnoreCase("create"))
{
lead.setAsigneeId(userDetailsService.getCurrentUser().getId());
lead.setCreatorId(userDetailsService.getCurrentUser().getId());
}
else if(type.equalsIgnoreCase("update"))
{
//some logic
}
}
private StatusEnum setLeadStatus(Lead lead, StatusEnum status,String string)
{
LeadStatus lstatus=null;
switch(string)
{
case "create":
lstatus = new LeadStatus(lead.getLeadId(),status,userDetailsService.getCurrentUser().getId(),userDetailsService.getCurrentUser().getId());
lsRepo.save(lstatus);
break;
case "udpate":
lstatus= lsRepo.FindLeadStatusByLeadID(lead.getLeadId()).get(0);
if(!lstatus.getStatus().equals(lstatus))
{
lstatus = new LeadStatus(lead.getLeadId(),status,userDetailsService.getCurrentUser().getId(),userDetailsService.getCurrentUser().getId());
lsRepo.save(lstatus);
}
break;
}
return lstatus.getStatus();
}
private Address setAddress(#Valid LeadCreateData payload,Address address)
{
//some setters
address.setCreator(userDetailsService.getCurrentUser().getId());
return aRepo.save(address);
}
As you can see, I am using userDetailsService.getCurrentUser().getId() in many places. I am getting this id from below autowired method. But my one API call is required everytime I need this id.
#Service
public class UserDetailsService
{
#Autowired
WebClient.Builder webClientBuilder;
#Autowired
HttpServletRequest request;
#Value("${common.serverurl}")
private String reqUrl;
public UserReturnData getCurrentUser()
{
UserReturnData userDetails = webClientBuilder.build()
.get()
.uri(reqUrl+"user/me")
.header("Authorization", request.getHeader("Authorization"))
.retrieve()
.bodyToMono(UserReturnData.class)
.block();
return userDetails;
}
}
I want a optimal way where I can call this API method to get current user only once. and I can use it throughout my #service class.
Create OncePerPrequestFilter or GenericFilterBean which has your UserDetailsService autowired.
And also you want to create something similar to RequestContextHolder or SecurityContextHolder which can hold your UserReturnData in a ThreadLocal variable. Look at those two spring classes to get idea but yours can be much simpler. Lets call it UserReturnDataContextHolder.
In the filter, you created in step1, when the request comes in populate it and when the response is leaving, clear it.
Now you can access it anywhere in the service via UserReturnDataContextHolder.getUserReturnData() and you are not making multiple calls either
Edit: The section below is contributed by Sridhar Patnaik as reference -
Below code to get it working
Added a class to store currentuserid
public class CurrentUser
{
private Long currentUserId;
//getter setter
}
Added a current user filter to intercept request and fetch current user.
public class CurrentUserFilter implements Filter
{
#Autowired
private CurrentUser currentUser;
#Autowired
UserDetailsService UserDetailsService;
#Override
public void init(FilterConfig arg0) throws ServletException {
// NOOP
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
try
{
this.currentUser.setCurrentUserId(UserDetailsService.getCurrentUser().getId());
chain.doFilter(servletRequest, servletResponse);
}
finally
{
this.currentUser.clear();
}
}
#Override
public void destroy() {
// NOOP
}
}
Added required AppConfig
#Configuration
public class AppConfig
{
#Bean
public Filter currentUserFilter() {
return new CurrentUserFilter();
}
#Bean
public FilterRegistrationBean tenantFilterRegistration() {
FilterRegistrationBean result = new FilterRegistrationBean();
result.setFilter(this.currentUserFilter());
result.setUrlPatterns(Lists.newArrayList("/*"));
result.setName("Tenant Store Filter");
result.setOrder(1);
return result;
}
#Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("tenantStore");
return result;
}
#Primary
#Bean(name = "proxiedThreadLocalTargetSource")
public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactoryBean result = new ProxyFactoryBean();
result.setTargetSource(threadLocalTargetSource);
return result;
}
#Bean(name = "tenantStore")
#Scope(scopeName = "prototype")
public CurrentUser tenantStore() {
return new CurrentUser();
}
}
And then autowired CurrentUser to my existing service class.
{..
#Autowired
CurrentUser currentUser;
...
private void setLeadFields(Lead lead, #Valid LeadCreateData payload,String type)
{
//some logic
if(type.equalsIgnoreCase("create"))
{
lead.setAsigneeId(currentUser.getCurrentUserId());
lead.setCreatorId(currentUser.getCurrentUserId());
lead.setAddress(setAddress(payload, new Address()));
}
else if(type.equalsIgnoreCase("update"))
{
lead.setAsigneeId(userDetailsService.getUserFromId(payload.getAssigneeId()).getId());
lead.setAddress(setAddress(payload,lead.getAddress()));
}
}

Get Request/Response Body&Header in Spring AOP

I want to get request/response body and header within my aspect before and after if it's available or how to get those .
I mean i think with before annotation should be work for request,
with after annotation should be work for response. Can be ?
What I've tried so far :
I tried logbook library it's very complicated for me i could'nt figured it out how to work with that.So i gave up.
The actuator can do trick but I am doing extra work like how many times the endpoints called etc.So therefore i can't use actuator.
Also i tried to get request headers like below at least but i think this headers coming same all the time.I couldn't get httpservletresponse like how httpservetrequest does.
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
then
request.getHeader("date") but what about requestbody ?
how to get requestbody ? responsebody ? repsonseheader ?
My aspect file :
#Aspect
#Component
public class AppAspect implements ResponseInfo{
#Before("execution(#(#org.springframework.web.bind.annotation.RequestMapping *) * *(..))")
public void loggingStartPointRequests(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
}
#After("execution(#(#org.springframework.web.bind.annotation.RequestMapping *) * *(..))")
public void loggingEndPointRequests(JoinPoint joinPoint) throws IOException {
}
}
My Controller Class:
#RestController
public class MainController {
#GetMapping("/people") //
public ResponseEntity<Poeple> getAllPeople(#RequestParam(name = "page", required = false) Integer page,
#RequestParam(name = "size", required = false) Integer size,
#RequestParam(name = "sortBy", required = false) Boolean sortByNameOrEpCount) {
doSomething();
}
}
I had the same problem and if you have your #Aspect annotated with #Component (or any #Autowired candidate) you can simply get the HttpServletRequest like this:
#Aspect
#Component
public class SomeAspect {
#Autowired
HttpServletRequest request;
#Before("...")
public void beforeAdvice(JoinPoint jp){
/* You will have the current request on the request property */
System.out.println(request.getRequestURL());
}
}
I know this is an old question but I hope it'll be helpful.
I think what you need is to implement the interface HandlerInterceptor, it would help you being able to inspect the request and the response. For example:
public class ApiMonitor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// when the client access to your endpoint
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
// when you finished your process
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// after you already returned an answer to the client
}
}
If you want to operate with the object that you're returning just before you send it to the client, then you need AOP, yes. That's an example of how I do it to modify an object on certain endpoints just before it's parsed to json.
#Component
#Aspect
public class MyCustomAOPInterceptor {
/**
* These poincuts check the execution of a method in any (*)
* class of my.package.controller and that start with
* get/list/find plus any other word (*) . For example
* my.package.controller.UserController.getUserById()
*/
#Pointcut("execution(* my.package.controller.*.get*(..))")
public void petitionsStartWithGet() { }
#Pointcut("execution(* my.package.controller.*.list*(..))")
public void petitionsStartWithList() { }
#Pointcut("execution(* my.package.controller.*.find*(..))")
public void petitionsStartWithFind() { }
#AfterReturning(pointcut = "petitionsStartWithGet() || petitionsStartWithList() || petitionsStartWithFind()", returning = "result")
public void translateEntities(JoinPoint joinPoint, Object result) {
// do your stuff; result is the object that you need
}
}

Spring boot - Pass argument from interceptor to method in controller

For learning purposes, I have made a custom authentication system where I pass a token from the client to the server through the Authorization header.
In the server side, I'd like to know if it's possible to create in the interceptor, before the request reaches a method in the controller, an User object with the email from the token as a property, and then pass this user object to every request where I require it.
This what I'd like to get, as an example:
#RestController
public class HelloController {
#RequestMapping("/")
public String index(final User user) {
return user.getEmail();
}
}
public class User {
private String email;
}
Where user is an object that I created in the pre-interceptor using the request Authorization header and then I can pass, or not, to any method in the RestController.
Is this possible?
#Recommended solution
I would create a #Bean with #Scope request which would hold the user and then put the appropriate entity into that holder and then take from that holder inside the method.
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class CurrentUser {
private User currentUser;
public User getCurrentUser() {
return currentUser;
}
public void setCurrentUser(User currentUser) {
this.currentUser = currentUser;
}
}
and then
#Component
public class MyInterceptor implements HandlerInterceptor {
private CurrentUser currentUser;
#Autowired
MyInterceptor(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
this.currentUser.setCurrentUser(new User("whatever"));
return true;
}
}
and in the Controller
#RestController
public class HelloController {
private CurrentUser currentUser;
#Autowired
HelloController(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#RequestMapping("/")
public String index() {
return currentUser.getCurrentUser().getEmail();
}
}
#Alternative solution
In case your object that you would like to have, only contains one field, you can just cheat on that and add that field to the HttpServletRequest parameters and just see the magic happen.
#Component
public class MyInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//TRY ONE AT THE TIME: email OR user
//BOTH SHOULD WORK BUT SEPARATELY OF COURSE
request.setAttribute("email", "login#domain.com");
request.setAttribute("user", new User("login#domain.com"));
return true;
}
}
You can use a local thread context object as follows - which will be handling one parameter per request thread (thread safe):
public abstract class LoggedUserContext {
private static ThreadLocal<User> currentLoggedUser = new ThreadLocal<>();
public static void setCurrentLoggedUser(User loggedUser) {
if (currentLoggedUser == null) {
currentLoggedUser = new ThreadLocal<>();
}
currentLoggedUser.set(loggedUser);
}
public static User getCurrentLoggedUser() {
return currentLoggedUser != null ? currentLoggedUser.get() : null;
}
public static void clear() {
if (currentLoggedUser != null) {
currentLoggedUser.remove();
}
}
}
Then in the interceptor prehandle function:
LoggedUserContext.setCurrentLoggedUser(loggedUser);
And in the interceptor postHandler function:
LoggedUserContext.clear();
From any other place:
User loggedUser = LoggedUserContext.getCurrentLoggedUser();

Create own class that transforms HTTP request to object in Spring?

I would like to create own class that will transform HTTP request and initializes object from this HTTP request in my Spring MVC application. I can create object by defining parameters in method but I need to do mapping in my own way and do it manually.
How can I do it with my own implementation that will pass to Spring and it will use it seamlessly?
Update1
Solution that kindly provided Bohuslav Burghardt doesn't work:
HTTP Status 500 - Request processing failed; nested exception is
java.lang.IllegalStateException: An Errors/BindingResult argument is
expected to be declared immediately after the model attribute, the
#RequestBody or the #RequestPart arguments to which they apply: public
java.lang.String
cz.deriva.derivis.api.oauth2.provider.controllers.OAuthController.authorize(api.oauth2.provider.domain.AuthorizationRequest,org.springframework.ui.Model,org.springframework.validation.BindingResult,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
Maybe I should mention that I use own validator:
public class RequestValidator {
public boolean supports(Class clazz) {
return AuthorizationRequest.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
AuthorizationRequest request = (AuthorizationRequest) obj;
if ("foobar".equals(request.getClientId())) {
e.reject("clientId", "nomatch");
}
}
}
and declaration of my method in controller (please not there is needed a validation - #Valid):
#RequestMapping(value = "/authorize", method = {RequestMethod.GET, RequestMethod.POST})
public String authorize(
#Valid AuthorizationRequest authorizationRequest,
BindingResult result
) {
}
I have two configurations classes in my application.
#Configuration
#EnableAutoConfiguration
#EnableWebMvc
#PropertySource("classpath:/jdbc.properties")
public class ApplicationConfig {
}
and
#Configuration
#EnableWebMvc
public class WebappConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthorizationRequestArgumentResolver());
}
}
What is wrong?
Update 2
The problem is with param BindingResult result, when I remove it it works. But I need the result to process it when some errors occur.
If I understand your requirements correctly, you could implement custom HandlerMethodArgumentResolver for that purpose. See example below for implementation details:
Model object
public class AuthorizationRequestHolder {
#Valid
private AuthorizationRequest authorizationRequest;
private BindingResult bindingResult;
// Constructors, accessors omitted
}
Resolver
public class AuthorizationRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return AuthorizationRequestHolder.class.isAssignableFrom(parameter.getParameterType());
}
#Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
// Map the authorization request
AuthorizationRequest authRequest = mapFromServletRequest(request);
AuthorizationRequestHolder authRequestHolder = new AuthorizationRequestHolder(authRequest);
// Validate the request
if (parameter.hasParameterAnnotation(Valid.class)) {
WebDataBinder binder = binderFactory.createBinder(webRequest, authRequestHolder, parameter.getParameterName());
binder.validate();
authRequestHolder.setBindingResult(binder.getBindingResult());
}
return authRequestHolder;
}
}
Configuration
#Configuration
#EnableWebMvc
public class WebappConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthorizationRequestMethodArgumentResolver());
}
}
Usage
#RequestMapping("/auth")
public void doSomething(#Valid AuthRequestHolder authRequestHolder) {
if (authRequestHolder.getBindingResult().hasErrors()) {
// Process errors
}
AuthorizationRequest authRequest = authRequestHolder.getAuthRequest();
// Do something with the authorization request
}
Edit: Updated answer with workaround to non-supported usage of #Valid with HandlerMethodArgumentResolver parameters.

GWT - RemoteService interface and Spring - how to get HttpSession?

I am using GWT (2.5) with RPC, Spring and Postgresql for my project. My issue is about HttpSession handling .
All queries to server are dispatched by Spring (DispatchServlet) to my GwtController (extends RemoteServiceServlet) .
The particular RemoteService is injected in the GwtController . It is easy to get the HttpSession inside the GwtContorller.
In example by getThreadLocalRequest().getSession() or just from request.getSession().
My question is how to get HttpSession object inside the RemoteService ?
public class GwtRpcController extends RemoteServiceServlet {
……………
private RemoteService remoteService;
private Class remoteServiceClass;
………………
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
…………
}
public String processCall(String payload) throws SerializationException {
…………
}
public void setRemoteService(RemoteService remoteService) {
…………….
}
}
My Interface - DataService which implements RemoteService
public class DataServiceImpl implements DataService {
public Data getData(){
!!!!! Here I want to get HttpSession !!!!!
…………………………
}
}
You can maintain a ThreadLocal in your Servlet and store there your current Request, then expose your Request with a static method.
public class GwtRpcController extends RemoteServiceServlet {
static ThreadLocal<HttpServletRequest> perThreadRequest =
new ThreadLocal<HttpServletRequest>();
#Override
public String processCall(String payload) throws SerializationException {
try {
perThreadRequest.set(getThreadLocalRequest());
return super.processCall(payload);
} finally {
perThreadRequest.set(null);
}
}
public static HttpServletRequest getRequest() {
return perThreadRequest.get();
}
}
public class DataServiceImpl implements DataService {
public Data getData(){
HttpServletRequest request = GwtRpcController.getRequest();
HttpSession session = request.getSession();
}
}

Resources