So, I'm trying to send cookies to my frontend and I'm not using spring security. When the user enter their name and click submit button, I want that info to be send to my spring and then I want to return the info of the user and cookie. I implemented the logic like this
#PostMapping("/addUser")
public User addUser(#RequestBody String name, HttpServletRequest request, HttpServletResponse response) {
HttpSession getSession = (HttpSession) request.getAttribute("session");
Cookie cookie = new Cookie("sessionId", getSession.getId());
response.addCookie(cookie);
sessionService.addSession(getSession);
String session = (String) getSession.getId();
System.out.println("Session id is " + session);
User newUser = new User(name, session);
userService.addUser(newUser);
return newUser;
}
to check if cookie is working correctly, I'm using this method
#PostMapping("/noOfUsers")
public int userCount(#RequestBody String sessionId, HttpServletRequest request, HttpServletResponse response) {
System.out.println(request.getSession().getId());
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("sessionId")) {
System.out.println(cookie.getValue());
}
}
}
return this.userService.noOfUser();
}
I know you people might say "use get mapping" well I want to send my user account too when checking that's why I'm using postmapping where I will be sending my username too.
So, coming back to question. Well, I did try using postman and it worked I got the cookie value as I should have but when working with frontend, it just didn't worked which made me question am I doing it in right way??
Related
So, I am working on creating a simple chat app. I'm not using spring security.
So, in front end, the user enters their name which is handled by this controller.
#PostMapping("/addUser")
public User addUser(#RequestBody String name, HttpServletRequest request) {
String session = (String) request.getSession().getAttribute("sessionId");
System.out.println("Session id is " + session);
User newUser = new User(name, session);
userService.addUser(newUser);
System.out.println(newUser);
return newUser;
}
I'm using pre handler method handler interceptor to generate session id for the user. Below is the code:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Its working");
// TODO Auto-generated method stub
if(request instanceof HttpServletRequest) {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpSession session = servletRequest.getSession();
session.setAttribute("sessionId", session.getId());
System.out.println("Connected with session id : " + session.getAttribute("sessionId"));
}
return true;
}
So, I want to make sure that whenever users are inactive for cetain time, I want to end the session for that user and also remove that user from the arraylist of user where I have kept all the users who register by entering their name (in the front end).
Is it possible to achieve without sprin security or do I have to learn spring security to implement it.
I did try using task scheduler but then I found out in some article that its impossible to call HttpSession there.
You can set the session life (time it can be inactive before being killed) with server.servlet.session.timeout=30m
You can take the user out of your list by implementing a HttpSessionListener.sessionDestroyed - spring-boot-session-listener
if you use WebSocket, You can use heartbeat for your session, on the other hand, if you use rest then you should keep the session in memory(redis, hazelcast, or in-memory (singleton object) like map<key, session>,
(keep in mind, the client should send a disconnect request or you should control it in the backend)
I've created a simple login web where the user enters the email and password and checks if the user and password are correct then he gets redirected to a welcome.jsp page , where it says login success , I'm checking 3 emails and passwords and creating session for each one , the problem I'm facing is that if the user enters the email or password wrong after 3 attempts he will be blocked for a certain amount of time and after the time expires he can try again , I can't think of a way of doing this , is there a way in which this could be done ?
import java.io.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
//#WebServlet(name = "loginController", value = "/login")
#WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
IOException {
String email = request.getParameter("email");
String password = request.getParameter("password");
String er = "Invalid user info";
int attempts = 3;
PrintWriter printWriter = response.getWriter();
LoginBean loginBean = new LoginBean();
loginBean.setEmail(email);
loginBean.setPassword(password);
try
{
if(email.equals("Mhamdoon4#gmail.com") && password.equals("pass001"))
{
System.out.println("Admin's Home");
HttpSession session = request.getSession(); //Creating a session
session.setAttribute("Mohammed", email); //setting session attribute
request.setAttribute("email", email);
request.getRequestDispatcher("welcome.jsp").forward(request, response);
}
else{
attempts--;
printWriter.println(attempts + " left");
}
if(email.equals("Mhamdoon6#gmail.com") && password.equals("pass0011"))
{
System.out.println("Editor's Home");
HttpSession session = request.getSession();
session.setAttribute("Ali", email);
request.setAttribute("email", email);
request.getRequestDispatcher("welcome.jsp").forward(request, response);
}
else{
attempts--;
printWriter.println(attempts + " left");
}
if(email.equals("Mhamdoon12#gmail.com") && password.equals("pass00901"))
{
System.out.println("User's Home");
HttpSession session = request.getSession();
session.setAttribute("Adam", email);
request.setAttribute("email", email);
request.getRequestDispatcher("welcome.jsp").forward(request, response);
}
else{
attempts--;
printWriter.println(attempts + " left");
}
// if()
// {
// System.out.println("Error message = Invalid info");
// request.setAttribute("errMessage", er);
//
// request.getRequestDispatcher("fail.jsp").forward(request, response);
// }
}
catch (IOException e1)
{
e1.printStackTrace();
}
catch (Exception e2)
{
e2.printStackTrace();
}
}
public void destroy() {
}
}
The easiest way, as your example is simple (string literals checking), is keeping the attempts in the session. This way the attempts are tied up to the session (in other words, to the browser's cookies).
To set the values in the session:
request.getSession().setAttribute("loginAttempts", 3);
request.getSession().setAttribute("lastLoginAttempt", LocalDateTime.now());
To read them:
Integer attempts = (Integer) request.getSession().getAttribute("loginAttempts");
LocalDateTime lastLoginAttempt = (LocalDateTime) request.getSession().getAttribute("lastLoginAttempt");
Now you just have to play with the values, recalculate, and reset them after a successful login. The variables will be kept as long as the browser session is kept.
TL;DR;
I see that everyone who ends up here may need a bit of a briefing about requests and sessions.
You have to understand that the piece of code that goes inside de doGet or doPost is executed every time you enter the url in the browser (The int attempts = 3; from your original post is executed every time, so it will always be 3).
The server collects all the data that comes from the client's browser request and builds a brand new HttpServletRequest object that contains all the data (url, request params, cookies, ip, port, etc.) every time. Press f5? everything gets executed again with a brand new HttpServletRequest.
The way the servers use to keep a conversational state between the server and the client (browser) is through the Session. The Session is the only thing that is kept between requests. You can save variables in the Session for later (like the attempts and the lastLoginAttempt), and rely on the Session to see if a user is successfully logged in.
And how do the server keeps the session between requests if everything gets recreated in each request? through the session cookie. The server users a normal cookie to which it gives a special value (In the Servlet specification this cookie is JSESSIONID). When a request come without that cookie the server creates one giving it the value of a unique identifier. Next requests from the same browser will have that cookie, and the server will use the cookie to attach the session to every HttpServletRequest generated from requests from that browser. So in the brand new HttpServletRequest that is created in every request, the server injects into it the same HttpSession that was being used by the same JSESSIONID.
I'm working with Spring and have 2 controllers, one of them is:
#RequestMapping("/meni/{id}")
public String meni(#PathVariable String id, Model model, HttpServletRequest request, HttpServletResponse response){
cookie = new Cookie("fake_session",id);
cookie.setMaxAge(30*60);
response.addCookie(cookie);
return "meni";
}
Then in the 'meni' static HTML page, I have a post request that goes to:
#PostMapping("/index/{id}")
public void post(#PathVariable String id,#RequestBody TestDTO testDTO, HttpServletResponse response, HttpServletRequest request){
Cookie [] cookies = request.getCookies();
for (int i=0;i<cookies.length;i++){
Cookie cookie = cookies[i];
if (cookie.getName().equals("fake_session")){
System.out.println("Same cookie!");
}
}
However, the if never gets passed. If i go to the get controller twice, it recognizes the cookie, but if i go the post controller, it the if does not get passed. Everything else is running smoothly in the post controller, it does all its other tasks well.
I go to the Post controller by clicking a button that calls a ajax function in my java script that sends a POST request to that URL. Am I suppose to do something with the cookie there maybe ? I always go to the GET controller before going to the post controller so that the cookie gets created.
Try using Spring MVC's #CookieValue(value = "fake_session", defaultValue = "default") to access data set within any HTTP cookie in your post method.
I am trying to save the latest time in the cookie. So after every controller call I have intercepting the call using a postHandle of HandlerInterceptor. In that I am updating the value in the cookie. But the values is not updating. Anyone have any idea why it is not working.
The code:
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, ModelAndView ex)
throws Exception {
boolean isSet = true;
if (request.getSession().getAttribute("userId") != null){
for (Cookie cookies: request.getCookies()) {
if ("RequestTime".equals(cookies.getName())) {
isSet = false;
cookies.setValue(Long.toString(System.currentTimeMillis()));
response.addCookie(cookies);
}
}
if(isSet){
Cookie cookie = new Cookie("RequestTime", Long.toString(System.currentTimeMillis()));
response.addCookie(cookie);
}
}
}
when you create cookie ,you should set cookie's path, when you want to share this cookie in your project,you should set cookie path as "/" like
if (request.getSession().getAttribute("userId") != null){
Cookie cookie = new Cookie("RequestTime",Long.toString(System.currentTimeMillis()));
cookie.setPath("/")
response.addCookie(cookie); }
the default cookie path is current request URL.
NOTE: if you want to share the cookie from different subdomain, you should also set cookie domain.
cookie.setDomain(".test.com")
I have a home.jsf that invoke a login servlet that look into database and query out the user object given the username and password. Then I save that user object into session under attribute name user, like this request.getSession().setAttribute("user", user);
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
boolean remember = "true".equals(request.getParameter("remember"));
//Hashing the password with SHA-256 algorithms
password = hash(password);
HttpSession s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id: {0}", s.getId());
}
User user = scholarEJB.findUserByUserNamePassword(username, password);
try {
if (user != null) {
request.login(username, password);
request.getSession().setAttribute("user", user);
if (remember) {
String uuid = UUID.randomUUID().toString();
UserCookie uc = new UserCookie(uuid, user.getId());
scholarEJB.persist(uc);
Helper.addCookie(response, Helper.COOKIE_NAME, uuid, Helper.COOKIE_AGE);
}else{
//If the user decide they dont want us to remember them
//anymore, delete any cookie associate with this user off
//the table
scholarEJB.deleteUserCookie(user.getId());
Helper.removeCookie(response, Helper.COOKIE_NAME);
}
response.sendRedirect("CentralFeed.jsf");
}else{
response.sendRedirect("LoginError.jsf");
}
} catch (Exception e) {
response.sendRedirect("LoginError.jsf");
}
Then I have a Filer that map to all my secured page, that will try to retrieve the user object from the session, otherwise, redirect me to home.jsf to login again
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id Before: {0}", s.getId());
}
User user = (User) request.getSession().getAttribute("user");
s = request.getSession(false);
if (s != null) {
logger.log(Level.INFO, "Id After: {0}", s.getId());
}
if (user == null) {
String uuid = Helper.getCookieValue(request, Helper.COOKIE_NAME);
if (uuid != null) {
user = scholarEJB.findUserByUUID(uuid);
if (user != null) {
request.getSession().setAttribute("user", user); //Login
Helper.addCookie(response, Helper.COOKIE_NAME, uuid, Helper.COOKIE_AGE);
} else {
Helper.removeCookie(response, Helper.COOKIE_NAME);
}
}
}
if (user == null) {
response.sendRedirect("home.jsf");
} else {
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(req, res);
}
Now as you see here, I manipulate some Cookie as well, but that is only happen when I check remember me. So now I am in CentralFeed.jsf, but then any request that I send from here will bring back to home.jsf to login again. I walk through a debugger, so when I first login, the first time I get into the Filter, i successfully retrieve the user object from session by request.getSession().getAttribute("user");. But after that, when I get back in the filter, I no longer the session attribute user anymore. I set session timeout to be 30 min in my web.xml
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
EDIT
Now when I print out the session Id between request, it is fact different session id, but I have no idea why? please help.
EDIT2
#BalusC: I actually did invalidate the session. Back then, you show me how to force a logout when user log in somewhere else (http://stackoverflow.com/questions/2372311/jsf-how-to-invalidate-an-user-session-when-he-logs-twice-with-the-same-credentia). So inside User entity i have this
#Entity
public class User implements Serializable, HttpSessionBindingListener {
#Transient
private static Map<User, HttpSession> logins = new HashMap<User, HttpSession>();
#Override
public void valueBound(HttpSessionBindingEvent event) {
HttpSession session = logins.remove(this);
if (session != null) {
session.invalidate(); //This is where I invalidate the session
}
logins.put(this, event.getSession());
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
logins.remove(this);
}
}
In the valueBound method, I did invalidate the session, when I comment it out, everything work. I walk through the debugger, and here is what happen. When I first log in, the LoginServlet catch it. Then the line request.getSession().setAttribute("user", user); invoke the method valueBound. Then the Filter got called, and the line chain.doFilter(req, res); invoke the valueBound method again, this time, session is not null so it get in the if and session.invalidate. I comment the session.invalidate out and it work. But as u might have guess, I cant force a log out when user login somewhere else. Do you see a obvious solution for this BalusC?
The HTTP session is maintained by the JSESSIONID cookie. Ensure that your Helper.COOKIE_NAME doesn't use the same cookie name, it will then override the session cookie.
If that is not the case, then I don't know. I would use Firebug to debug the HTTP request/response headers. In a first HTTP response on a brand new session you should be seeing the Set-Cookie header with the JSESSIONID cookie with the session ID. In all subsequent requests within the same session, you should be seeing the Cookie header with the JSESSIONID cookie with the session ID.
A new session will be created when the Cookie header is absent or contains a JSESSIONID cookie with a (for the server side) non-existing session ID (because it's been invalidated somehow), or when the server has responded with a new Set-Cookie header with a different session ID. This should help you in nailing down the culprit. Is it the server who generated a new session cookie? Or is it the client who didn't send the session cookie back?
If it was the server, then somewhere in the server side the session has been expired/invalidated. Try putting a breakpoint on HttpSession#invalidate() to nail it further down.
If it was the client (which would be very weird however, since it seems to support cookies fine), then try to encode the redirect URL to include the JSESSIONID.
response.sendRedirect(response.encodeRedirectURL(url));
Try with different clients if necessary to exclude the one and other.
look at the JSessionID param in your request. If it changes that means you are losing your session (browser is telling your Server its another session). Dont know why its happening but propably is something you are doing (open another window, change servlet context and come back, change server in some request... etc.).
Please post more information if you confirm that