How to disable or remove Allow response header from OPTIONS? - java

I have SecurityConfig class and I have added code to disable headers but I want to disable the 'Allow' response header. I have tried many different ways but no luck. How to add a custom header to disable?
#Configuration
#Slf4j
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.headers().xssProtection().disable()
.and().headers().frameOptions().disable()
.and().headers().contentTypeOptions().disable()
.and().headers().disable()
.httpBasic();
}
}
Rest Controller
{
#RequestMapping(value = Constants.API_BASE_MAPPING + Constants.API_EVENT, method = RequestMethod.OPTIONS)
public ResponseEntity<?> publishEventMessage() {
return getResponseEntity();
}
private ResponseEntity<?> getResponseEntity() {
return ResponseEntity
.ok().contentType(MediaType.APPLICATION_JSON)
.allow() // want to remove this
.build();
}
}
Below is the response header from my OPTIONS API call

If you want to set an empty Allow Header response in a particular method in your controller, you can use:
return ResponseEntity
.ok().contentType(MediaType.APPLICATION_JSON)
.header("Allow", "")
.build();
Also, you can disable the OPTIONS http method for a certain path in your security configuration adding:
.antMatchers(HttpMethod.OPTIONS,"path/to/deny").denyAll()
You can't delete headers after being set. One possible solution is prevent that by creating a Filter which skips the setHeader.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
public void setHeader(String name, String value) {
if (!name.equalsIgnoreCase("Allow")) {
super.setHeader(name, value);
}
}
});
}
Based on this: https://stackoverflow.com/a/7895292/3713193
How to define a filter in Spring Boot: https://www.baeldung.com/spring-boot-add-filter

Related

Disabling a filter for only a few paths in spring security

How do I get a filter to apply to every request off the root path except for ones I want to ignore? Here's my example:
I have a Spring Security filter like so:
private static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().antMatcher("/**")
.addFilterBefore(new AuthenticationFilter(), BasicAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) {
web.ignoring().requestMatchers(SecurityServletRequestMatchers.servletIgnoreAuthMatcher());
}
}
This filter populates a CustomApiToken object which contains all of our header information and puts it in the spring security context SecurityContextHolder.getContext().setAuthentication(token) for easy access to the token on the requesting controller.
I'm trying to add springfox to the project, which means I want to disable the filter for the UI and api docs pages.
My original attempt was to add a clause to the method:
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().antMatcher("/**")
.addFilterBefore(new AuthenticationFilter(), BasicAuthenticationFilter.class);
http.requestMatcher(SecurityServletRequestMatchers.servletIgnoreAuthMatcher()).headers() //.servletIgnoreAuthMatchers has all the swagger urls also
.defaultsDisabled()
.disable()
.authorizeRequests()
.anyRequest().authenticated();
}
However I discovered that this only takes the second clause into account due to spring security only accepting the last clause.
I've since tried:
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/**")
.addFilterBefore(new AuthenticationFilter(), BasicAuthenticationFilter.class)
.requestMatcher(SecurityServletRequestMatchers.servletIgnoreAuthMatcher()).headers()
.defaultsDisabled()
.disable()
.authorizeRequests()
.anyRequest().authenticated();
}
But that left the webfilter on the springfox url giving me a missing authentication token error.
I've tried looking around here, and on the internet, but none of the examples have given me an acceptable response yet.
In your custom AuthenticationFilter you can define a RequestMatcher and use it before doing your logic, like so:
public class AuthenticationFilter extends OncePerRequestFilter {
private final RequestMatcher ignoredPaths = new AntPathRequestMatcher("/swagger-ui");
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
if (this.ignoredPaths.matches(request)) {
filterChain.doFilter(request, response);
return;
}
// do your logic
filterChain.doFilter(request, response);
}
}
You can override shouldNotFilter method of OncePerRequestFilter in your custom filter to split your filter and not_filter logic, e.g. like this:
public class AuthenticationFilter extends OncePerRequestFilter {
private final List<AntPathRequestMatcher> excludedMatchers;
public AuthenticationFilter (List<AntPathRequestMatcher> excludedMatchers) {
this.excludedMatchers = excludedMatchers;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// your filter logic here
filterChain.doFilter(request, response);
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return excludedMatchers.stream()
.anyMatch(matcher -> matcher.matches(request));
}
}
By default this method always returns false, so a filter is applied to any request unless otherwise specified.
Note, that AntPathRequestMatcher's constructor also can take http method, allowing to create more specific not_filter logic, if you have multiple endpoints with the same path and different request methods, but want to allow a free access only to a specific one.

Securing a REST Api with Spring Security

I'm new to Spring Security and I'm trying to secure a REST api inside my app. I have the app URIS and then I have an URI like "/api/v1/" to my rest api.
I have secured my application with username/password authentication and it's working fine, but now I want to secure my REST api returning 401 Unauthorized if the user isn't authenticated, but I don't know how to keep the two authentications together.
What it's the way to go here?
PS: I'm using Spring MVC with Spring Security
This is my Spring Security configuration right now:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccountService accountService;
#Bean
public TokenBasedRememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices("remember-me-key", accountService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(true).userDetailsService(accountService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/favicon.ico", "/resources/**", "/signup").permitAll().anyRequest()
.authenticated()
.and().formLogin().loginPage("/signin").permitAll().failureUrl("/signin?error=1")
.loginProcessingUrl("/authenticate")
.and().logout().logoutUrl("/logout").permitAll().logoutSuccessUrl("/signin?logout")
.and().rememberMe().rememberMeServices(rememberMeServices()).key("remember-me-key")
.and().csrf();
}
#Bean(name = "authenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
So you want to have form login plus secure rest api right?
jhipster can generate such project structure. Let me give some example code for achieving this if you don't want to use jhipster (it's pretty cool though, I recommend)
to return 401 for unauthorized you need something like this:
/**
* Returns a 401 error code (Unauthorized) to the client.
*/
#Component
public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(Http401UnauthorizedEntryPoint.class);
/**
* Always returns a 401 error code to the client.
*/
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2)
throws IOException,
ServletException {
log.debug("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
then inject and add this to your SecurityConfig.configure(HttpSecurity http) method:
http.authenticationEntryPoint(authenticationEntryPoint)
plus since REST requests are ajax requests you also need ajax entry points:
/**
* Returns a 401 error code (Unauthorized) to the client, when Ajax authentication fails.
*/
#Component
public class AjaxAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
}
}
/**
* Spring Security success handler, specialized for Ajax requests.
*/
#Component
public class AjaxAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}
/**
* Spring Security logout handler, specialized for Ajax requests.
*/
#Component
public class AjaxLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler
implements LogoutSuccessHandler {
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}
add them to security config too:
http.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
All credit goes to the amazing jhipster authors.
You can use expression based access control
http://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
Something like,
<intercept-url pattern=/** access="isFullyAuthenticated()/>

Spring secure filter to protect anonymous requests

One more question about spring configuration...
I have several rest methods opened to everyone and not secured. These rest methods on server #1 are used by another server #2 in the same domain to get some data. The idea is that server #2 sets my_super_secure_cookie to some secure token and server #1 decodes and verifies it. Here is the code:
#Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
// Some code
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/rest/public/*").permitAll()
.anyRequest().authenticated();
}
// More code
}
public class SuperSecurityFilter extends FilterSecurityInterceptor implements Filter {
public SuperSecurityFilter(String key) {
super(key);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
Cookie[] cookies = req.getCookies();
Optional<Cookie> tokenCookie = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals("my_super_secure_cookie")).findFirst();
if (tokenCookie.isPresent()) {
Cookie cookie = tokenCookie.get();
TokenCookie.create(cookie.getValue()).validate();
} else {
throw new Exception("Ooops!"));
}
chain.doFilter(req, res);
}
}
The question is how do I configure SecurityConfig to use SecurityTokenFilter on request to any of the /rest/public/* rest methods. Something like:
http
.antMatcher("/rest/public/*")
.addFilterBefore(new SuperSecurityFilter());
is not working, SuperSecurityFilter is not called on request.
p.s. I'm forced to work with this type of security model due to current business logic restrictions.
I solved (applied workaround?) the issue I have by implementing not filter, but interceptor, like this:
public class SuperSecurityInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// implementation here
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// Nothing here
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Nothing here
}
}
And registered this interceptor in my entity extending WebMvcConfigurerAdapter. Like this:
registry.addInterceptor(new SupeSecurityInterceptor()).addPathPatterns("/rest/public/*");
Not sure if this is right thing to do though... Anyway would be glad to know about the conventional approach of implementing this type of functionality.

How to handle spring security InternalAuthenticationServiceException thrown in Spring ProviderManager

ProviderManager is throwing InternalAuthenticationServiceException.class while retrieving users in DaoAuthenticationProvider.class,
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
I want to handle this exception and return my custom response to the client.
I don't want to handle this by writing custom ProviderManager.
For all other OAuth exceptions i am able to handle the exceptions using Custom WebResponseExceptionTranslator.
But I am unable to catch security exceptions like InternalAuthenticationServiceException.class.
I don't have option to use ErrorController with the /error path, it is breaking other flows.
You can write a class which is annotated with #ControllerAdvice and have a #ExceptionHandler(value=InternalAuthenticationServiceException.class).
Ex:-
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler(InternalAuthenticationServiceException.class)
public ResponseEntity<String> handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) {
ResponseEntity<String> response = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
return response;
}
}
UPDATE
If you don't have controllers and using #EnableAuthorizationServer then you need to extend from AuthorizationServerConfigurerAdapter and override configure(AuthorizationServerEndpointsConfigurer endpoints) as below. You can use AuthorizationServerEndpointsConfigurer.exceptionTranslator to handle your InternalAuthenticationServiceException.
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
// other endpoints
.exceptionTranslator(e -> {
if (e instanceof InternalAuthenticationServiceException) {
InternalAuthenticationServiceException internalAuthenticationServiceException = (InternalAuthenticationServiceException) e;
// return a ResponseEntity or throw a custom Exception.
}
});
}
First you need to implements your own AuthenticationEntryPoint the name is not really autoexplicative...
For example if you need to return always status code 200 (only for learning purpose, please donĀ“t do it in real world...)
#Component("myOwnAuthenticationEntryPoint")
public class MyOwnAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_OK, "Unauthorized");
}
Then in your WebSecurityConfig you need to set it as your authentication exception handler entry point.
...
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Autowired
MyOwnAuthenticationEntryPoint myOwnAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().authenticationEntryPoint(myOwnAuthenticationEntryPoint);
...
}
Thats all. :)
I've solved that problem by override unsuccessfulAuthentication method in my filter and send an error response to the client with the desired HTTP status code. In my case, I also created my custom exception (RecordNotFoundException) that is thrown from my service.
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
if (failed.getCause() instanceof RecordNotFoundException) {
response.sendError((HttpServletResponse.SC_NOT_FOUND), failed.getMessage());
}
}

Spring Security- How to specify filter processing url in CustomTokenAuthenticationFilter

I am trying to secure my Spring Rest API with token here is my custom filter
public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger logger = LoggerFactory.getLogger(CustomTokenAuthenticationFilter.class);
public CustomTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(new NoOpAuthenticationManager());
setAuthenticationSuccessHandler(new TokenSimpleUrlAuthenticationSuccessHandler());
}
public final String HEADER_SECURITY_TOKEN = "X-CustomToken";
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String token = request.getHeader(HEADER_SECURITY_TOKEN);
logger.info("token found:"+token);
AbstractAuthenticationToken userAuthenticationToken = authUserByToken(token);
if(userAuthenticationToken == null || userAuthenticationToken.getPrincipal().equals("guest")) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
return userAuthenticationToken;
}
/**
* authenticate the user based on token
* #return
*/
private AbstractAuthenticationToken authUserByToken(String token) {
if(token==null) {
return null;
}
AbstractAuthenticationToken authToken = new MyToken(token);
try {
return authToken;
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return authToken;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
super.doFilter(req, res, chain);
}
}
and here is how I configured it
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
protected AbstractAuthenticationProcessingFilter getFilter() {
return new CustomTokenAuthenticationFilter("/api/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable();
}
}
If you look at the getFilter(), I have passed "/api/*" as a filter processing url, but I want to configure these urls with HttpSecurity object, some thing as follows
http.authorizeRequests().antMatchers("/", "/rome").permitAll()
.antMatchers("/api/admin", "/api/newUser").access("hasRole('ADMIN')")
.antMatchers("/api/db").access("hasRole('ADMIN') or hasRole('DBA')")
Problem I see is that, the Custom filter requires a String as "filter processing url" but I do not want specify anything. That information should be passed by configuring HttpSecurity object through antMatchers etc.
Is it really possible? if yes how can I achieve that?
I used OncePerRequestFilter.
public class MyAuthenticationFilter extends OncePerRequestFilter {
// private RequestMatcher requestMatcher;
private List<RequestMatcher> includedPathMatchers = new ArrayList<>();
private List<RequestMatcher> excludedPathMatchers = new ArrayList<>();
// implement getters and setters
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
// your filter implementation and security logics
}
}
You can treat this class as a normal bean (use #Autowired and so on). Then you just need do register it in your context and inject it in the security chain.
Hope it helps.
This answer will be useful to you. It says to use setter setFilterProcessingURL() available in AbstractAuthenticationProcessingFilter

Categories

Resources