I understand that there is always a token that basically glued to the request, now i would like to get retrieve a token and stuck, would appreciate some thoughts. Thank you
#GetMapping("/current")
public ResponseEntity<UserDto> getCurrent() {
return ResponseEntity.ok(something);
}
in the method body i would probably implement another service where constructor is taking token as an argument for example then does some equality checks.
In your controller accept HttpServletRequest. then you can extract any header from it.
#GetMapping("/current")
public ResponseEntity<UserDto> getCurrent(HttpServletRequest request) {
String token = request.getHeader("Authorization");
return ResponseEntity.ok(something);
}
If you don't want to take HttpServletRequest from controller you can Autowire it like this
#Autowired
private HttpServletRequest request;
or do the following
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Your question is quite hard to understand. Your "token" could be anything.
I'll try to answer your question in general:
For example, you have some option which is available only for registered users. If you are using jwt security, you generate tokens for users and "glue" them to request/response headers
For example (that's my current jUnit test):
protected MockHttpServletRequestBuilder getAuth(String url) {
return MockMvcRequestBuilders.get(url).header("Authorization", this.token);
}
And token is generated using jwt security, and after it's generated I can "glue" it to the headers. And I can just check what's in header and get token value.
So to check or get this token, you have to check what's in your header, just that simple :)
That's very general example. But the main idea is:
If you add something - you can get something. But if you haven't generated a token - you wouldn't get one "by default", because there is none, java do not generate tokens to any request. It's something you must first create first.
If you are uncommon with what I am talking about - please start from here:
https://jwt.io/introduction/
Related
I'm new to REST and I'm making simple REST application with users and articles. I wonder what's the difference between two samples below:
#GetMapping("/user/{id}")
public User getUserById(PathVariable("id") String id) {
.....
return userService.getUserById();
}
and
#GetMapping("/user/{id}")
public ResponseEntity<User> getUserById(PathVariable("id") String id) {
.....
return new ResponseEntity<> ....
}
Which one is better to use?
And what's the main difference between two of them?
ResponseEntity is containing the entire HTTP response that returns as a response which gives the flexibility to add headers, change status code and do similar things to the response.
Another hand sending PJO class directly like returning users in the example is somewhat similar to return ResponseEntity.ok(user) which responded to user details successfully to the user. But the ability to change headers, status codes is not available if you return PJO directly.
It is good to use ResponseEntity over PJO when there is a scenario you need to change the headers or you need to change status according to the result.
eg: show not found when there is no data you can return ResponseEntity.status(404).body(<-body->).
at least in the Response Entity, you can set the http status and you can use ResponseEntity<?> where ? is generic any object its very convenient to use
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 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 trying to call a service which has CSRF enabled and all it's endpoints are configured to request authentication header from the user.
I'm using Spring RestTemplate as follows:
ResponseEntity<String> responseEntity = getRestTemplate().exchange(
"localhost:9090/",
"HEAD",
entity,
String.class);
return responseEntity.getBody();
However, I'm not able to read the Headers from the response as I'm getting HTTP 401 error.
My workaround is to read the token from the exception that RestTemplate throws HttpClientErrorException. Like this:
exception.getResponseHeaders().get("Set-Cookie");
for (String header : headers) {
if (header.startsWith("XSRF-TOKEN")) {
token = header.split("=")[1];
break;
}
}
Is there any way to get XSRF-TOKEN token with out relying on reading it from the exception?
You are not getting an exception when accessing with GET method. Hence, I would create a get endpoint for retrieving the token and then use it for next POST calls.
Hope that approach makes sense.
the csrf only blocks requests of type post, put, delete ... that is, the get is free, therefore in order to obtain the token, first you have to make a request to a get method and extract the token from there that you would use to the next requests.
in case the token is not generated, add this to the configure of your security configuration:
http.csrf (). csrfTokenRepository (CookieCrsfTokenRepository.withHttpOnlyFalse) .any () ........
XSRF-TOKEN following spring specification is marker for header by default. So you should try get it in this way:
List tokenList = responseEntity.getHeaders().get("XSRF-TOKEN");
This collection consist of single element as usual, so first element should be your token.
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.