Spring boot: How to set & read cookie - spring-boot

I am trying to set the cookie in the response after login & I want to read that cookie on every further rest api calls.. I tried the code like below but I am not getting the cookie value.. please help me.. thanks in advance..
#RequestMapping(value = "/login", method = RequestMethod.POST, consumes = "text/plain")
public String setCookie(HttpServletRequest request, HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException, ServiceException
{
response.addCookie(new Cookie("token", generateToken()));
return "login success";
}
#RequestMapping(value = "/getResource", method = RequestMethod.POST, consumes = "text/plain")
public String getCookie(HttpServletRequest request, HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException, ServiceException
{
Cookie[] cookies = request.getCookies();
if (cookies != null) {
Arrays.stream(cookies)
.forEach(c -> System.out.println(c.getName() + "=" + c.getValue()));
}
return "resource list";
}

Set cookie:
Cookie cookie= new Cookie("userName", authentication.getName());
response.addCookie(cookie);
Use Cookie Value:
public String hello(Model model, HttpServletRequest request, HttpServletResponse response,#CookieValue("userName") String usernameCookie) {
console.log(usernameCookie);
}
hope this helps

Related

Getting blocked by CORS policy despite using a Filter

I have this SimpleCORSFilter:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCORSFilter implements Filter {
#Override
public void init(FilterConfig fc) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "PATCH,POST,GET,OPTIONS,DELETE,PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, resp);
}
}
#Override
public void destroy() {
}
}
Everything works fine exceptn this one endpoint where I create my own ResponseEntity object in order to return paged content:
#RequestMapping(value = ROOT + "/{businessId}/reviews", method = RequestMethod.GET)
public #ResponseBody HttpEntity<PagedResources<MenuReviewDto>> getAll(
#PathVariable(name = "businessId") Long businessId,
#RequestParam(value = "page", defaultValue = "0") Integer page,
#RequestParam(value = "size", defaultValue = "10") Integer size,
Sort sort,
PagedResourcesAssembler assembler
) {
Pageable pageable = PageRequest.of(page, size, sort);
Page<ReviewDto> reviews = this.reviewService.getAll(businessId, pageable);
PagedResources<ReviewDto> pagedResources = assembler.toResource(reviews);
return new ResponseEntity<>(pagedResources, HttpStatus.OK);
}
It's the only request that currently fails giving me the typical error:
Access to XMLHttpRequest at 'https://192.168.1.144:8443/r/api/v1/admin/businesses/3/reviews?page=0&sort=createdAt,desc&size=5' from origin 'https://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The weird thing:
When debugging during this request, I can see that doFilter() of my SimpleCORSFilter is getting called. So the headers should be added?!
Ok, this was fast. And I post this here because it blows my mind a little.
I found this in the log output:
2019-08-1...ExceptionResolver : Resolved [org...Exception: Could not write JSON: ..
yadda, yadda, yadda ..
.. java.util.ArrayList[0]->mahlzeit.shared.review.ReviewRatingDto["isCut"])]
Now, let's see what isCut is:
public class ReviewRatingDto {
private Boolean isCut;
// ..
public void setIsCut(boolean isCut) {
this.isCut = isCut;
}
public boolean getIsCut() {
return isCut;
}
}
The issue: It's the getter which returns boolean instead of Boolean. This was of course lazyily auto-generated code using my IDE of choice and ultimateively my own mistake but be aware that the resulting error might lead you to question your CORS settings ..

How to store access token in cookies in OAuth2?

In my current project, I am using OAuth2 for token-based authentication in order to access the Rest APIs, but these tokens are readable by js. Because of this and a couple of other reasons I wanted to store the access token in cookies.
I have gone through the internet and could not find a way to put tokens in cookies. Can someone please help me with this?
Finally, found a solution for this. I have created a /login API where I am setting access token in cookies.
#PostMapping(consumes = "application/json")
public ResponseEntity<?> login(#RequestBody LoginRequest loginRequest,
HttpServletResponse httpResponse) throws Exception {
ResponseEntity<?> result = null;
try {
String url = UriComponentsBuilder.fromHttpUrl(environment.getProperty("oauth.token.url"))
.queryParam("username", loginRequest.getUsername())
.queryParam("password", loginRequest.getPassword())
.queryParam("grant_type", OauthConstants.GRANT_TYPE_PASSWORD)
.toUriString();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add(AppConstants.AUTHORIZATION_HEADER, AppConstants.AUTH_HEADER_CLIENT_DEFAULT);
HttpEntity<String> httpEntity = new HttpEntity<>(headers);
ResponseEntity<HashMap> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, HashMap.class);
Map<String, Object> authMap = response.getBody();
logger.info("Adding cookies");
String accessToken = (String) authMap.get(AppConstants.ACCESS_TOKEN);
String refreshToken = (String)authMap.get(AppConstants.REFRESH_TOKEN);
List<Cookie> cookies = new ArrayList<>();
cookies.add(newAppCookie(AppConstants.ACCESS_TOKEN, accessToken));
cookies.add(newAppCookie(AppConstants.REFRESH_TOKEN, refreshToken));
cookies.stream().forEach(c -> httpResponse.addCookie(c));
logger.info("Cookies added successfully");
result = ResponseEntity.ok(authMap);
} catch (HttpClientErrorException hex) {
logger.error("HttpClientErrorException occurred in login(): ", hex);
result = new ResponseEntity<>(hex.getResponseBodyAsString(),
HttpStatus.UNAUTHORIZED);
} catch (Exception e) {
logger.error("Exception occurred in login(): ", e);
throw e;
}
return result;
And after user logs in, for every API request to server a Filter is applied to check the access token in the cookies is valid or not as shown below.
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationOAuthFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
#Autowired
private Environment environment;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getRequestURI().equals("/oauth/token")||
httpRequest.getRequestURI().equals("/login")) {
chain.doFilter(request, response);
return;
}
Cookie[] cookies = httpRequest.getCookies();
if (cookies == null) {
logger.info("No Cookies found");
chain.doFilter(request, response);
return;
}
Map<String,String> cookiesMap = Arrays.asList(cookies).stream().collect(Collectors.toMap(Cookie::getName, Cookie::getValue));
if (!cookiesMap.containsKey(AppConstants.ACCESS_TOKEN)) {
logger.info("No Access token found in cookie");
chain.doFilter(request, response);
return;
}
ApplicationRequestWrapper mutableRequest = new ApplicationRequestWrapper(httpRequest);
mutableRequest.putHeader("Authorization","Bearer "+ cookiesMap.get(AppConstants.ACCESS_TOKEN));
logger.info("Access token found in cookie");
chain.doFilter(mutableRequest, response);
}

How do I extract client data from the httpheaders?

I have a login post request.
#RequestMapping(value = EWPRestContants.DO_LOGIN, method = RequestMethod.POST, consumes=MediaType.APPLICATION_XML_VALUE,produces=MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> doLogin(#RequestBody Loginrequest logReq,#RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse httpResponse) throws Exception {
//........
}
I want to extract data from the header. Is there an API to do so?
Suppose my header contains customer msIsdn number and name. How do I fetch those details. getFirst() is used to get the user-agent details or the start line only.
This is the answer.
String id= headers.getFirst("ID");
like
#RequestHeader(value="User-Agent", defaultValue="foo") String userAgent
#RequestMapping(value = EWPRestContants.DO_LOGIN, method = RequestMethod.POST, consumes=MediaType.APPLICATION_XML_VALUE,produces=MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> doLogin(#RequestBody Loginrequest logReq,#RequestHeader(value="User-Agent", defaultValue="foo") String userAgent,#RequestHeader(value="Accept-Language") String acceptLanguage, HttpServletRequest request, HttpServletResponse httpResponse) throws Exception {
//........
}
or from
#RequestMapping(value = EWPRestContants.DO_LOGIN, method = RequestMethod.POST, consumes=MediaType.APPLICATION_XML_VALUE,produces=MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> doLogin(#RequestBody Loginrequest logReq,#RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse httpResponse) throws Exception {
String userAgent = headers.getFirst(HttpHeaders.USER_AGENT);
}

HTTP redirect: 301 (permanent) vs. 302 (temporary) in Spring

I want to make a 301 redirect in Spring, So here the piece of code I use
#RequestMapping(value = { "/devices" } , method = RequestMethod.GET)
private String initGetForm(#ModelAttribute("searchForm") final SearchForm searchForm,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response,
Model model, Locale locale) throws Exception {
String newUrl = "/devices/en";
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", newUrl);
response.setHeader("Connection", "close");
return "redirect:" + newUrl;
}
But checking the IE Developer Tools I got this Status 302 Moved Temporarily !
Spring is resetting your response headers when it handles the redirection since you are returning a logical view name with a special redirect prefix.If you want to manually set the headers handle the response yourself without using Spring view resolution. Change your code as follows
#RequestMapping(value = { "/devices" } , method = RequestMethod.GET)
private void initGetForm(#ModelAttribute("searchForm") final SearchForm searchForm,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response,
Model model, Locale locale) throws Exception {
String newUrl = request.getContextPath() + "/devices/en";
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", newUrl);
response.setHeader("Connection", "close");
}
You can use RedirectView with TEMPORARY_REDIRECT status.
#RequestMapping(value = { "/devices" } , method = RequestMethod.GET)
private ModelAndView initGetForm(#ModelAttribute("searchForm") final SearchForm searchForm,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response,
Model model, Locale locale) throws Exception {
....
RedirectView redirectView = new RedirectView(url);
redirectView.setStatusCode(HttpStatus.TEMPORARY_REDIRECT);
return new ModelAndView(redirectView);
}

Spring & Security: limit uploads to authenticated users

I'm facing a security problem regarding file uploads.
How do I limit file uploads to specific user roles?
I'm using #PreAuthorize("hasRole('USER')"), but it is uploading the file first and then checking the role. You can especially see this when file upload size is exceeded. User will get an upload size exceeded exception instead of redirecting to the login-form.
This is how my controller looks like:
#Controller
#PreAuthorize("hasRole('USER')")
#Secured("ROLE_USER") // added this just to see if it makes a difference, it doesn't
#RequestMapping(value = "/self/upload", produces = "application/json")
public class JsonUserSelfUpload {
...
#RequestMapping(value = "", method = RequestMethod.POST, consumes="multipart/form-data")
public ModelAndView fileUpload(
#RequestParam(value = "file", required = true) MultipartFile inputFile,
#RequestParam(value = "param1", defaultValue = "") String type,
HttpServletResponse response
) throws Exception {
...
}
}
Anyone know how to secure file uploads to specific roles?
Edit, to be more specific:
I want to reject uploads if user is not authenticated. By reject I mean, close connection before the upload actually finishes. Not sure if spring is capable in doing this or I'd need a filter to reject uploads (multipart).
Update:
Tried with a filter with no success either.
Seems like one has no way to close the connection.
This is what my filter looks like:
public class RestrictUploadFilter implements Filter{
#Override
public void init(FilterConfig arg0) throws ServletException {
}
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String contentType = request.getContentType();
if (HttpMethods.POST.equals(request.getMethod()) && contentType != null && contentType.toLowerCase().indexOf("multipart/form-data") > -1) {
UserSession session = SpringHelper.getUserSession();
if (session != null && session.getRoles().contains(UserRole.USER)) {
// user is allowed to upload
chain.doFilter(req, res);
} else {
// access denied
response.setStatus(HttpStatus.FORBIDDEN_403);
response.setHeader("Connection", "close");
response.flushBuffer();
}
} else {
chain.doFilter(req, res);
}
}
}

Resources