Currently setting up http only cookie in a Spring boot project via configurations as follows.
This cookie is getting set correctly when ever I call following endpoint.
#Bean
public CookieSerializer defaultCookieSerializer() {
DefaultCookieSerializer cookie = new DefaultCookieSerializer();
cookie.setDomainNamePattern(".*");
cookie.setCookieName("my_cookie");
cookie.setUseSecureCookie(true);
cookie.setUseHttpOnlyCookie(true);
cookie.setCookieMaxAge(1200);
return cookie;
}
As can see, the cookie called my_cookie is being set for 2 mins.
In my controller within same project, I have the following controller method.
In the event I enter the error block, I wish to delete the cookie called my_cookie. How can I do that?
This is the closest question I found for this but is not the same case considering I set it via configurations.
https://stackoverflow.com/questions/9821919/delete-cookie-from-a-servlet-response
#PostMapping(value = "/endpoint")
public List CustomResponse(
#RequestBody Request request,
) throws Exception {
CustomResponse response = null;
if (otherCookie != null) {
CustomResponse response = // perform some other rest request and get value from there
}
if (response == null) {
// I want to delete the cookie named `my_cookie` at this stage.
throw new CustomException('name');
}
return response;
}
To delete a cookie, set the Max-Age directive to 0 and unset its value. You must also pass the same other cookie properties you used to set it. Don't set the Max-Age directive value to -1. Otherwise, it will be treated as a session cookie by the browser.
// create a cookie
Cookie cookie = new Cookie("username", null);
cookie.setMaxAge(0);
cookie.setSecure(true);
cookie.setHttpOnly(true);
cookie.setPath("/");
//add cookie to response
response.addCookie(cookie);
For more, refer to the post by Dzone:
https://dzone.com/articles/how-to-use-cookies-in-spring-boot
Related
I'm falling into a problem this morning with a custom request between two application, what i need to do is to let application able to talk eachother with two Rest API cause i need to do some actions on the first application by the second. The two applications are developed with springboot.
Suppose to call this two applications admin and superadmin
superadmin send a request with a RestAPI and a customized header -> name = key value = 1234
admin recieve the request and first of all check if the header is present or not, after that the header is finded it can proceed to do all the task.
Here's the code that i've developed :
SUPERADMIN :
#PostMapping(value="/test_api_header")
public ResponseEntity<String> test_API(#RequestParam String url) {
RestTemplate template = new RestTemplate();
URI targetUrl = UriComponentsBuilder.fromUriString(url) // Build the base link
.path("/test_API") // Add path
.build() // Build the URL
.encode() // Encode any URI items that need to be encoded
.toUri(); // Convert to URI
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", "application/json");
headers.add("superadminKey", "123456abc");
// build the request
ResponseEntity<String> entity = template.exchange(targetUrl, HttpMethod.GET, new HttpEntity<String>(headers), String.class);
return entity;
}
ADMIN :
#Value("123456abc")
private String saKey;
#GetMapping(value = "/superadmin/test_API")
public String test_API(HttpServletRequest request) {
if (request.getHeader("superadminKey") == saKey) {
return "Finally";
} else {
return "Nothing to do, header not present";
}
}
The SUPERADMIN is able to communicate with the RESTApi in the ADMIN application, in fact on postman i received the answer : Nothing to do, header not present, but i really cannot be able to set that customized header in the superadmin request cause i cannot found it also on postman request in the section "headers".
I've seen that i could also create a customized API Key for this special case, but really don't know how it works, if someone could help me I would be very grateful!
I am trying to learn Spring Security right now and I have seen many different examples using this. I know what CSRF is and that Spring Security enables it by default.
The thing that I am curious about to know is this kind of customization.
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests(request -> {
request
.antMatchers("/login").permitAll()
.anyRequest()
....more code
What kind of customization does .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) this line and when it is appropriate to use it.
I would appreciate it if anyone can come with a simple explanation.
CSRF stands for Cross Site Request Forgery
It is one kind of token that is sent with the request to prevent the attacks. In order to use the Spring Security CSRF protection, we'll first need to make sure we use the proper HTTP methods for anything that modifies the state (PATCH, POST, PUT, and DELETE – not GET).
CSRF protection with Spring CookieCsrfTokenRepository works as follows:
The client makes a GET request to Server (Spring Boot Backend), e.g. request for the main page
Spring sends the response for GET request along with Set-cookie header which contains securely generated XSRF Token
The browser sets the cookie with XSRF Token
While sending a state-changing request (e.g. POST) the client (might be angular) copies the cookie value to the HTTP request header
The request is sent with both header and cookie (browser attaches the cookie automatically)
Spring compares the header and the cookie values, if they are the same the request is accepted, otherwise, 403 is returned to the client
The method withHttpOnlyFalse allows angular to read XSRF cookie. Make sure that Angular makes XHR request with withCreddentials flag set to true.
Code from CookieCsrfTokenRepository
#Override
public CsrfToken generateToken(HttpServletRequest request) {
return new DefaultCsrfToken(this.headerName, this.parameterName,
createNewToken());
}
#Override
public void saveToken(CsrfToken token, HttpServletRequest request,
HttpServletResponse response) {
String tokenValue = token == null ? "" : token.getToken();
Cookie cookie = new Cookie(this.cookieName, tokenValue);
cookie.setSecure(request.isSecure());
if (this.cookiePath != null && !this.cookiePath.isEmpty()) {
cookie.setPath(this.cookiePath);
} else {
cookie.setPath(this.getRequestContext(request));
}
if (token == null) {
cookie.setMaxAge(0);
}
else {
cookie.setMaxAge(-1);
}
cookie.setHttpOnly(cookieHttpOnly);
if (this.cookieDomain != null && !this.cookieDomain.isEmpty()) {
cookie.setDomain(this.cookieDomain);
}
response.addCookie(cookie);
}
#Override
public CsrfToken loadToken(HttpServletRequest request) {
Cookie cookie = WebUtils.getCookie(request, this.cookieName);
if (cookie == null) {
return null;
}
String token = cookie.getValue();
if (!StringUtils.hasLength(token)) {
return null;
}
return new DefaultCsrfToken(this.headerName, this.parameterName, token);
}
public static CookieCsrfTokenRepository withHttpOnlyFalse() {
CookieCsrfTokenRepository result = new CookieCsrfTokenRepository();
result.setCookieHttpOnly(false);
return result;
}
You may explore the methods here
You may find additional information about the httpOnly attribute of cookies here: https://www.cookiepro.com/knowledge/httponly-cookie/
Recently I started using Spark Java Framework (2.7.2) to create a lightweight web application. One of its requirements is that the application must be deployed to an Apache Tomcat Server 8.5.
I've managed to set things going, but I have not been able to set any custom cookie.
I have used the following method but none worked.
response.cookie("my_cookie", "value");
response.cookie("/path", "my_cookie", "value", -1, false, true);
It seems like tomcat is setting correctly the JSESSIONID cookie but I have no control over this cookie generation and I would like to generate a random and unique cookie, in order to be used for user authorization.
EDIT:
The control flow for setting the cookie is this
// In the main application
before("/*", AccessController.setSession);
// Method for setting an existing session
public static Filter setSession = (Request request, Response response) -> {
// If the user is not set in the session
if (!SessionUtil.hasSession(request)) {
// Obtain the cookie session ID
String sessionId = SessionUtil.getCookie(request);
System.out.println(sessionId);
// Obtain the user according to the session ID
User user = app.getUserFromSession(sessionId);
System.out.println(user != null);
// if does exists we set the session
if (user != null)
SessionUtil.setSession(request, user);
}
};
// Methods for the session
public static boolean hasSession(Request request) {
if (request.session().attribute("user") == null)
return false;
return true;
}
public static String getCookie(Request request) {
return request.cookie(COOKIE_NAME);
}
public static void setSession(Request request, User user) {
request.session().attribute("user", user);
}
This is called when a login is succesfull. Cookie is stored in the user database persisting sessions
public static void setSession(Response response, String cookie) {
response.cookie(COOKIE_NAME, cookie);
}
My question is: How can i set a cookie that does not already exist through a filter?
As i understand how filters work i can catch an incoming request before it reaches a given servlet, work on the request and pass it to the servlet. When the servlet than has generated the response i can catch the outgoing response and work with it again.
What i have done, is on the request determined that the specific cookie isin't present so i set a boolean value representing this. Than when the response returns from the servlet i add the specific cookie.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
ServletContext sc = filterConfig.getServletContext();
//Run when receiving the request from the client
boolean cookie = false;
System.out.println("Searching for the cookie (request)");//debug
Cookie[] cookies = httpRequest.getCookies();
if(cookies != null) {
for(int a = 0; a < cookies.length; a++) {
if(cookies[a].getName().equals("PositionCookie")) {
cookie = true;
}
}
}
chain.doFilter(httpRequest, httpResponse);
//Run when sending the response to the client
System.out.println("Determining to create cookie (response)");//Debug
if(!cookie) {
System.out.println("Creating 'PositionCookie' (response)");//debug
Cookie c = new Cookie("PositionCookie", "/test/data/string");
c.setPath("/");
c.setMaxAge(-1);
httpResponse.addCookie(c);
}
}
Since i just want this to be a session cookie i have given it a MaxAge of -1.
All the debug lines are activated and written to catalina.out so i know that new Cookie statement has been reached but a new cookie is not added to the browsers saved cookies. I dont have any browser settings that deny new cookies and i get the JSESSIONID cookie without problems.
Cookies are set in HTTP headers. The headers are the first thing that are written as part of the response. The Servlet container only buffers so much of the response before writing it (headers first) to the client. Once the headers have been written the response is considered to be committed. (The application can also force the commit of the response.) Once the response has been committed cookies cannot be added (since the HTTP headers have already been sent to the client).
I strongly suspect the response is committed by the time you try and add the cookie so the call is simply ignored.
The simple solution is to move the call to addCookie() to before the call to chain.doFilter(httpRequest, httpResponse);
I'm writing a simple app which logs onto my webservice and then downloads some information via SQL about the logged in user.
Currently I have a dependency on Google's Volley and can readily send off volleys, read the response, etc. However, the problem comes when ASP (backend) is creating a new session on each request, thereby clearing any set session variables.
I've tried to read the response back from the server to get the cookie, and then attach that as a header on any subsequent volley, but it's not working as expected. I need to figure out why my sessions aren't persisting.
INIT
String sSessionID = "";
My Volley call
String urlLoginAttempt = SERVER_ADDRESS + PAGE_LOGIN + "?u="+username+"&p="+pw;
Response.Listener responseListener = new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(ctx, "DEBUG\nVolley Session ID : " + sSessionID, Toast.LENGTH_SHORT).show();
}
};
StringRequest stringRequest = new StringRequest(Request.Method.POST, urlWithParams, responseListener, this.errorListener) {
//Add an extra cookie value header into the request, if cookie variable has been set
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
if (!sSessionID.equalsIgnoreCase("")) {
params.put("Cookie", sSessionID);
}
return params;
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
//Volley can automatically get the session ID
sSessionID = parseCookie(response.headers.get("Set-Cookie"));
return super.parseNetworkResponse(response);
}
public String parseCookie(String cookie) {
String[] splitCookie = cookie.split(";");
String[] splitSessionId = splitCookie[0].split("=");
String sCookie = splitSessionId[1];
return sCookie;
}
};
// Add the request to the RequestQueue.
netQ.add(stringRequest);
On first call, sSessionID is empty.
As the first response is received (a login attempt), the "set-Cookie" header is stripped, parsed, and saved into the local variable sSessionID
The actual response from the ASP backend is currently "1" or "0", depending on whether the correct username/password has been sent to the server. This works correctly. When the response is "1", the backend also sets some session variables
When I try and re-login, I step into the code and see that the session id (cookie) has been changed, and no set session variables exist. This happens even though all subsequent requests I add a "Cookie" header with that same sSessionID to try and persist the session.
An example sSessionID value would be the header value HTTP_COOKIE: ASPSESSIONIDXXXXXXX=IAOPCEODJPBJGJXXXXXXX; stripped down to IAOPCEODJPBJGJXXXXXXXX
Login.ASP Page
<%
//Attempt to set any session variable to begin a new session
session("userid")=-1
username=ucase(request.querystring("u"))
pw=request.querystring("p")
//for testing only:
if username="J" AND pw="pass" then
session("userid")="188473"
session("name")="Jammo"
...
response.write("1")
else
session("userid")=-1
reponse.write("0")
end if
%>
EDIT
A little more overview of my environment. On my webserver I have login.asp and getdata.asp. If I send a volley onto getdata then the ASP page will try and see if the user is logged in my checking the session var session("userid"). If there is no value set, I assume there is no user logged in. The page will return a "0" and the app will switch the user back to my login tab. If there IS a value set in session("userid") then it will return data about the current user name (JSON format).
Session variables on ASP seem to be the sensible way to do this... As Volleys are stateless, in order to do this I seem to have to send in a custom header with the request to show the session variable ("Cookie" header). However, this isn't working as the returned session variable still changes with each volley response