JSF request-scoped managed bean http-session cause

2019-04-16 05:45发布

问题:

I'm using a SessionFilter servlet for validating users and then giving access of the system to them. My restricted files are in a folder named "com.shadibandhan.Restricted". Session filter is working fine.

here's the relevant code of the sessionfilter servlet

@Override
public void doFilter(ServletRequest req, ServletResponse res,
        FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
    String servletPath = request.getServletPath();
    String contextPath = request.getContextPath();
    String remoteHost = request.getRemoteHost();
    String url = contextPath + servletPath;
    boolean allowedRequest = false;

    if (urlList.contains(servletPath)) {
        allowedRequest = true;
    }

    if (!allowedRequest) {
        HttpSession session = request.getSession(false);
        if (null == session) {

            System.out.println("Session is not present");
            response.sendRedirect(contextPath);
            return;

        } if (null != session) {
            //String loggedIn = (String) session.getAttribute("sb_logged_in");
            System.out.println("Session is present");
            System.out.println("\nSession no. is = " + session.getId());

            if (session.getAttribute("logged-in") == "true") {
                System.out.println("Session logged-in attribute is true, " + session.getAttribute("sessionUsername") + " is logged in.");

                //ServletContext context = request.getServletContext();
                RequestDispatcher dispatcher = request.getRequestDispatcher(servletPath);
                dispatcher.forward(request, response);
            } else {
                System.out.println("Session logged-in attribute is not true");
                response.sendRedirect(contextPath);
            }
        }
    }

    chain.doFilter(req, res);
}

Now, when a user logs in, I put his username and profile id in the httpsession, Here's is the bean that is bound with the login page.

@ManagedBean
@SessionScoped
public class UserLoginManagedBean {

    private User user = null;
    private String username = null;
    private String password = null;
    private ServiceProvider server = null;
    HttpServletRequest request = null;
    HttpServletResponse response = null;
    HttpSession session = null;
    private Date date;
    private int profileActiveness=0;
    private int profileActivenessPercentage=0;

    public UserLoginManagedBean() {
        this.user = new User();
        this.server = ServiceProvider.getInstance();
    }

    public String validateLogin() {

        System.out.println("Inside validate login");
        boolean isUserValid = false;

        System.out.println(this.username + " " + this.password);

        isUserValid = this.authenticate(username, password);

        if (isUserValid) {
            //this.user = found;
            System.out.println("User is valid---Redirecting to messages.xhtml");
            return "com.shadibandhan.Restricted/profile.xhtml?faces-redirect=true";

        } else {
            //addGlobalErrorMessage("Unknown login, please try again");
            return null;
        }
    }

    public boolean authenticate(String username, String password) {
        boolean isUserValid = false;
        String status = null;

        //isUserValid = this.server.authenticateUser(this.username, this.password);

        this.user = (User) this.server.getRecordByTwoColumns(User.class, "username" , this.username, "password", this.password);

        if(null != this.user){
            isUserValid = true;
        }else{
            isUserValid = false;
        }

        if (isUserValid) {

            FacesContext context = FacesContext.getCurrentInstance();
            this.request = (HttpServletRequest) context.getExternalContext().getRequest();
            this.response = (HttpServletResponse) context.getExternalContext().getResponse();
            this.session = request.getSession(true);
//                 if there's no session, it'll creat a new one due to the true flag


            status = this.updateUserRecord();


            if (status.equals("success")) {
                if (null != this.session) {

                    session.setAttribute("sessionUsername", this.user.getUsername());
                    session.setAttribute("sessionProfileId", this.user.getProfile().getProfileId());
                    session.setAttribute("logged-in", "true");

                    System.out.println("Session username is --->" + session.getAttribute("sessionUsername"));
                }

            } else {
                isUserValid = false;
                FacesMessage msg = new FacesMessage("Something went wrong");
                FacesContext.getCurrentInstance().addMessage(null, msg);
            }
        }

        return isUserValid;
    }

    public String logOut() {
        FacesContext context = FacesContext.getCurrentInstance();
        System.out.println("inside logout method");
        this.request = (HttpServletRequest) context.getExternalContext().getRequest();

        if (null != this.request) {

            this.session = request.getSession(false);
            session.invalidate();
            System.out.println("Session is now invalidated");
            return "../index.xhtml?faces-redirect=true";
        } else {
            System.out.println("You're already signed out");
            return null;
        }
    }

    private String updateUserRecord() {
        String status = null;

       Date lastLoginDate=this.user.getLastLogin();
       Date currentDate= new Date();
       this.profileActiveness=this.user.getProfileActiveness();


                SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");

        try {
            lastLoginDate = format.parse(lastLoginDate.toString());
            currentDate = format.parse(currentDate.toString());
        } catch (ParseException e) {
            e.printStackTrace();
        }    

        // Get msec from each, and subtract.
        long diff = currentDate.getTime() - lastLoginDate.getTime();
        long diffSeconds = diff / 1000;         
        long diffMinutes = diff / (60 * 1000);         
        long diffHours = diff / (60 * 60 * 1000);                      
        System.out.println("Time: " + diff + " .");
        System.out.println("Time in seconds: " + diffSeconds + " seconds.");         
        System.out.println("Time in minutes: " + diffMinutes + " minutes.");         
        System.out.println("Time in hours: " + diffHours + " hours.");
        if(diffHours<12)
        {
            if(profileActiveness<8){
            profileActiveness++;
            profileActivenessPercentage=(int) (profileActiveness*12.5);
            this.user.setProfileActiveness(this.profileActiveness);
            }
            }
        if(diffHours>71)
        {
            if(profileActiveness>2){
            profileActiveness-=2;
            profileActivenessPercentage=(int) (profileActiveness*12.5);
            this.user.setProfileActiveness(this.profileActiveness);
            }
            else{
            profileActiveness=0;
            }
        }



        this.user.setLastLogin(this.getCurrentDate());
        this.user.setLoginStatus(true);

        status = this.server.updateObject(this.user);

        return status;
    }

    // ...
}

And, in another managed bean (request-scoped) named, MessagesManagedBean, when i try to get the profile id after the user has logged in, it works like a charm.

Now, I've two questions here :

  1. Whenever I try to access a page from the restricted folder that has a bean bound with it having some code related to the http session as in this case the MessagesManagedBean, It gives me a Can't instantiate bean exception because i'm getting the attribute in the constructor, why ?
  2. Even, when I'm not logged in, it calls the bean constructor, whenever i try to access the page bound with it.

回答1:

You're continuing the request by chain.doFilter() after calling response.sendRedirect(). The sendRedirect() merely sets a Location response header with the new URL which the browser will then handle. But if you continue the request by chain.doFilter(), then the whole JSF process will still be executed.

You need to add a return; statement after the sendRedirect() call to exit the filter.

} else {
    System.out.println("Session logged-in attribute is not true");
    response.sendRedirect(contextPath);
    return;
}

Unrelated to the concrete problem, you've a major design mistake in your session scoped bean. You should never assign the HTTP request, response and session as an instance variable of the bean. This makes your session scoped bean threadunsafe. Remove all those properties and declare them threadlocal inside the very same method block only.