Consuming HttpServletRequest Multiple Times and Chain Between Methods - java

I have to read HttpServletRequest multiple times. I have wrapped HttpServletRequest like said in those posts Http Servlet request lose params from POST body after read it once
In my filter class which extends AbstractAuthenticationProcessingFilter, i can consume and chain request in successfulAuthentication method since it has chain parameter. But in addition to those solutions i have to chain request between attempt and succesful authentication steps:
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
// wrapping request and consuming
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
// Since i couldn't chain wrapped httpServletRequest from attemptAuthentication step, this request still gets non-wrapping one and inputstream is empty
}
How can i pass wrapped request from attemptAuthentication to successfulAuthentication?

This is an old question but in case someone gets the same problem:
You can wrap a request like suggested in the previous answer but you need to wrap it before it gets filtered by Authentication filter:
Spring security configuration will look something like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthFilter authFilter = new AuthFilter();
WrapperFilter wrapperFilter = new WrapperFilter();
http .cors()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(exceptionHandler)
.and()
.authorizeRequests()
.antMatchers("/v1/*", "/api/*")
.authenticated()
.and()
.addFilterBefore(authFilter, BasicAuthenticationFilter.class)
.addFilterBefore(wrapperFilter, AuthFilter.class);
}
So that wrapper filter goes before your auth filter and wrapper filter's doFilter wraps the request and passes it on:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
MultiReadHttpServletRequest wrapper = new MultiReadHttpServletRequest((HttpServletRequest) request);
chain.doFilter(wrapper, response);
}
And MultiReadHttpServletRequest is the following:
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private byte[] body;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
try {
body = IOUtils.toByteArray(request.getInputStream());
} catch (IOException ex) {
body = new byte[0];
}
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
}
#Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
ByteArrayInputStream wrapperStream = new ByteArrayInputStream(body);
#Override
public boolean isFinished() {
return false;
}
#Override
public boolean isReady() {
return false;
}
#Override
public void setReadListener(ReadListener readListener) {
}
#Override
public int read() throws IOException {
return wrapperStream.read();
}
};
}
}

There is no such way. The only thing you can do is wrap the request every time you gonna read it in one more filter. Just wrap the request every time before you read it copying body.
See some working code for a wrapper e.g. here

Related

request did not match to Ant [pattern='/login']

I am working on microservices. the first part, I work on authentication on the other part, I work on gateway. when I up my project to Openshift, It appears the following mistake
AutogestionAuthenticationFilter : Did not match request to Ant [pattern='/perform-login']
this mistake appears which I do not have knowledge about its causes however, I will show you up the classes involved in
SecurityConfiguration
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.ignoringAntMatchers("/login")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(autogestionFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public AutogestionAuthenticationFilter autogestionFilter() throws Exception {
AutogestionAuthenticationFilter authenticationFilter = new AutogestionAuthenticationFilter();
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
authenticationFilter.setFilterProcessesUrl("/login");
return authenticationFilter;
}
this is the class which appears as mistake
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
this.setUsernameParameter("email");
this.setPasswordParameter("id");
try {
String username = this.obtainUsername(request);
Long password = Long.parseLong(Objects.requireNonNull(this.obtainPassword(request)));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, password);
return getAuthenticationManager().authenticate(auth);
} catch (NumberFormatException | NullPointerException e) {
throw new AuthenticationServiceException("Wrong format");
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
AutogestionUser user = (AutogestionUser) authResult.getPrincipal();
String token = jwtProvider.generateToken(authResult); //token
String body = objectMapper.writeValueAsString(new JwtBody(token));
response.addHeader("Content-Type", "application/json");
response.getWriter().write(body);
}

Spring authentication filter throwing wrong error message

I have a custom filter that is used to authenticate the user. I am always getting full authentication requried error even though I have thrown a custom exception with specific message & added exception handler as well.
Code for filter:
#Slf4j
#Component
public classTokenValidationFilter extends OncePerRequestFilter {
#Autowired
private TokenValidationHelper tokenValidationHelper;
#Override
protected void doFilterInternal(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
MultiReadRequestWrapper request = new MultiReadRequestWrapper(httpRequest);
SecurityContext context = SecurityContextHolder.getContext();
// check if already authenticated
if (context.getAuthentication() == null) {
Authentication authentication =
tokenValidationHelper.validateAndAuthenticate(request);
context.setAuthentication(authentication);
}
filterChain.doFilter(request, httpResponse);
}
}
Code for exception handler:
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler({IrrecoverableAuthException.class})
#ResponseBody
#ResponseStatus(HttpStatus.UNAUTHORIZED)
public RegistrationErrorResponse handleInternalServerException(IrrecoverableAuthException exception) {
return getErrorResponse(exception , Category.Error exception.getMessage());
}
}
But still getting wrong message
"Full authentication access is required to access this resource"
Exception handler won't be invoked from within the filter. You can use HttpServletResponse from within the filter and write your error response manually as follows:
protected void onFailedAuthentication(
HttpServletRequest request,
HttpServletResponse response,
IrrecoverableAuthException failed) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(failed.getStatus().getStatusCode());
try (OutputStream out = response.getOutputStream()) {
out.write(MAPPER.writeValueAsBytes(getErrorResponse())); // build the required response here
out.flush();
} catch (IOException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
Call this method from your filter
#Slf4j
#Component
public classTokenValidationFilter extends OncePerRequestFilter {
#Autowired
private TokenValidationHelper tokenValidationHelper;
#Override
protected void doFilterInternal(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
MultiReadRequestWrapper request = new MultiReadRequestWrapper(httpRequest);
SecurityContext context = SecurityContextHolder.getContext();
// check if already authenticated
if (context.getAuthentication() == null) {
try {
Authentication authentication =
tokenValidationHelper.validateAndAuthenticate(request);
context.setAuthentication(authentication);
} catch(IrrecoverableAuthException ex) {
onFailedAuthentication(httpRequest, httpResponse, ex);
}
}
filterChain.doFilter(request, httpResponse);
}
}

How do I add a permission check for many methods in a controller? (Filter, Action)

I have the following at the beginning of many methods.
if ((user.privilege & User.Privilege.WRITE) == 0) {
session.setAttribute("alert", "You do not have permission to save.");
return new ModelAndView("redirect:/admin/home");
}
How can I extract this and put it into a separate method that is called before many other controller methods, similar to Ruby on Rails before_action :validate_privilege, [:save, :weekly_report, ...]?
I found this in the docs, but it doesn't give any examples.
https://docs.spring.io/spring-boot/docs/1.5.19.RELEASE/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners
I found a way to do it based on #Deadpool's answer. It seems like it is more complicated than a simple annotation would be.
#Bean
public GenericFilterBean beforeAction() {
String[] actions = new String[]{"/admin/censor", "/admin/weekly-report", "/admin/email-blast-submit"};
return new GenericFilterBean() {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// System.out.println("requestURI:"+req.getRequestURI());
boolean found = Arrays.stream(actions).anyMatch(req.getRequestURI()::equals);
if (found) {
User user = (User) req.getSession().getAttribute("user");
if (user != null && (user.privilege & User.Privilege.WRITE) == 0) {
req.getSession().setAttribute("alert", "You do not have permission to save.");
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect("/admin/home");
return;
}
}
chain.doFilter(request, response);
}
};
}
For the API using OAuth2, I used
#Override
public ResponseEntity<...> delete(Principal principal, #RequestBody ...) throws ApiException {
isAuthorized(principal, "ROLE_USER");
...
/** If not authorized, throw ApiException. */
private void isAuthorized(Principal principal, String role) throws ApiException {
OauthClientDetail cd = userMapper.getOauthClientDetails(principal.getName());
if (cd == null || cd.authorities == null || !cd.authorities.equals(role)) {
throw new ApiException(HttpStatus.UNAUTHORIZED, ...);
}
}
(ApiException, OauthClientDetail (POJO), and UserMapper (MyBatis) are custom classes.)
If you are looking for Filter to check each request for authorization you can use Filter
#Component
public class AuthFilter implements Filter {
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
LOG.info(
"Starting a transaction for req : {}",
req.getRequestURI());
chain.doFilter(request, response);
LOG.info(
"Committing a transaction for req : {}",
req.getRequestURI());
}
// other methods
}
or GenericFilterBean
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
You should use Spring Security
http.regexMatcher("/*") --> your base path or sth.
.exceptionHandling()
.and()
.csrf()
.disable();
http.addFilterAfter(authenticationProcessingFilter(),BasicAuthenticationFilter.class);

AbstractAuthenticationProcessingFilter: doFilter gets hit, but `attemptAuthentication` does not

I have the following filter definition:
#Component
public class JWTAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private UserDetailsService customUserDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
private static Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
private final static UrlPathHelper urlPathHelper = new UrlPathHelper();
public JWTAuthenticationFilter() {
super("/**"); // what should I pass here ?
setAuthenticationManager(new NoOpAuthenticationManager());
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request, customUserDetailsService);
return authentication;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(request, response);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,FilterChain chain, Authentication authentication) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authentication);
logger.debug("successful authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
logger.debug("failed authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
}
}
and the following two configure methods in web configuration for my Spring security:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/login**", "/signup**");
}
There are a couple of problems with this code:
I don't want the filter to exectue when calls are made to /login and /signup endpoints, but doFilter still gets hit even when a call is made to these two endpoints.
attemptAuthentication, successfulAuthentication and unsuccessfulAuthentication don't get hit, but doFilter does. Why? why these methods are not called ?
What is happening here ? and why ?
Try this(but not tested!):
public class JWTAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private UserDetailsService customUserDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
private static Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
private final static UrlPathHelper urlPathHelper = new UrlPathHelper();
public JWTAuthenticationFilter() {
super("/**"); // what should I pass here ?
setAuthenticationManager(new NoOpAuthenticationManager());
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request, customUserDetailsService);
return authentication;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,FilterChain chain, Authentication authentication) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authentication);
logger.debug("successful authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
logger.debug("failed authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
}
}
In your configuration file:
#Bean("jwtAuthenticationFilter")
public LoginRequestFilter jwtAuthenticationFilter(){
LoginRequestFilter filter = new LoginRequestFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setAuthenticationSuccessHandler(successHandler);
filter.setAuthenticationFailureHandler(failureHandler);
//filter.setAuthenticationFailureHandler(failureHandler);
return filter;
}
and add this as well
http.authorizeRequests().and()
.addFilterAt(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
I don't want the filter to exectue when calls are made to /login and /signup endpoints, but doFilter still gets hit even when a call is made to these two endpoints.
While I can't explain the relationship betweeh the two configure() methods you've written, you can remove configure(WebSecurity) and include all path expressions in configure(HttpSecurity), like this:
.authorizeRequests()
.antMatchers("/login", "/signup")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated();
attemptAuthentication, successfulAuthentication and unsuccessfulAuthentication don't get hit, but doFilter does. Why? why these methods are not called ?
AbstractAuthenticationProcessingFilter.doFilter() would normally invoke your overridden attemptAuthentication() (and other methods, as appropriate). Your overridden implementation effectively turns this filter into a "no op" by handing the request down to the remainder of the filter chain unconditionally.
Probably too late, however, I've given this answer to a similar question, so it can also be useful here:
Your custom implementation extends the UsernamePasswordAuthenticationFilter (which in its turn extends the AbstractAuthenticationProcessingFilter). The UsernamePasswordAuthenticationFilter, by default, is used for .formLogin authentication, handling the default AntRequestMatcher "/login". If you use a different protected endpoint, the filter's attemptAuthentication() method never gets action. So, if you want to use a different matcher (a different protected endpoint), you have to override the default AntRequestMatcher. For instance, you can do so within your custom filter constructor, by using something like that:
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/auth/signin", "GET"));

Spring Boot Security: Exception handling with custom authentication filters

I'm using Spring Boot + Spring Security (java config).
My question is the old one, but all info which I've found is partially outdated and mostly contains xml-config (which difficult or even impossible to adapt some time)
I'm trying to do stateless authentication with a token (which doesn't stored on server side). Long story short - it is a simple analogue for JSON Web Tokens authentication format.
I'm using two custom filters before default one:
TokenizedUsernamePasswordAuthenticationFilter which creates token after
successful authentication on entry point ("/myApp/login")
TokenAuthenticationFilter which tries to authenticate the user using token (if provided) for all restricted URLs.
I do not understand how properly handle custom exceptions(with custom message or redirect) if I want some...
Exceptions in filters are not relevant to exceptions in controllers, so they will not be handled by same handlers...
If I've understood right, I can not use
.formLogin()
.defaultSuccessUrl("...")
.failureUrl("...")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAthenticationFailureHandler)
to customize exceptions, because I use custom filters...
So what the way to do it?
My config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and() .anonymous()
.and() .authorizeRequests()
.antMatchers("/").permitAll()
...
.antMatchers(HttpMethod.POST, "/login").permitAll()
.and()
.addFilterBefore(new TokenizedUsernamePasswordAuthenticationFilter("/login",...), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new TokenAuthenticationFilter(...), UsernamePasswordAuthenticationFilter.class)
}
We can set AuthenticationSuccessHandler and AuthenticationFailureHandler in your custom filter as well.
Well in your case,
// Constructor of TokenizedUsernamePasswordAuthenticationFilter class
public TokenizedUsernamePasswordAuthenticationFilter(String path, AuthenticationSuccessHandler successHandler, AuthenticationFailureHandler failureHandler) {
setAuthenticationSuccessHandler(successHandler);
setAuthenticationFailureHandler(failureHandler);
}
Now to use these handlers just invoke onAuthenticationSuccess() or onAuthenticationFailure() methods.
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authentication) throws IOException, ServletException {
getSuccessHandler().onAuthenticationSuccess(request, response, authentication);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException failed)
throws IOException, ServletException {
getFailureHandler().onAuthenticationFailure(request, response, failed);
}
You can create your custom authentication handler classes to handle the success or failure cases. For example,
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Authentication authentication)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authentication);
// Do your stuff, eg. Set token in response header, etc.
}
}
Now for handling the exception,
public class LoginFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e)
throws IOException, ServletException {
String errorMessage = ExceptionUtils.getMessage(e);
sendError(httpServletResponse, HttpServletResponse.SC_UNAUTHORIZED, errorMessage, e);
}
private void sendError(HttpServletResponse response, int code, String message, Exception e) throws IOException {
SecurityContextHolder.clearContext();
Response<String> exceptionResponse =
new Response<>(Response.STATUES_FAILURE, message, ExceptionUtils.getStackTrace(e));
exceptionResponse.send(response, code);
}
}
My custom response class to generate desired JSON response,
public class Response<T> {
public static final String STATUES_SUCCESS = "success";
public static final String STATUES_FAILURE = "failure";
private String status;
private String message;
private T data;
private static final Logger logger = Logger.getLogger(Response.class);
public Response(String status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
public String getStatus() {
return status;
}
public String getMessage() {
return message;
}
public T getData() {
return data;
}
public String toJson() throws JsonProcessingException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
try {
return ow.writeValueAsString(this);
} catch (JsonProcessingException e) {
logger.error(e.getLocalizedMessage());
throw e;
}
}
public void send(HttpServletResponse response, int code) throws IOException {
response.setStatus(code);
response.setContentType("application/json");
String errorMessage;
errorMessage = toJson();
response.getWriter().println(errorMessage);
response.getWriter().flush();
}
}
I hope this helps.

Categories