I am learning how secure my endpoints, but everything i searched for contains pretty complicated examples, that didn't really answerd my question, and for now, just for the sake of this example project, i was looking for something simple.
My current solution is to make endpoints return like this:
return authenticate(request.headers) ? cityService.getCity() : utils.unauthenticatedResponse();
Where authenticate(request.headers) checks for token in header.
The thing i want to improve is to have that authenticate method run before every request to my endpoints (aside from login and register), so i can just return cityService.getCity(), and i won't have to make that check every time.
Will appreciate every answers, but please make it easy yo understand, since i am just a beginner.
Since you need to run the authenticate method before every request, you need to implement a Filter. It's pretty straightforward and you can get the steps and template to implement a filter here.
Every request to an endpoint will first pass through the filter (this is configurable), where you can have the authenticate method and then allow it further accordingly.
For starters, you can implement a filter like below:
#Component
public class AuthFilter implements Filter {
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
if(authenticate(req.getHeaders)){
chain.doFilter(request, response);
} else {
//else logic, ie throw some exception in case authenticate returns false
}
}
}
The advantages that this provides are :
You can implement multiple filters
You can provide Order/priority to filters
You can configure which endpoints need to pass through the filter and which ones do not.
You can use ContainerRequestFilter (if you are using Spring/Tomcat)
Every request coming to the server will go through this filter, so you can implement your code in it.
Related
I have a working #RestController component that yields API web endpoints.
This is one of those endpoints
#CrossOrigin
#GetMapping(API_VERSION + PLAYER + METHOD_FETCH + "/{uid:^[0-9]*$}")
public Player fetchPlayer(#PathVariable("uid") String uid) {
return mongoTemplate.findById(uid, Player.class);
}
Now when using my Vue.js App I call this endpoint. The problem is the axios http client library turns a get request that has authentication headers into a options request to probe the server for actual access.
Now I need to consume this options request and have it be enabled for CORS. I did the following therefore:
#RestController
#Log
#RequestMapping("/**")
public class AuthenticationEndpoint {
#CrossOrigin
#RequestMapping(method = RequestMethod.OPTIONS)
public void handleOptionRequest(){
log.info("option request handled");
}
}
I map it to every url so it "should" intercept every OPTIONS request. But it does not. When having a
GET http://{{host}}:80/api/v0.1/player/fetch/4607255831
Authorization: Basic MTIzNTM2NDMyNDphYmMxMjM=
The more specific API web endpoint is handled before the OPTIONS handler.
How can I actually put the OPTIONS handler before the others in Spring MVC?
I want it to act like an interceptor
OR
What is the best practise way to achieve the wanted behaviour? I kinda feel I am hacking around a better solution.
How can I actually put the OPTIONS handler before the others in Spring MVC? I want it to act like an interceptor.
Your can create a component a class that implements Filter interface and give it a High order :
#Component
#Order(1)
public class RequestInterceptor implements Filter {
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String httpMethod = req.getMethod();
if(HttpMethod.OPTIONS.name().equals(httpMethod)){
//do something here before reaching the method handler
}
chain.doFilter(request, response);
}
// other methods
}
Or you can extends OncePerRequestFilter.java and make the same check as above in the doFilterInternal method.
EDIT
If you want to control whether to proceed handling a giving request or not you can use HandlerInterceptor :
A HandlerInterceptor gets called before the appropriate HandlerAdapter
triggers the execution of the handler itself. This mechanism can be
used for a large field of preprocessing aspects, e.g. for
authorization checks, or common handler behavior like locale or theme
changes. Its main purpose is to allow for factoring out repetitive
handler code.
HandlerInterceptor is basically similar to a Servlet
Filter, but in contrast to the latter it just allows custom
pre-processing with the option of prohibiting the execution of the
handler itself, and custom post-processing. Filters are more powerful,
for example they allow for exchanging the request and response objects
that are handed down the chain. Note that a filter gets configured in
web.xml, a HandlerInterceptor in the application context.
#Comonent
public class LoggerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception{
// do checks and decide wether to complete or to stop here
// true if the execution chain should proceed with the next interceptor or the handler itself.
// Else, DispatcherServlet assumes that this interceptor has already dealt with the response itself.
return true;
}
// other methods
}
I am capturing URLs requested for my website. I need to see what all pages were requested from my website.
To achieve this I created a basic filter, and started logging page requests from there.
Now, this filter catches all the requests specific to a page.
For e.g. abc.com/page1, abc.com/resources/myjs.js, etc.
My problem is that for each page request, subsequent resources(js,css) are requested too. I want to capture only the relevant requests.
Right now, I check for patterns like /resources to ignore such requests, but I am looking for a more clean approach.
Also, will interceptors be more useful here?
I have seen filter patterns as well. But those are not useful, since I would have to create patterns for my filter.
If you want to capture the urls accessed from your website, you can configure spring boot to generate access logs in following way until you don't want more advance information:
server.tomcat.accesslog.directory=/logs/ #absolute directory path for log files\
server.tomcat.accesslog.enabled=false #Enable access log.
server.tomcat.accesslog.pattern=any_pattern # log string format pattern for access logs.
To perform any operation based on any request pattern, you can go ahead with filters.
I'm using filters for such requirements in following way:
public class CustomFilter extends OncePerRequestFilter{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String url = request.getRequestURI();
if (url.startsWith("/resources/")) {
//your business logic
}
filterChain.doFilter(request,response);
}
}
Intro
I have just asked another question which answered might lead to a solution, however there might be a better way to do that. The security is provided by spring-security.
Problem description
I store Users in DynamoDB. By following a certain flow - which isn't a case here - might be awarded with a special veryImportantUser=true parameter. I'd like to return custom status code 202 in this case, so front-end can show to such user certain view.
What I have:
I have MyAuthenticationSuccessHandler with the following onAuthentcationSuccess:
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
super.onAuthenticationSuccess(request, response, authentication);
MyUserDetails userDetails = (MyUserDetails)SecurityContexHolder.getContext().getAuthentication().getPrincipal();
if (userDetails.isVIP())
response.setStatus(HttpServletResponse.SC_ACCEPTED);
}
Problem:
I put a breakpoint in that method and it gets triggered. The response status is set to 202 but in my Postman I receive status 200. Can I handle it in other way then RequestHeaderAuthenticationFilter? I'd love to do that without struggling with xml configuration.
I'm trying to build an api with Google Cloud Endpoints.
As Cloud Endpoints does not provide authentication beside Googles own OAuth I try to build my own. Therefore I want to access the parameters provided for the API (for example #Named("token") token) inside a servlet filter.
Unfortunately I cannot find any of the provided information inside the httpRequest. Is that normal? Is there a possibility to access the parameters?
I would appreciate if someone could help me!
UPDATE:
With the infos from jirungaray I tried to build an authentication using headers but ran into the same problem. Used a REST-Client to send some headers as I could not figure out how to do this with the API Explorer. Inside my filter I try to access the token from the headers:
#Override
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader(Constants.AUTH_TOKEN);
...
chain.doFilter(request, response);
}
The reason why I try to do something like this, is that I'm using Guice for Dependency Injection and want my token to be injected inside another object.
With Guice I have the following Provider using the token to inject a FacebookClient (using the token) per request.
#Provides
public FacebookClient getFacebookClientProvider(#Named("fbToken") Provider<String> fbToken) {
return new DefaultFacebookClient(fbToken.get(), Version.VERSION_2_2);
}
As described in the Guice wiki (SevletModule) this uses a sevlet filter to get the information from the request.
Is there any solution to achieve this kind of DI with Cloud Endpoints?
Philip,
Yes, it does makes sense you are getting an empty request. Your endpoint calls are first handled by Google (they receive the API calls) and then those are processed and sent to a handler in your app. As this is all done in the background it's very easy to miss that your endpoints aren't actually getting the same request you sent, they get a completely different request sent from Google's infrastructure.
Even though your approach should work including tokens info in url makes them easier to sniff, even if you use SSL or encrypt your params the token is there in plain sight.
For what you are trying to achieve I recommend you include the token as a header in your request and retrieve that header by accessing the HTTPRequest directly on the endpoint, this is injected automatically if you include an HTTPServletRequest param in you endpoint method.
eg.
public APIResponse doSomething(SomeComplexRquestModel request,
HttpServletRequest rawRequest) {
}
If you still feel you should go with your original approach just comment and I'll help you debug the issue.
I need to change the serverName of the ServletRequest object in my Grails controller. I'm having trouble figuring out how to do this since the serverName is a read-only property.
The most correct thing to do is probably to set up a clever filter or redirect which "fixes" your request URL before your servlet even gets involved. I know nothing about how to do that; you should ask on serverfault.com if you want to do that.
In java, you can fake it by creating your own subclass of HttpServletRequestWrapper which provides setServerName() and overrides getServerName() while delegating all other methods to the superclass. You can then provide a filter which creates an instance of your request and sends that one down the chain.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
YourHttpServletRequest yourRequest =
new YourHttpServletRequest(request, newServerName);
chain.doFilter(yourRequest, response);
}
If I understand this correctly, CORS filter might help
I've used http://software.dzhuvinov.com/cors-filter.html in my previous project.
But you can also lookup on github for example https://github.com/eBay/cors-filter