I am doing a rest app in spring and i have a log out method like below. I dont have knowledge on spring so i just searched around and made this.
#RestController
public class LogoutController {
#Autowired
private DatabaseService databaseService;
#RequestMapping(value = "/myApp/user/logout", method = GET, produces = {"application/xml"})
public Users performLogout(#RequestHeader("AuthenticationID") String authID, HttpServletRequest request) throws DatatypeConfigurationException {
return handleLogout(request, authID);
}
private Users handleLogout(HttpServletRequest request, String authID) throws DatatypeConfigurationException {
LogService.info(this.getClass().getName(), "Received Logout Request");
final UsersXMLBuilder usersXMLBuilder = new UsersXMLBuilder();
Users usersXML = usersXMLBuilder.buildDefaultUsersTemplate();
HttpSession session = request.getSession();
AppUtilities utils = new AppUtilities();
try {
//Checking with RegEX
if (utils.isValidUUID(authToken)) {
//Get User Login Record from DB By the AuthID and Delete It
//Invalidate The Session
session.invalidate();
LogService.info(this.getClass().getName(), "Session Invaliated");
} else {
LogService.info(this.getClass().getName(), "Invalid AuthID Found. Not a Valid UUID");
usersXML.setResponseCode(-5);
usersXML.setResponseText("User Session is Not Valid");
}
} catch (Exception ex) {
LogService.error(this.getClass().getName(), ex);
usersXML.setResponseCode(-4);
usersXML.setResponseText("Error Occured!");
return usersXML;
} finally {
LogService.info(this.getClass().getName(), "LogOut Process Finished");
}
return usersXML;
}
}
Questions
1- Is it possible i can return a XML message when spring gives white label error page when i pass no authentication ID in request.
2- How can i get the Authentication Header and check it for null and give message that AuthID is missing.
3- How can i set a attribute explicitly and check for it in every controller that if it exists or not.
4- I plan to have a table where i can store user login time and give a session 10 mins time and Update it more 10 mins if i get any request from the user with the AuthID. So can i have a class or method which can check any incoming request? so i can detect the AuthID and Update My table.
Thank you for your time and help.
You can use an interceptor for that : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping-interceptor
The interceptor will run for every request. It can stop the request and do a response itself.
Related
I want to obtain a HttpSession object by URL Path variable id to get some attributes from it.
Context:
I'm trying to implement a web server that has a register and login sub-systems as a learning exercise.
I'm using JAVA, Springboot and various other spring dependencies like hibernate, jdbc, etc.
I got the behavior I wanted, but as I tested my logic with an Android client application I encountered that the register confirmation link I send, does not work if I access it from another device, because the device-sender has a different session and thus my logic fails.
The flow of my registration is as follows:
User POSTs at /register -> { name, email, password }
Server saves this information in their session and sends confirmation email with /register/confirm/{token}
As the user GETs at /register/confirm/{token} that was send to their email,
the server checks if this token is contained in their session and commits the information from the session to the database.
Of course if I register from the device and try to confirm through another device they'd have different sessions and hence the temp information would not be available to the other device, but this is the same user trying to register and I'm looking for a work around. The way I decided to change my code is to send the user /register/confirm/{sessionId}+{token} to their email, but I can't find my way around obtaining the other HttpSession.
(#ServletComponentScan)
I tried to create a HttpSessionListener and tried to maintain a HashMap of HttpSession's but for some reason the Framework would instantiate the Listener object, but never send createSession events to it thus it's HashMap is always empty, thus {sessionId} is never found.
To provide some extra code for context.
My Listener:
#WebListener
public class SessionLookUpTable implements HttpSessionListener {
static final HashMap<String, HttpSession> sessionHashMap = new HashMap<>();
public SessionLookUpTable() {
super();
System.out.println("-------------- Session Listener Created"); // DEBUG
}
// Always empty for some reason, despite constructor being called
static public Optional<HttpSession> findSessionById(String sessionId) {
if (!sessionHashMap.containsKey(sessionId))
return Optional.empty();
return Optional.of( sessionHashMap.get( sessionId ) );
}
#Override
public void sessionCreated(HttpSessionEvent se) {
HttpSessionListener.super.sessionCreated(se);
HttpSession session = se.getSession();
sessionHashMap.put( session.getId(), session );
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSessionListener.super.sessionDestroyed(se);
sessionHashMap.remove(se.getSession().getId() );
}
};
The controller entry points
#PostMapping("/register")
public String register(HttpSession session,
#RequestParam("email") String username,
#RequestParam("password") String password,
#RequestParam("password2") String pw2)
{
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setPrivilegeLevel( Role.USER_PRIVILEGE_NORMAL );
if(session.getAttribute(ATTRIBUTE_USER_ID) != null) {
return "Already registered";
}
if(!userService.isUserDataValid(user)) {
return "Invalid input for registry";
}
if(userService.usernameExists(user.getUsername())) {
return "User already exists";
}
session.setAttribute(ATTRIBUTE_REGISTER_DATA, user);
String token = userService.sendConfirmationEmail( session );
if(token != null) {
session.setAttribute(ATTRIBUTE_USER_ID, 0L );
session.setAttribute(ATTRIBUTE_REGISTER_TOKEN, token);
}
return "A link was sent to your email.";
}
#RequestMapping("/register/confirm/{sessionId}+{token}")
void confirmRegister(HttpSession sessionIn,
#PathVariable("sessionId") String sessionId,
#PathVariable("token") String token) {
Optional<HttpSession> optSession = SessionLookUpTable.findSessionById( sessionId );
if(optSession.isEmpty())
return;
HttpSession session = optSession.get();
// Multiple confirmations guard
Long userId = (Long)session.getAttribute(ATTRIBUTE_USER_ID);
if( userId != null && userId != 0L ){
return;
}
String sessionToken = (String)session.getAttribute(ATTRIBUTE_REGISTER_TOKEN);
if(!sessionToken.equals(token)) {
return;
}
User user = (User)session.getAttribute(ATTRIBUTE_REGISTER_DATA);
user.setDateRegistered( LocalDate.now() );
Long id = userService.register( user );
session.setAttribute(ATTRIBUTE_USER_ID, id);
}
I'm stuck at this stage for quite a while, so any help is appreciated. Thank you.
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 am creating a Spring MVC web application using spring security for authentication.
My application has a requirement that the user is logged in automatically when the user accesses a particular URL, say http://www.example.com/login?username=xyz
The user xyz is in the database, and I need to create a session for this user automatically.
Request you to let me know how this can be achieved.
Thanks!
You can do something like this, the idea would be to retrieve the user from the database, make the checks you want and then use the UsernamePasswordAuthenticationToken to create a new Principal into the current session.
The use of the returned Principal of the method is for subsequent uses on the same request, as the Principal won't be available to be retrieved by normal means until the next request.
public Principal doAutoLogin(String username, String password, HttpServletRequest hreq, HttpServletResponse hres, boolean rememberMe) {
User user = getUserFromDatabase(username);
if(user != null && passwordsMatch(password, user.getPassword())) {
try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(hreq));
if(LOG.isDebugEnabled())
LOG.debug("Logging in with "+ token.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(token);
if(rememberMe) {
rememberMeServices.onLoginSuccess(hreq, hres, token);
}
return token;
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
LOG.error("Failure in autoLogin", e);
return null;
}
}
else {
throw new IllegalArgumentException("The specified credentials can't be found");
}
}
I am working on a web application of my own.Iam using Spring-mvc3 architecture n my application.I want to add authentication to my application.I thought of doing it by sessions but am not able to do so.I have created a login page and after login am able to authenticate and go to next page and get data that i have saved during session creation.Then after that when am navigating to another page my session data that i have saved is getting lost and when iam trying to get the session id am getting a new session id which is different from the one the got created at the time of authentication please check this and provide me the appropriate answer.
#RequestMapping(value = "checkLogin.action")
public String validateLogin(
#ModelAttribute("loginDto") LoginDto loginDetails,
HttpServletRequest request) {
boolean validUser;
try {
validUser = userDao.validateLogin(loginDetails);
if (validUser) {
HttpSession session = request.getSession();
if (session.isNew()) {
logger.debug("New Session is Created");
System.out.println("Session id is" + session.getId());
} else {
logger.debug("Not a New Session");
System.out.println("Session Id" + session.getId());
}
//session.setMaxInactiveInterval(1000);
session.setAttribute("LoginData", loginDetails);
session.setAttribute("loggedUser",true);
return "home";
}
return "redirect:loginPage.action?message=Unable to Login invalid Id/Password";
} catch (DaoException e) {
logger.error("Problem in UserDao");
return "redirect:loginPage.action?message=Unable to Login invalid Id/Password";
} catch (Exception e) {
logger.error("problem validating User Login");
e.printStackTrace();
return "redirect:loginPage.action?message=Unable to Login invalid Id/Password";
}
}
this is how am setting data in my session and am able to retrieve that set attributes in view home to which on success it goes to, from home if i go to another view there am not able to get that data.
I wanted to know using anchor tag for navigating to another page clears the session data??????
I have a login form that calls a certain LoginBean, which returns a ajax callback parameter indicating whether the credentials are valid or not.
The code is as follows:
public void doLogin() {
Authentication authenticationRequestToken =
new UsernamePasswordAuthenticationToken(user, password);
try {
Authentication authenticationResponseToken =
authenticationManager.authenticate(authenticationRequestToken);
SecurityContextHolder.getContext().
setAuthentication(authenticationResponseToken);
if (authenticationResponseToken.isAuthenticated()) {
RequestContext context = RequestContext.getCurrentInstance();
FacesMessage msg;
boolean loggedIn = true;
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Welcome", user);
FacesContext.getCurrentInstance().addMessage(null, msg);
context.addCallbackParam("loggedIn", loggedIn);
}
} .authenticate(...) catches ...
// Here I need some code that continue whatever j_spring_security_check
// would do after authenticating.
}
The way my application is working now, after this call to doLogin(), the form is submited to j_spring_security_check, and then the authentication process takes place again, wasting previous work.
I'm trying to find a solution for this, any help is appreciated.
So, the bottom line is that I need something that would simulate what happens when j_spring_security_check is intercepted by the filters (or a way to force this interception explicitly), so the processing would take place behind the button, not after the form is submited.
It will be better if you just forward to the spring security authentication url instead of using the SecurityContextHolder yourself. Look at this code:
public String doLogin() throws ServletException, IOException {
FacesContext context = FacesContext.getCurrentInstance();
String springCheckUrl = this.buildSpringSecurityCheckUrl();
HttpServletRequest request = (HttpServletRequest) context
.getExternalContext().getRequest();
RequestDispatcher dispatcher = request
.getRequestDispatcher(springCheckUrl);
dispatcher.forward((ServletRequest) request,
(ServletResponse) context.getExternalContext.getResponse());
context.responseComplete();
return null;
}
private String buildSpringSecurityCheckUrl() {
StringBuilder springCheckUrl = new StringBuilder(
"/j_spring_security_check").append("?").append("j_username")
.append("=").append(this.userName.trim()).append("&")
.append("j_password").append("=")
.append(this.userPassword.trim());
return springCheckUrl.toString();
}
}