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.
Related
I have a simple blog webpage with a lot of links. They are all using GetMapping. Therefore whenever I have a bot try to access those pages using POST I get an error saying
Request method 'Post' not supported.
I understand that this is caused because I use #GetMapping, and if I switch to #RequestMapping then everything will be fine. However I do not want to allow anyone to access my blog with POST. The only page that should be POST is /contact/message except that page everything should be accessed through GET.
So I have two questions:
How do I enforce people to use GET only.
How do I catch attempts to use POST and redirect them to /error?
Sidenote: I do not use spring security, there is no logging in or anything that is hidden behind an account. Also most of my mappings are using regex. Do not know if this info is of any help or not.
#GetMapping(value = {
"", "{page:^[1-9][0-9]*$}", "{section:^\\d*[a-zA-Z][a-zA-Z0-9]*[^.]+$}",
"{section:^\\d*[a-zA-Z][a-zA-Z0-9]*[^.]+$}/{page:^[1-9][0-9]*$}"})
You can add a filter in your application:
public class Tfil implements Filter {
#Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (!"GET".equals(req.getMethod())) {
//redirect or error page
HttpServletResponse res = (HttpServletResponse) servletResponse;
//here redirect
res.sendRedirect("redirect url");
//or return 404 code
res.setStatus(404);
}
filterChain.doFilter(servletRequest,servletResponse);
}
}
TLDR: My method requires 2 redirects/forwards to work (1 for authentication and 1 to serve the jsp page). How can I resolve both redirects/forwards (or make it a non-requirement) so as to not run into the error, java.lang.IllegalStateException: Cannot forward after response has been committed.
For more context:
I have a java servlet with a method that looks something like the following:
#GET
#Path("/test")
#Authenticate
public Viewable test(#Context HttpServletRequest request, #Context HttpServletResponse response) {
Map<String, Object> model = createModel();
return new Viewable("/somePath/jspFile", model);
}
The #Authenticate annotation intercepts the call to do some Open ID Connect type authentication which results in the user being forwarded to a different server for all authentication needs. If the user is authenticated, they are redirected back to my application.
However, when hitting the url for this method, I am getting java.lang.IllegalStateException: Cannot forward after response has been committed. I don't know too much about using this Viewable class, but based on the fact that I don't run into that error when returning String/void/whatever else, I assume returning a new Viewable needs to do some forwarding that results in the user seeing the jsp page.
I've read the main SO post about this error, but I am unsure how to apply the fixes to my current problem. For example, I don't know how I would apply something like the following fix:
protected void doPost() {
if (someCondition) {
sendRedirect();
} else {
forward();
}
}
The fix assumes that I can I can either redirect OR forward, but my current method needs a redirect for authentication AND a forward/redirect to serve the jsp page. Maybe there's an obvious fix I'm missing that doesn't require a complete rehaul of the current code?
Edit: It would be nice if I could check if the user was authenticated first, but I assume using this annotation at all automatically entails an initial redirect
Edit: It looks like the user is redirected for the initial login authentication, but does not need to be redirected again after being authenticated once due to SSO
Ok based on some preliminary testing, it seems like the following solution has worked for me:
Check if the user has already been authenticated
Return a Response rather than a Viewable.
Since the user only needs to be redirected the first time for authentication, I can return an empty/meaningless response as a placeholder. And then once the user has been authenticated and is returned to my app, I can return a Viewable wrapped in a Response object.
So the code would look something like the following:
#GET
#Path("/test")
#Authenticate
public Response test(#Context HttpServletRequest request, #Context HttpServletResponse
response) {
Map<String, Object> model = createModel();
if (userIsAuthenticated()) {
return Response.status(401).build();
} else {
return Response.ok(new Viewable("/somePath/jspFile", model)).build();
}
}
I have a theoretical doubt on CORS implementation.
A way to enable cross-origin requests is to set a specific Header on the response:
private void setAccessControlHeaders(HttpServletResponse resp) {
resp.setHeader("Access-Control-Allow-Origin", "http://www.allowed.domain.com");
resp.setHeader("Access-Control-Allow-Methods", "POST");
}
My question is: if I set the header in the response (which is at the end of the request-response chain), it means the request I receive is already processed, side effects are caused, and then the program decides if the response must be sent back or not, based on the presence of this header in the response.
For example:
public class MyServlet extends HttpServlet {
//...
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Exception{
Order order = (Order) parseBodyRequest(req);
orderRepository.save(order); //if I check the allowed domains later, I can get serious side effects!
resp.setHeader("Access-Control-Allow-Origin","http://www.allowed.domain.com");
resp.getWriter().println("Order n."+ order.getId()+ "has been saved successfully!");
}
}
In the example above, the order is parsed and saved into the database before even checking if the domain from which the request comes is allowed or not.
This thing seems absurd, so how does it work in reality?
Try this article: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
In short: For requests that are able to alter user data, CORS specifies a preflight request that asks the destination server whether it would accept a request with a given method and set of headers. (eg POST and Content-type) without actually sending the request. The browser implements this transparently.
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.
I'm having a JavaEE Website running on a cloud-platform.
Now I want to use two types of authentications:
Is from an SSO-System, which is well integrated in the platfrom and works very nicely.
Is the problematic part: I want to authorize a user from 1) for the time of a session, and give him access to a more restricted resource.
Some details
I get the user and his data from 1).
The user first has to ask for permission to 2), which can be denyed or granted. A user gets authorization from a service, which is outside of the scope of his servlet.
For this purpose I pass a User-POJO (with the session of this user as a member) to a service.
If the service grants the rights to this user, it will set an attribute to the user session:
userSession.setAttribute("auth", "granted");
To restrict access to that resource I use a Filter:
#WebFilter("/supersecret/*")
public class NiceFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession();
// check
if (session.getAttribute("auth") != "granted")
// redirect to login
else
chain.doFilter(req, res);
}
//...
While this is currently working, I feel that my solution is very sloppy.
Altering the user-session outside the scope of a servlet seems to be bad practice.
Adding an attribute to the session for security-purposes is probably not a good idea?
I'd rather want to use standard JavaEE-mechanisms, but most of them are already used for auth-method 1), like declaring login-config in the web.xml.
Any ideas for a more robust solution to this problem?
Thanks in advance :)