I have a customized spring AuthenticationProvider class but try to intercept the HTTPServletRequest and HTTPServletResponse within the loadUserDetails method.
#Component("darnGoodAuthenticaionProvider")
public class DarnGoodAuthenticaionProvider
extends HandlerInterceptorAdapter
implements AuthenticationUserDetailsService {
private HttpServletRequest request;
private HttpServletResponse response;
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
this.request = request;
this.response = response;
// we don't want anything falling here
return true;
}
#Override
public UserDetails loadUserDetails(Authentication token)throws
UsernameNotFoundException{
.......
}
}
I know the preHandler method from HandlerIntercepterAdapter is capable to the job but how can I be sure that the preHandler method is called prior to loadUserDetails, so that I can get the request and response prepared?
Thanks
On a servlet container, each request will be handled from the moment the request is received until the response is returned by only one thread (request == current thread).
So it's a matter of putting a servlet filter BEFORE the spring security filter chain (with the filter-mapping element above the filter-mapping of spring security), and storing the request and response in the thread using a ThreadLocal variable - see also this answer.
Then on the DarnGoodAuthenticaionProvider access the request using a static method RequestResponseHolder.getRequest().
web.xml config:
<filter>
<filter-name>saveRequestResponseFilter</filter-name>
<filter-class>sample.save.request.filter.SaveRequestResponseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>saveRequestResponseFilter</filter-name>
<url-pattern>/mobilews/*</url-pattern>
</filter-mapping>
Filter to save the request response in the thread:
public class SaveRequestResponseFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
RequestResponseHolder.setRequestResponse(req,resp);
try {
chain.doFilter(request, response);
}
finally {
RequestResponseHolder.clear();
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
...
}
#Override
public void destroy() {
...
}
}
Request/Response holder:
public class RequestResponseHolder {
private static ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<HttpServletRequest>();
private static ThreadLocal<HttpServletResponse> responseHolder = new ThreadLocal<HttpServletResponse>();
public static void setRequestResponse(HttpServletRequest request, HttpServletResponse response) {
requestHolder.set(request);
responseHolder.set(response);
}
public static HttpServletRequest getServletRequest(){
return requestHolder.get();
}
public static HttpServletResponse getServletResponse() {
return responseHolder.get();
}
public static void clear() {
requestHolder.remove();
responseHolder.remove();
}
}
Obtaining the request from DarnGoodAuthenticaionProvider:
HttpServletRequest req = RequestResponseHolder.getServletRequest();
Related
I can't update a header inside my interceptor before it gets to my controller, through the interceptor I would like to modify an already present header
public class MyInterceptor implements Filter {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse res = (HttpServletResponse) servletResponse;
//something
//myHeaders is still present when send a request
req.setAttribute("myHeaders","someValue");
chain.doFilter(req, rest);
}
}
So that inside the controller I can get the modified header:
#RestController
#RequestMapping("/")
public class FooClass{
#Autowired
private Service service;
#GetMapping("/foo")
public ResponseEntity<Void> fooApi(
#RequestHeader(value = "myHeaders") String myHeaders,
) {
service.doSomething(myHeaders);
return ResponseEntity.ok().build();
}
}
How could I do? I tried to do some research but failed.
In your Filter you can create an anonymous subclass of HttpServletRequestWrapper, override the method public String getHeader(String name) so that it returns a specific value for the header name you care about (and delegates to super.getHeader(String) if it's not the header name you care about).
Something like this:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(servletRequest) {
#Override
public String getHeader(String name) {
if ("myHeader".equalsIgnoreCase(name)) {
return "Some value";
}
return super.getHeader(name);
}
};
chain.doFilter(requestWrapper, response);
}
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);
}
}
I'm currently implementing audit trail in my project, I tried using HandlerInterceptor and it seems it won't work in my project, so i looked for another way and I discovered that it's possible with OncePerRequestFilter.
Here's the code of my OncePerRequestFilter class:
#Component
#Order
public class LogFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String method = request.getMethod();
String username = SecurityContextHolder.getContext().getAuthentication().getName();
String url = request.getRequestURL().toString();
// Log the info you need
// ...
filterChain.doFilter(request, response);
}
}
The only problem so far that I see with my current configuration of OncePerRequestFilter is it also includes the resources such as css / javascripts.
example these links will be also go to the filter:
http://localhost:8000/project/css/style.css
http://localhost:8000/project/3277a64fcca0dbde907d8684aed8f170.png
http://localhost:8000/project/js/script.js.map
What i want is to filter only the controller request mappings, and ignore the resources
example:
http://localhost:8000/project/accounts/client-users
http://localhost:8000/project/accounts
This code is a workaround to ignore resource file. not sure if it's the best practice tho.
#Component
#Order
public class LogFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String method = request.getMethod();
String username = SecurityContextHolder.getContext().getAuthentication().getName();
String url = request.getRequestURL().toString();
filterChain.doFilter(request, response);
}
protected boolean shouldNotFilter(HttpServletRequest request)
throws ServletException {
String url = request.getRequestURL().toString();
return isResourceUrl(url);
}
private boolean isResourceUrl(String url) {
boolean isResourceUrl = false;
List<String> resourceRequests = Arrays.asList(
"/css/", "/js/", "/scss/", "/fonts/", "/emails/",
".css", ".js", ".scss", ".eot", ".svg", ".ttf", ".woff", ".otf", ".ico", ".png");
for (String resourceRequest : resourceRequests) {
if (url.contains(resourceRequest)) {
isResourceUrl = true;
}
}
return isResourceUrl;
}
}
Use something like this:
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(
"/example/docs",
"/swagger-resources/**",
"/swagger-ui.html");
}
I am building a mobile app and Restful API, I want the user of the app to be able to do GET what ever resources he want without Authentication. But if he want to do POST he have to enter his username and pass.
I already made a HTTP basic Authentication by putting a filter in web.xml.
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>org.service.RestAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/webapi/*</url-pattern>
</filter-mapping>
and there are the classes
public class AuthenticationService {
ClientsService s = new ClientsService();
public boolean authenticate(String authCredentials) {
if (null == authCredentials)
return false;
// header value format will be "Basic encodedstring" for Basic
// authentication. Example "Basic YWRtaW46YWRtaW4="
final String encodedUserPassword = authCredentials.replaceFirst("Basic"
+ " ", "");
String usernameAndPassword = null;
try {
byte[] decodedBytes = Base64.decode(
encodedUserPassword);
usernameAndPassword = new String(decodedBytes, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
final StringTokenizer tokenizer = new StringTokenizer(
usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
boolean authenticationStatus =s.auth(username, password);
return authenticationStatus;
}
}
and the filter
public class RestAuthenticationFilter implements javax.servlet.Filter {
public static final String AUTHENTICATION_HEADER = "Authorization";
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
// better injected
AuthenticationService authenticationService = new AuthenticationService();
boolean authenticationStatus = authenticationService
.authenticate(authCredentials);
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
what i need to know is : how to pass the username and password or maybe just the id of the client to the methods of Restful after the Authentication.
A solution different to my comment: you can look for the HTTP request method and then make a decision if you call a authentication method or not:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filter) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authCredentials = httpServletRequest
.getHeader(AUTHENTICATION_HEADER);
boolean authenticationStatus;
// check request method
if (((HttpServletRequest).request).getMethod().equals("GET")) {
authenticationStatus=true;
} else {
// better injected
AuthenticationService authenticationService =
new AuthenticationService();
authenticationStatus = authenticationService
.authenticate(authCredentials);
}
if (authenticationStatus) {
filter.doFilter(request, response);
} else {
if (response instanceof HttpServletResponse) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse
.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
}
Update:
With JAX-RS a possible solution for obtaining more request information may look like this:
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
#GET
#Path("offers")
#Produces("application/xml")
public YourList getOffers(#Context HttpServletRequest request)
{
System.out.println("request to "+request.getRequestURI()+" , Auth: "+request.getHeader(AUTHENTICATION_HEADER));
// more stuff for obtaining data
}
I am using Dropwizard and I would like to wrap a request object from within a filter, and gain access to that instance from my resource class
e.g. do the following
Filter.java
public class ServiceRequestExtractionFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
ProxyResourceRequest requestWrapper = new RequestWrapper(httpRequest, svcRequestData);
chain.doFilter(requestWrapper, response);
}
}
...
Resource.java
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public class DemoResource {
#GET
#Timed
public Response get(#Context UriInfo uriInfo, #Context RequestWrapper request) {
...
this doesn't work nor do I have any reason to expect it to, but it seemed like a good way to explain what I was trying to do.
Try this ContainerFilter documented here: http://dropwizard.io/manual/core.html#jersey-filters
public class DateNotSpecifiedFilter implements ContainerRequestFilter {
#Context ExtendedUriInfo extendedUriInfo;
#Override
public ContainerRequest filter(ContainerRequest request) {
boolean methodNeedsDateHeader = extendedUriInfo.getMatchedMethod().isAnnotationPresent(DateRequired.class);
String dateHeader = request.getHeaderValue(HttpHeaders.DATE);
if (methodNeedsDateHeader && dateHeader == null) {
Exception cause = new IllegalArgumentException("Date Header was not specified");
throw new WebApplicationException(cause, Response.Status.BAD_REQUEST);
} else {
return request;
}
}
}
Just add this in you application run():
environment.jersey().getResourceConfig().getContainerRequestFilters().add(new DateNotSpecifiedFilter());