web filter cannot read Property file in spring boot - spring-boot

I have simple ip filter, which will filter ips not in white list.
The code is simple, like
#EnableConfigurationProperties(AppProperties.class)
public class ClientIpFilter implements Filter {
#Autowired
private AppProperties appProperties;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
HashSet<String> trustedIps = loadAccessIPList();
HttpServletRequest req = (HttpServletRequest) request;
Map<String, String> headerMap = new HashMap<String, String>();
Enumeration<String> headers = req.getHeaderNames();
while (headers.hasMoreElements()) {
String str = (String) headers.nextElement();
headerMap.put(str, req.getHeader(str));
}
.............
}
And I define a Configuration to include it.
#Configuration
public class CustomWebSecurityConfigurerAdapter {
#Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
ClientIpFilter securityFilter = new ClientIpFilter();
registrationBean.setFilter(securityFilter);
registrationBean.setOrder(1);
return registrationBean;
}
}
But the Filter cannot autowired AppProperties which includes configuration from my application.preperties.

Your AppProperties file should be like below with annotations and appropriate prefix as mentioned in application.properties file
#Configuration
#ConfigurationProperties(prefix = "com.test")
public class AppProperties {
private String name;
private String age;
}
The #EnableConfigurationProperties should not be required in ClientFilter class. Just autowiring the Properties file should be ok ..

I find the root reason. I need to let the ClientIpFilter to be a component so that it can be autowired, then spring can register other property.
#Component
#EnableConfigurationProperties(AppProperties.class)
public class ClientIpFilter implements Filter {
#Autowired
private AppProperties appProperties;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
HashSet<String> trustedIps = loadAccessIPList();
HttpServletRequest req = (HttpServletRequest) request;
Map<String, String> headerMap = new HashMap<String, String>();
.............
}

Related

#Value variable is null in customFilter implementing Filter

my custom filter is not taking value from .properties/.yml file
Note: property file is located at src/main/resources folder
#Slf4j
public class CustomFilter implements Filter {
#Value("${xyz.domainName:http://localhost:8080/x1}")
private String DOMAIN_NAME;
private static final String REDIRECT_URL_ENDPOINT = "/v1/xyz/abc/";
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String url = request.getRequestURL().toString();
String id = url.substring(url.lastIndexOf("/") + 1);
if (url.startsWith(DOMAIN_NAME)) {
ServletContext context = request.getServletContext();
RequestDispatcher dispatcher = context.getRequestDispatcher(REDIRECT_URL_ENDPOINT + id);
dispatcher.forward(request, response);
}
else
filterChain.doFilter(request, response);
}
}
Edit (Added WebSecurityConfigClass): My WebSecurityConfig class looks like:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomFilter(), ApiKeyAuthFilter.class);
}
}
Looking at the code, you filter is not managed by the Spring Context, therefore Spring-related functionalities like #Value do not work. So, let Spring take care of your filter.
#Component
#Slf4j
public class CustomFilter implements Filter {
#Value("${xyz.domainName:http://localhost:8080/x1}")
private String DOMAIN_NAME;
private static final String REDIRECT_URL_ENDPOINT = "/v1/xyz/abc/";
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String url = request.getRequestURL().toString();
String id = url.substring(url.lastIndexOf("/") + 1);
if (url.startsWith(DOMAIN_NAME)) {
ServletContext context = request.getServletContext();
RequestDispatcher dispatcher = context.getRequestDispatcher(REDIRECT_URL_ENDPOINT + id);
dispatcher.forward(request, response);
}
else
filterChain.doFilter(request, response);
}
#Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(this);
registration.addUrlPatterns("/*");
return registration;
}
}
If you want to register the filter before a Spring security filter you can do this:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Customfilter customfilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(customfilter, ApiKeyAuthFilter.class);
}
}

Defined Property from Application.property getting NULL in class file using Spring boot Filter Implementation

I am trying to do one sample of rest template and property injection for variable defined from application.property. For this I created one filter implementation and done like the following. But when I am accessing the property I am getting error like following
java.lang.IllegalArgumentException: URI must not be null
And I created my filter implementation code like the following,
#Component
public class CustomSecurityFilter extends OncePerRequestFilter implements Filter{
#Value ("${securityTriggerServiceApiUrl}" )
public String triggerUrl;
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException {
String authHeaderToken = request.getHeader("authToken");
System.out.println("token :"+authHeaderToken);
if(checkAuthenticationByAuthToken(authHeaderToken))
{
filterChain.doFilter(request, response);
}
else
{
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
}
}
private static HttpHeaders getApiHeaders(){
String plainCredentials="${seccurityTriggerEncodingCredential}";
String base64Credentials = new String(Base64.encodeBase64(plainCredentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Credentials);
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public Boolean checkAuthenticationByAuthToken(String authTokenRequest) {
AuthorizationRequest authApiRequestObj = new AuthorizationRequest();
authApiRequestObj.auth_token = authTokenRequest;
RestTemplate restTemplate = new RestTemplate();
HttpEntity<Object> request = new HttpEntity<Object>(authApiRequestObj, getApiHeaders());
AuthorizationResponse authorizationResponseObj = restTemplate.postForObject(getApiTriggerStringUrl(), request, AuthorizationResponse.class);
System.out.println("RoleId is :"+authorizationResponseObj.role_id);
if(authorizationResponseObj.role_id >= 0 ) {
return true;
}
return false;
}
public String getApiTriggerStringUrl() {
return this.triggerUrl;
}
}
My application.property file added like the following,
seccurityTriggerEncodingCredential=test:test
securityTriggerServiceApiUrl=http://localhost:8065/test/security/authorizeUser
My Security config file contains like the following way,
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilterAfter(new CustomSecurityFilter(), BasicAuthenticationFilter.class);
}
}
Why I am getting the error like this?
The problem is while registering your filter, you have created a new filter with new. So configuration is not picking the one created by Spring. You can do either of following -
1) Autowire the existing spring bean in your configuration class -
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomSecurityFilter customSecurityFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilterAfter(customSecurityFilter, BasicAuthenticationFilter.class);
}
}
You can do autowiring any way either directly through #Autowired or through constructor.
2) Second way is to create CustomSecurityFilter bean by yourself. Sample code as per your code -
a) Don't annotate your filter with #Component. Remove #Value and create a setter for your property.
public class CustomSecurityFilter extends OncePerRequestFilter implements Filter{
public String triggerUrl;
public void setTriggerUrl(String triggerUrl) {
this.triggerUrl = triggerUrl;
}
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException {
String authHeaderToken = request.getHeader("authToken");
System.out.println("token :"+authHeaderToken);
if(checkAuthenticationByAuthToken(authHeaderToken))
{
filterChain.doFilter(request, response);
}
else
{
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
}
}
private static HttpHeaders getApiHeaders(){
String plainCredentials="${seccurityTriggerEncodingCredential}";
String base64Credentials = new String(Base64.encodeBase64(plainCredentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Credentials);
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public Boolean checkAuthenticationByAuthToken(String authTokenRequest) {
AuthorizationRequest authApiRequestObj = new AuthorizationRequest();
authApiRequestObj.auth_token = authTokenRequest;
RestTemplate restTemplate = new RestTemplate();
HttpEntity<Object> request = new HttpEntity<Object>(authApiRequestObj, getApiHeaders());
AuthorizationResponse authorizationResponseObj = restTemplate.postForObject(getApiTriggerStringUrl(), request, AuthorizationResponse.class);
System.out.println("RoleId is :"+authorizationResponseObj.role_id);
if(authorizationResponseObj.role_id >= 0 ) {
return true;
}
return false;
}
public String getApiTriggerStringUrl() {
return this.triggerUrl;
}
}
b) Your configuration class will be then -
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilterAfter(customSecurityFilter(), BasicAuthenticationFilter.class);
}
#Bean
public CustomSecurityFilter customSecurityFilter() {
CustomSecurityFilter customSecurityFilter = new CustomSecurityFilter();
customSecurityFilter.setTriggerUrl(<property value>);
return customSecurityFilter;
}
}

How to return response as Json from spring filter?

In spring rest I need to send authentication error from doFilter() of my filter class. In response i need to send json with fields like status, message and errorCode. Kindly suggest how to achieve. We are not using spring boot.Below is the sample response on Authentication error
{ "responseCode":" Error code",
"responseMessage": "Some Error message",
"responseStatus":"Fail"
}
Inside doFiler(), i am validating token, if its not valid I need to send above sample response.
Assuming you have Jackson's ObjectMapper exposed as a Spring bean, you could use the following for a OncePerRequestFilter:
#Component
#RequiredArgsConstructor
public class MyFilter extends OncePerRequestFilter {
private final ObjectMapper mapper;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
FilterChain filterChain) throws IOException {
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("message", "Invalid token");
httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(httpServletResponse.getWriter(), errorDetails);
}
}
For a plain servlet Filter, the solution would be much the same:
#Component
#RequiredArgsConstructor
public class MyFilter implements Filter {
private final ObjectMapper mapper;
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException {
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("message", "Invalid token");
httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(httpServletResponse.getWriter(), errorDetails);
}
#Override
public void destroy() {
}
}
The above examples rely on constructor injection and use Lombok's #RequiredArgsConstructor to generate a constructor that receives values for the fields marked with final.
You also could replace the Map<String, Object> for any arbitrary POJO, according to your needs.

Custom Spring Security Filter Test with MockMvc always returns 404

I am trying to test a custom filter using MockMvc and it always returns status 404 instead of the actual status returned from the filter class.
The filter class looks as follows:
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
// some logic
chain.doFilter(servletRequest, servletResponse);
}
}
This is registered in with the SpringBootApplication using FilterRegistrationBean
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
ConfigurableApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
} catch (Throwable e) {
}
#Bean
public FilterRegistrationBean<CustomFilter> customFilter() {
FilterRegistrationBean<CustomFilter> filterRegistration = new FilterRegistrationBean<>();
filterRegistration.setFilter(new CustomFilter());
filterRegistration.addUrlPatterns("/test/*");
return filterRegistration;
}
}
The test for this is written as follows:
#RunWith(SpringRunner.class)
#SpringBootTest(CustomFilter.class)
#EnableAutoConfiguration
#WebAppConfiguration
public class CustomFilterTest {
#Autowired
private CustomFilter filter;
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
this.mvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(filter)
.build();
}
#Test
public void testCustomFilter() throws Throwable {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders
.request(HttpMethod.POST, "/resource/abc")
.header("SomeHeader", "xxx")
.content("{}");
MockHttpServletResponse response = mvc.perform(request).andReturn().getResponse();
assertEquals(response.getStatus(), HttpServletResponse.SC_CONTINUE);
}
}
The assertion always returns the status value of 404.
Am I missing something here?
Add urlPatterns to the addFilter(filter, "/resource/abc") and add a test controller for the above code snippet as follows:
#RestController
public class TestController {
#PostMapping("/resource/abc")
public void testSecureEndpoint() {
}
}

Spring Boot Rest API filter not invoked

I want to implement filter on my Spring Boot Rest API project but some how my filter not invoked. I have added below implementation for that.
public class AutorizationFilter implements Filter{
Logger log = LoggerFactory.getLogger(AutorizationFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("**** Start ****");
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("**** doFilter ****");
chain.doFilter(request, response);
}
#Override
public void destroy() {
log.info("**** end ****");
}}
init and destroy method are working but doFilter method not invoke.
#Configuration
public class RestApiConfig {
#Bean
public FilterRegistrationBean<AutorizationFilter> filterRegistrationBean(){
FilterRegistrationBean<AutorizationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AutorizationFilter());
registrationBean.addUrlPatterns("/**");
return registrationBean;
}}
My controller is as below:
#RestController
#RequestMapping("/home")
public class HomeController {
#RequestMapping(value="/hello",method=RequestMethod.GET)
public ResponseEntity<Object> hello() {
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("data","called home controller");
return new ResponseEntity<>(resultMap,HttpStatus.OK);
}
}
Any one please help me why dofilter method is not working.
finally i found the solution, I need to replace registrationBean.addUrlPatterns("/**") with registrationBean.addUrlPatterns("/*") and it's working fine for me.

Resources