I am trying to inject a auth service to a Filter -
#Autowired
AuthRequestService authService;
And use it in doFiler method -
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("SAPServiceFilter: "+ req.getRequestURI());
//TODO - create auth sender
authService.isAuthnticate((HttpServletRequest)request); //null
chain.doFilter(request, response);
}
My filter class in sub-package of my #SpringBootApplication class and annotated with #service -
#Service
public class AuthRequestService {
#PostConstruct
public void init() {
System.out.println("AuthRequestService #PostConstruct");
}
public boolean isAuthnticate(HttpServletRequest request) {
System.out.println("isAuthnticate");
return true;
}
}
The class also appears when listing all my beans using -
for (String name : applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
Still when debugging authService is null, one last thing the filter is registered with FilterRegistrationBean -
FilterRegistrationBean<SAPServiceFilter> filterRegBean = new FilterRegistrationBean<>();
filterRegBean.setFilter(new SAPServiceFilter());
You could use constructor injection. Supposed your filter registration bean lives in a component and has access to the service you could autowire it there and pass it with the constructor
#Autowired
AuthRequestService authRequestService;
[...]
FilterRegistrationBean<SAPServiceFilter> filterRegBean = new FilterRegistrationBean<>();
filterRegBean.setFilter(new SAPServiceFilter(authRequestService));
Your filter is not under the control of spring. That´s why the autowired dependencies are not being injected.
In your filter init code add this line:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
or
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,getServletContext());
But there are more other ways to register a servlet filter in spring context:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners-beans
Related
I'm trying to customize a login process in my own login controller instead of using UsernamePasswordAuthenticationFilter
#PostMapping(value = "/login")
public ResponseEntity<?> login(
HttpServletRequest httpRequest,
#RequestBody AuthenticationRequest authenticationRequest) {
// authentication code here
Authentication authenticate=this.authenticationManager.authenticate(authRequest);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
return handlerAuthLogin(httpRequest, result, authorizationRequest);
}
But I can't auto inject Principal in other controllers if I login success as below:
#Controller
public class UsersController {
#RequestMapping(value = "/me")
public string getMyName(Principal principal){
return principal.getName(); // principal is null
}
}
Any guys know why to fix it?
When you execute context.setAuthentication(authentication) the authentication is only valid for the current request. So for the second /me request you need to set the authentication as well.
Therefore you need to authenticate the user on a per-request base. That can be done by implementing a GenericFilterBean:
public class CustomAuthenticationFilter extends GenericFilterBean {
private final AuthenticationManager authenticationManager;
public CustomAuthenticationFilter(
AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
/*
Note that you need to receive the authentication token in different manner now.
Usually headers are used for that.
*/
Authentication authenticate = authenticationManager.authenticate(request.getHeader("authToken"));
SecurityContext context = SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
}
After implementing the filter you need to register it in the servlet container at the position where it is best suited. Spring Security handles the security filters per WebsecutiryConfigurer, so you need to register your filter in the config of the respective configurer of your users.
As an example I put it after ConcurrentSessionFilter:
#Configuration
#Order(1)
public static class UserWebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
CustomAuthenticationFilter filter = new PlayerAuthenticationFilter(jwtService,
objectMapper);
http.addFilterAfter(filter, ConcurrentSessionFilter.class);
(...)
}
}
Check out the documentation about filter ordering to find the position best suited for your method.
Update
I wrote a more in-depth blog post about this topic. Fell free to check it out.
#Marcus
Thanks for your clarification, I found the cause for my case.
I wrongly config a
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public WebSecurityConfig() {
super(true); // I disable the default config, so the SecurityContextPersistenceFilter didn't be added by default and all my SecurityContext info is not persistent
}
}
I have Spring Boot application with REST API mapped on /api. I need to define additional servlet on /. I want all request that match /api was handled by REST API and all others requests by the servlet. How to do this?
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#RestController
#RequestMapping("/api")
public class ApiController {
#GetMapping
public String get() {
return "api";
}
}
#Bean
public ServletRegistrationBean customServletBean() {
return new ServletRegistrationBean<>(new HttpServlet() {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().println("custom");
}
}, "/*");
}
}
In code above I want something like this:
curl http://localhost:8080/api/
> api⏎
curl http://localhost:8080/custom/
> custom
I have tried with filter to redirect requests, but all requests go to custom servlet:
#Bean
public FilterRegistrationBean apiResolverFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter((req, response, chain) -> {
HttpServletRequest request = (HttpServletRequest) req;
String path = request.getRequestURI().substring(request.getContextPath().length());
if (path.startsWith("/api/")) {
request.getRequestDispatcher(path).forward(request, response);
} else {
chain.doFilter(request, response);
}
});
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
This project is available on github: https://github.com/mariuszs/nestedweb
When mapping a servlet to the root path you will override the mapping for the DispatcherServlet which, by default, is mapped to /.
There are basically 3 solutions you could try
Map the DispatcherServlet to /api and modify the mappings in your controllers
Use a ServletForwardingController to forward the request to the configured but unmapped Servlet
Use a ServletWrappingController to wrap a Servlet instance
Number 2 and 3 are almost the same, with this difference that with option 3 Spring also manages the Servlet instance whereas with option 2, the Servlet container manages the Servlet.
Mapping DispatcherServlet to /api
Option 1 can be an option if all of your controllers are mapped under /api, if they aren't this isn't an option. In your application.properties you would set the spring.mvc.servlet.path to /api. Then you would configure your other Servlet like you did in your question.
Use a ServletForwardingController
Spring provides a ServletForwardingController which will lookup a Servlet in the ServletContext given the name of the servlet and forward the request to it. You will still have to register the Servlet but prevent it from being mapped.
Next you would need a SimpleUrlHandlerMapping to map the URLs to this controller or set it as the default handler (basically a catch all).
#Bean
public ServletForwardingController forwarder() {
ServletForwardingController controller = new ServletForwardingController();
controller.setServletName("my-servlet");
return controller;
}
#Bean
public CustomServlet customServlet() {
return new CustomServlet();
}
#Bean
public ServletRegistrationBean customServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(customServlet(), false);
registration.setServletName("customServlet");
return registration;
}
#Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setDefaultHandler(forwarder());
mapping.setOrder(LOWEST_PRECEDENCE - 2);
return mapping;
}
Use a ServletWrappingController
Spring provides a ServletWrappingController which will internally create and configure a Servlet instance. It acts as an adapter from/to the Servlet to a Spring Controller. You don't have to register the CustomServlet in this case and is thus slightly easier to configure the then ServletForwardingController.
Next you would need a SimpleUrlHandlerMapping to map the URLs to this controller or set it as the default handler (basically a catch all).
#Bean
public ServletWrappingController wrapper() {
ServletWrappingController controller = new ServletWrappingController ();
controller.setServletName("my-servlet");
controller.setServletClass(CustomerServlet.class);
return controller;
}
#Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setDefaultHandler(wrapper());
mapping.setOrder(LOWEST_PRECEDENCE - 2);
return mapping;
}
Depending on your architecture and url structure you might want to go for option 1 or option 3.
I'm trying to log all request and response with the body for my REST service.
So far for logging request, I'm using the Spring built-in solution to log payloads RequestLoggingFilterConfig and it works perfectly.
Now I'm looking for a similar solution for logs Response.
The question is how can I logs the whole responses with the body from REST and can it be done only by the configuration file?
My configuration for the request
#Configuration
public class RequestLoggingFilterConfig {
#Bean
public CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(10000);
filter.setIncludeHeaders(true);
filter.setAfterMessagePrefix("REQUEST DATA: ");
return filter;
}
}
and the application.properties
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
application.properties logging.level.* help just configured your logging level, For handling the logging response,
You need some kind of middleware which can intercept your response
1: Custom Filter Filter is one of the best approach for handle this problem
eg:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class WebFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(WebFilter.class);
private static final boolean CONDITION = true;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.debug("Initiating WebFilter >> ");
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(requestWrapper, response);
// log your response here
}
#Override
public void destroy() {
logger.debug("Destroying WebFilter >> ");
}
}
register your filter
#Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(someFilter());
registration.addUrlPatterns("/api/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("someFilter");
registration.setOrder(1);
return registration;
}
public Filter someFilter() {
return new WebFilter();
}
2:InterceptorAdapter
You can extend HandlerInterceptorAdapter and override the afterCompletion method
public void afterCompletion,
#Component
public class LogginInterceptor
extends HandlerInterceptorAdapter {
#Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
// log your response here
}
}
3:AOP Also you can use magical AOP for handle the response using #After
impl
I am trying to authenticate user by token, But when i try to auto wire one my services inside the AuthenticationTokenProcessingFilter i get null pointer exception. because autowired service is null , how can i fix this issue ?
My AuthenticationTokenProcessingFilter class
#ComponentScan(basePackages = {"com.marketplace"})
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
#Autowired
#Qualifier("myServices")
private MyServices service;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
#SuppressWarnings("unchecked")
Map<String, String[]> parms = request.getParameterMap();
if (parms.containsKey("token")) {
try {
String strToken = parms.get("token")[0]; // grab the first "token" parameter
User user = service.getUserByToken(strToken);
System.out.println("Token: " + strToken);
DateTime dt = new DateTime();
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime createdDate = fmt.parseDateTime(strToken);
Minutes mins = Minutes.minutesBetween(createdDate, dt);
if (user != null && mins.getMinutes() <= 30) {
System.out.println("valid token found");
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getEmailId(), user.getPassword());
token.setDetails(new WebAuthenticationDetails((HttpServletRequest) request));
Authentication authentication = new UsernamePasswordAuthenticationToken(user.getEmailId(), user.getPassword(), authorities); //this.authenticationProvider.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}else{
System.out.println("invalid token");
}
} catch(Exception e) {
e.printStackTrace();
}
} else {
System.out.println("no token found");
}
// continue thru the filter chain
chain.doFilter(request, response);
}
}
I Tried adding follwing in my AppConfig
#Bean(name="myServices")
public MyServices stockService() {
return new MyServiceImpl();
}
My AppConfig Annotations are
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.marketplace")
public class AppConfig extends WebMvcConfigurerAdapter {
You cannot use dependency injection from a filter out of the box. Although you are using GenericFilterBean your Servlet Filter is not managed by spring. As noted by the javadocs
This generic filter base class has no dependency on the Spring
org.springframework.context.ApplicationContext concept. Filters
usually don't load their own context but rather access service beans
from the Spring root application context, accessible via the filter's
ServletContext (see
org.springframework.web.context.support.WebApplicationContextUtils).
In plain English we cannot expect spring to inject the service, but we can lazy set it on the first call.
E.g.
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
private MyServices service;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(service==null){
ServletContext servletContext = request.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
service = webApplicationContext.getBean(MyServices.class);
}
your code ...
}
}
It's an old enough question, but I'll add my answer for those who like me google this issue.
You must inherit your filter from GenericFilterBean and mark it as a Spring #Component
#Component
public class MyFilter extends GenericFilterBean {
#Autowired
private MyComponent myComponent;
//implementation
}
And then register it in Spring context:
#Configuration
public class MyFilterConfigurerAdapter extends WebMvcConfigurerAdapter {
#Autowired
private MyFilter myFilter;
#Bean
public FilterRegistrationBean myFilterRegistrationBean() {
FilterRegistrationBean regBean = new FilterRegistrationBean();
regBean.setFilter(myFilter);
regBean.setOrder(1);
regBean.addUrlPatterns("/myFilteredURLPattern");
return regBean;
}
}
This properly autowires your components in the filter.
I just made it work by adding
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
I am unsure why we should do this even when i tried adding explicit qualifier. and now the code looks like
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
#SuppressWarnings("unchecked")
Map<String, String[]> parms = request.getParameterMap();
if (parms.containsKey("token")) {
If your filter class extends GenericFilterBean you can get a reference to a bean in your app context this way:
public void initFilterBean() throws ServletException {
#Override
public void initFilterBean() throws ServletException {
WebApplicationContext webApplicationContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//reference to bean from app context
yourBeanToInject = webApplicationContext.getBean(yourBeanToInject.class);
//do something with your bean
propertyValue = yourBeanToInject.getValue("propertyName");
}
And here is less explicit way for those who doesn't like hardcoding bean names or need to inject more than one bean reference into the filter:
#Autowired
private YourBeanToInject yourBeanToInject;
#Override
public void initFilterBean() throws ServletException{
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, getServletContext());
//do something with your bean
propertyValue = yourBeanToInject.getValue("propertyName");
}
You can configure your bean filter and pass as a parameter whatever you need. I know out of Spring context where the filter it is, you cannot get the dependency injection that the auto-scan of spring does. But not 100% sure if there´s a fancy annotation that you can put in your filter to do some magic stuff
<filter>
<filter-name>YourFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>YourFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
and then inject bean in the spring.xml
<bean id="YourFilter" class="com.YourFilter">
<property name="param">
<value>values</value>
</property>
</bean>
I am late to the party but this solution worked for me.
Add a ContextLoaderListener in web.xml. applicationContext can have dependency beans.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
Then add in MyFilter SpringBeanAutowiringSupport processInjectionBasedOnServletContext which will add the webapplicationcontext into the filter which will add all the dependencies.
#Component
public class MyFilter implements Filter {
#Autowired
#Qualifier("userSessionServiceImpl")
private UserSessionService userSessionServiceImpl;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain
chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) req;
if (userSessionServiceImpl == null) {
ServletContext context = httpRequest.getSession().getServletContext();
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, context);
}
.... (for brevity)
}
}
I have a filter in one of the jar files added as reference.
My project is in spring nature. I have developed a web services. All the requests to this web services should be intercepted by my filter "HelloWorld".
This filter is there in one of the reference files.
Here I thought I would implement it as Interceptor.
The filter in reference files looks like
public class HelloWorld implements Filter {
private static final Logger _logger = Logger.getLogger(HelloWorld.class.getName());
protected String name = null;
public HelloWorld()
{
}
public HelloWorld(String name) {
this.name = name;
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
PrintWriter out = response.getWriter();
out.println("Hello "+name);
chain.doFilter(req, res);
}
public void init(FilterConfig config) throws ServletException {
//Get init parameter
String testParam = config.getInitParameter("test");
//Print the init parameter
System.out.println("test param: " + testParam);
}
public void destroy() {
//add code to release any resource
}
//some other methods as well
}
What would be the best way to implement this. I cannot configure filter in my web.xml due to limitation in my application.
Can we directly give this HelloWorld filter as reference to interceptor, so that it behaves like interceptor.
Can we change this filter as interceptor in spring and configure in spring.xml without changing the functionality.
Apologies if my question is simple.
Thanks.