如何使用j_security_check获取已连接用户的数量和角色?

11

我通过托管的Bean这种方式获取已连接用户的用户名(使用j_security_check):

......
    username =   FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal().getName();
然后在JSF页面中这样显示:#{userBean.username}。但是我无法获取已连接用户的数量并获取他们的角色信息。 换句话说,我想要显示用户名字旁边的用户角色和已连接用户的数量。请问如何实现呢! 感谢您的帮助!
编辑: 我现在可以使用托管bean中的namedquery获取已连接用户的角色:
public Users getUserRole(){
      try {
            Users auser = (Users)
            em.createNamedQuery("Users.findByUsername").
                    setParameter("username", getRemoteUser()).getSingleResult();
            return auser; 
        } catch (NoResultException nre) {
            JsfUtil.addErrorMessage(nre, "getUserRole Error");
            return null;
        }

    }

同时在xhtml页面中:

<h:outputLabel for="rolefacet" value="Role: "/>
  <h:outputFormat id="rolefacet" value="#{UserBean.userRole.ugroup}" /> 

在用户实体类中,ugroup是角色名字。


编辑:对我仍然没有用的一种解决方法是在我的web.xml文件中添加一个HttpSessionListener:

package beans;

/**
 *
 * @author med81
 */

import java.io.Serializable;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.ArrayList;

import javax.faces.context.FacesContext;


public class SessionCounter implements Serializable, HttpSessionListener {

    private List sessions = new ArrayList();
   Object  s =  FacesContext.getCurrentInstance().getExternalContext().getSession(false);

    public Object getS() {
        return s;
    }

    public void setS(Object s) {
        this.s = s;
    }


    public SessionCounter() {
    }


    public void sessionCreated(HttpSessionEvent event) {
        HttpSession session = event.getSession();
        sessions.add(session.getId());

        session.setAttribute("counter", this);
    }


    public void sessionDestroyed(HttpSessionEvent event) {
        HttpSession session = event.getSession();
        sessions.remove(session.getId());

        session.setAttribute("counter", this);
    }

    /**
     * 
     * @return size of the session list
     */
    public int getActiveSessionNumber() {
        return sessions.size();
    }


}
2个回答

12

以下是一个基本的开始示例,说明当您使用Servlet 3.0时,如何通过新的HttpServletRequest#login() API实现编程式登录。

登录表单:login.xhtml

<h:form>
    <h:inputText value="#{user.username}" />
    <h:inputSecret value="#{user.password}" />
    <h:commandButton value="Login" action="#{user.login}" />
    <h:messages />
</h:form>
用户管理器Bean:com.example.UserManager
@ManagedBean(name="user")
@SessionScoped
public class UserManager implements Serializable {

    private String username;
    private String password;
    private User current;

    @EJB
    private UserService userService;

    @ManagedProperty("#{loginManager.logins}")
    private Set<User> logins;

    public String login() {
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();

        try {
            request.login(username, password);
            current = userService.find(username, password);
        } catch (ServletException e) {
            // Unknown login. Will be handled later in current==null check.
        }

        if (current == null) {
            context.addMessage(null, new FacesMessage("Unknown login"));
            return null;
        } else {
            logins.add(current)
            return "home?faces-redirect=true";
        }
    }

    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "login?faces-redirect=true";
    }

    // ...
}

退出登录(以及使会话无效)的监听器:com.example.LogoutListener

@WebListener
public class LogoutListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        // NOOP.
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        UserManager userManager = (UserManager) event.getSession().getAttribute("user");
        if (userManager != null && userManager.getCurrent() != null) {
            userManager.getLogins().remove(userManager.getCurrent());
        }
    }

}

(不要在logout()方法中执行此操作!是会话失效触发了这个操作,当logout()被调用或会话已过期时,会话失效才会发生)

在任何已登录的视图中,您可以按以下方式获取当前用户和登录次数:

<p>Welcome, #{user.current.name}!</p>
<p>Total logged in users: #{user.logins.size()}</p>

Set<User> logins 中的信息保留多久?内容什么时候过期? - Stephan
@Stephan 只需使用 Ctrl+F 查找“remove”。 - BalusC
@BalusC 是的,我已经注意到了注销和remove()。请允许我重新表达我的问题。我该如何确保维护包含已登录用户列表的会话(在这种情况下为Set<User> logins;)将永久维持(直到应用程序关闭)。会话不会在某个时刻过期并且信息丢失吗? - Stephan
@Stephan,你在哪里看到LoginManager必须是会话范围的? - BalusC
1
@SeanCoetzee 只需让 LoginManager 将数据存储在共享数据存储中即可。 - BalusC
显示剩余4条评论

8

获取连接用户的数量

我假设您想要获取已登录用户的数量。基本上,您需要拥有一个应用程序范围内的Set<User>,其中包含所有已登录的用户,并在其登录时将User添加到该集合中,在其注销或会话销毁时将User从该集合中删除。以下是使用应用程序作用域托管 bean 的示例:

@ManagedBean(eager=true)
@ApplicationScoped
public class LoginManager implements Serializable {

    private Set<User> users = new HashSet<User>();

    public Set<User> getUsers() {
        return users;
    }

}

如果您使用的是 Java EE 6,那么将 j_security_check 替换为一个管理 bean 方法就很容易了,该方法利用新的 Servlet 3.0 HttpServletRequest#login() 并同时将 User 添加到注入的 LoginManager bean 的 Set<User> 中。但是在 Java EE 5 上,没有简单的方法来挂钩它。您需要检查每个已登录用户的请求。最好的实现方式是在出现 UserPrincipal 时将 User 对象放入会话中。您可以使用一个过滤器来执行大致以下工作的 doFilter() 方法。
UserPrincipal principal = request.getUserPrincipal();
User user = (User) session.getAttribute("user");

if (principal != null && user == null) {
    user = userService.findByName(principal.getName());
    session.setAttribute("user", user);
    LoginManager loginManager = (LoginManager) servletContext.getAttribute("loginManager");
    loginManager.getUsers().add(user);
}

最后,要从登录中删除用户,最好是钩入HttpSessionListener#sessionDestroyed(),假设你在注销时使会话无效。当会话过期时也会调用此方法。
public void sessionDestroyed(HttpSessionEvent event) {
    User user = (User) event.getSession().getAttribute("user");
    if (user != null) {
        LoginManager loginManager = (LoginManager) event.getSession().getServletContext().getAttribute("loginManager");
        loginManager.getUsers().remove(user);
    }
}

谢谢BalusC!我明天会尝试这个解决方案并提供反馈!我原本想到了类似的方法(使用实现HttpSessionListener的类和两个或三个方法:void SessionCreated(),void SessionDestroyed和int getActiveSessionsNumber(){return sessions.size();},但是你的解决方案似乎更好!让我们试试吧! - Hanynowsky
是的,如果您提供一个返回users.getSize()getCount()方法,您就可以通过#{loginManager.count}或其他方式在视图中获取登录计数。 - BalusC
是的,它有:http://download.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal%28%29 在一个Filter中,你只需要将ServletRequest强制转换回HttpServletRequest - BalusC
好的!是的,没错!我使用了错误的属性!抱歉! 还有一件事,servletContext是从哪里来的?在Filter类中声明:ServletContext servletContext = .....something; ?? - Hanynowsky
request.getServletContext()。 - BalusC
1
能否麻烦 @BalusC 添加第二个答案(使用 HttpServletRequest#login(),并同时将用户添加到注入的 LoginManager bean 的 Set<User>中),考虑到我现在正在使用 Servlet 3.0。如果可以的话,不胜感激! - Hanynowsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接