绑定用户实体和GlassFish Principal [英] Binding a User entity and a GlassFish Principal

查看:104
本文介绍了绑定用户实体和GlassFish Principal的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个实体类 User ,其中包含用户名,名字,姓氏和密码等信息,并且我的GlassFish 3.1服务器设置可以执行身份验证。到现在为止还挺好。容器验证用户后,我需要一些方法将主体绑定到实际的用户实体。毕竟,GlassFish告诉我用户laurens已经过身份验证,它不会给我相应的 User 实体。



为此,我编写了一个JSF托管bean UserController 。我想知道的是,如果这是正确的方式来查看实际的实体,是否有任何明显的陷阱我没有看到。



UserController 具有以下字段:

  @EJB $ b $ private UserFacade userFacade; 

私人用户用户;

userFacade 是一个无状态会话bean坚持并找到用户实例。 user 字段由JSF页面用于获取和设置用户属性。



我使用以下方法来执行带有两个辅助方法的绑定:

  @PostConstruct $ b $ private void init(){
尝试{
user = userFacade.find(getUserPrincipal()。getName());
} catch(NullPointerException ex){
//故意留空 - 用户未登录。
}
}

private HttpServletRequest getHttpServletRequest( ){
return(HttpServletRequest)FacesContext.getCurrentInstance()。getExternalContext()。getRequest();
}

private Principal getUserPrincipal(){
return getHttpServletRequest()。getUserPrincipal();



$ b $ p
$ b

JSF页面使用以下方法来确定要显示哪些组件如果用户已经通过身份验证,则不需要显示登录表单),单击登录按钮时认证用户,或点击注册按钮时注册为新用户。

  public boolean isAuthenticated(){
return getUserPrincipal()!= null;

$ b $ public void authenticate(){
try {
getHttpServletRequest()。login(user.getEmailAddress(),user.getPassword());
} catch(Exception ex){
// TODO:处理失败的登录尝试
}
}

public void register(){
userFacade.create(user);
}

请问这是正确的方法吗?



谢谢!

编辑: $ b

为输入!我想了一下,虽然我认为将密码移到不同的表中对我来说目前处理得有点多,但我认为我可以通过将 UserController 在一个 @RequestScoped AuthenticationController 和一个精简的 @SessionScoped UserController


$ b AuthenticationController 将具有 emailAddress 密码字段,由网页的emailAddress和密码字段绑定。它还会包含 public void authenticate()来验证用户并在之后放弃凭证。然后, @SessionScoped UserController 可以绑定到相应的 User 实体无需知道密码。事实上,我相信我可以从 User 完全删除密码字段。

解决方案您提出的方法有一些粗糙的边缘,但大多数情况下它很好。



如果您打算存储对 User 实体,那么最好在 SessionScoped 受管bean中这样做。这有利有弊。显而易见的好处是,


  • User 实体可以通过应用程序流,跨所有页面。这意味着您需要为会话绑定 Principal 到一个 User 实体一次。如果需要,您可以通过所有页面重新使用绑定值。



不太明显的缺点是


  • 密码字段会长时间储存​​在内存中。充其量,在尝试进行身份验证(无论是否成功,无论该字段是否包含明文或散列密码)时,您都应尝试取消实体的密码字段。另外,将密码字段定义为延迟取回( FetchType <$ c $)会很有意义( FetchType EAGER )相对于默认的eager fetch(c> LAZY ) 。如果你实现这个(特别是取消密码字段),你需要注意涉及在 User 实体上进行合并操作的问题;在这种情况下,让一个单独的实体为用户存储密码可能会更好(非常不幸,但这是在某些应用程序中,为了保护密码和哈希值而必须弯曲背部的程度)。



  • 话虽如此,还需要确保以下内容:


    • 应谨慎处理匿名用户主体。如果您没有编写一个过滤器来强制实施访问控制机制来保护应用程序的私人页面,那么您应该更多地关注页面中授权逻辑的构建方式,而不是您不必担心所有当你使用过滤器。匿名主体就像任何其他主体一样,只是它不受领域中的身份支持。如果Principal to User实体绑定方案出于某种原因失败,则必须使会话无效并将用户重定向到登录页面,特别是如果您的页面依赖于 User 实体而不是使用 Principal 对象来强制进行访问控制检查。

    • 确保您有单独的应用程序登录页面。这在大多数接受登录页面中的表单接受用户凭证的应用程序中更为可取;如果表单处于对话框或其他一些工具中,则单独的登录页面通常不是必需的。这很可取,原因很简单,您希望您的登录过程实现POST-REDIRECT-GET模式 - 成功登录到应用程序的用户必须重定向到应用程序的主页面。如果不这样做,将导致浏览器刷新(由任何有权访问终端的人员执行)将重新提交凭证的情况;很显然,使用模式对话框或类似的应用程序不太容易出现这个问题。
    • / p>

      这是基于编辑的问题。如果按照建议实现 authenticate 方法,则可以实现将 User 实体绑定到 Principal 只有在认证成功后才可以使用。

      以下是我的应用程序中类似实现的复制:

        public String authenticate()
      {
      String result = null;
      ExternalContext externalContext = FacesContext.getCurrentInstance()。getExternalContext();
      HttpServletRequest请求=(HttpServletRequest)externalContext.getRequest();
      尝试
      {
      request.login(userId,password);
      result =/private/MainPage.xhtml?faces-redirect=true;
      }
      catch(ServletException ex)
      {
      logger.error(验证用户失败。,ex);
      FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR,Messages.getString(Login.InvalidIdOrPasswordMessage),null);
      FacesContext.getCurrentInstance()。addMessage(null,facesMessage);
      }
      返回结果;
      }

      这是从facelet调用的:

       < h:form id =LoginFormacceptcharset =UTF-8> 
      < p>
      < h:outputLabel for =useridvalue =#{msg ['Login.userid.label']}/>
      < h:inputText id =useridvalue =#{loginBean.userId}/>
      < / p>
      < p>
      < h:outputLabel for =passwordvalue =#{msg ['Login.password.label']}/>
      < h:inputSecret id =passwordvalue =#{loginBean.password}/>
      < / p>
      < h:commandButton id =submitvalue =#{msg ['Login.submit.label']}
      action =#{loginBean.authenticate}/>
      < / h:表格>

      注意在验证成功的情况下使用POST-REDIRECT-GET模式。在重定向之前,我留下了一些与当前会话失效相关的代码,以防止会话修复攻击。 User 实体与 Principal 的绑定将在新会话中完成,只要它在会话范围的bean。


      I have an entity class User that contains information such as username, first name, last name and a password and I have my GlassFish 3.1 server setup to perform authentication. So far, so good. After the container has authenticated a user, I need some way to bind the principal to the actual User entity. After all, GlassFish is telling me is that user "laurens" has authenticated, it is not giving me the corresponding User entity.

      To that end I wrote a JSF managed bean UserController. What I would like to know is if this is the correct way to look the actual entity up and if there are any obvious pitfalls I am not seeing.

      UserController features the following fields:

      @EJB
      private UserFacade userFacade;
      
      private User user;
      

      The userFacade is a stateless session bean to persist and find User instances. The user field is used by the JSF page to get and set properties on the user.

      I use the following method to perform the binding accompanied by two helper methods:

      @PostConstruct
      private void init() {
          try {
              user = userFacade.find(getUserPrincipal().getName());
          } catch (NullPointerException ex) {
              // Intentionally left empty -- User is not logged in.
          }
      }
      
      private HttpServletRequest getHttpServletRequest() {
          return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
      }
      
      private Principal getUserPrincipal() {
          return getHttpServletRequest().getUserPrincipal();
      }
      

      The following methods are used by the JSF page to determine what components to show (if the user is already authenticated then there is no need to show a login form), authenticate the user if the "Login" button is clicked, or register as a new user when the "Register" button is clicked.

      public boolean isAuthenticated() {
          return getUserPrincipal() != null;
      }
      
      public void authenticate() {
          try {
              getHttpServletRequest().login(user.getEmailAddress(), user.getPassword());
          } catch (Exception ex) {
              // TODO: Handle failed login attempt
          }
      }
      
      public void register() {
          userFacade.create(user);
      }
      

      Would this the correct way to go about?

      Thanks!

      Edit:

      Thanks for the input both! I thought about it for a bit, and while I think moving the passwords to a different table is a little to much for me to handle at the moment, I do think I can address some of the issues by separating the UserController in a @RequestScoped AuthenticationController and a stripped down @SessionScoped UserController.

      The AuthenticationController would have emailAddress and password fields, bound by the web page's emailAddress and password fields. It would additionally contain the public void authenticate() to authenticate the user and discard the credentials afterwards. The @SessionScoped UserController can then bind to the appropriate User entity without ever needing to know a password. In fact I believe I would be able to remove the password field from User altogether.

      解决方案

      Your proposed approach has a few rough edges, but for the most part it is quite fine.

      If you intend to store a reference to the User entity, then it is preferable to do so in a SessionScoped managed bean. This has it's pros and cons. The obvious advantage is that

      • the User entity is available through out the application flow, across all pages. This would mean that you need to bind the Principal to a User entity only once for a session. And you can re-use the bound value through all pages, if the need arises.

      The not-so-obvious disadvantage is that

      • the password field would be stored in memory for quite a long duration. At best, you should attempt to nullify the password field of the entity after an authentication attempt (whether it is unsuccessful or not, irrespective of whether the field contains the password in clear or a hash). Also, it would make a lot of sense to define the password field as lazily fetched (FetchType of LAZY) as opposed to the default of eager fetch (FetchType of EAGER). If you implement this (in particular, nullification of the password field), you'll need to watch out for problems involving merge operations conducted on the User entity; in such an event, it might be better to have a separate entity to store passwords for users (quite unfortunate, but that is the extent to which you must bend your back to protect password and their hashes in certain applications).

      Having said that, it is also necessary to ensure the following:

      • The Anonymous user principal should be handled carefully. If you are not writing a filter that enforces an access control mechanism to protect your "private" pages of the application, you should worry a lot more about the way authorization logic in your pages are constructed, not that you do not have to worry at all when you use a filter. The Anonymous principal is just like any other principal, except that it isn't backed by an identity in a realm. If the Principal to User entity binding scheme fails for some reason, you must invalidate the session and redirect the user to the login page again, especially if your pages rely on the User entity instead of the Principal object to enforce access control checks.
      • Ensure that you have a separate login page for the application. This is preferable in most applications that accept user credentials in a form, present in the login page; if the form is in a dialog or in some other contraption, a separate login page is usually not necessary. This is desirable for the simple reason that you would want your login process to implement the POST-REDIRECT-GET pattern - a user on successfully logging into your application must be redirected to the main page of the application. Failing to do so, would result in the scenario where a browser refresh (performed by any person having access to the terminal) would re-submit the credentials; quite obviously applications that use a modal dialog or something similar are less susceptible to this problem.

      Update

      This is based on the edited question. If you implement the authenticate method as proposed, you can implement your scheme of binding the User entity to the Principal only upon successful authentication.

      The following is a reproduction of a similar implementation from my application:

      public String authenticate()
      {
          String result = null;
          ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
          HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
          try
          {
              request.login(userId, password);
              result = "/private/MainPage.xhtml?faces-redirect=true";
          }
          catch (ServletException ex)
          {
              logger.error("Failed to authenticate user.", ex);
              FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, Messages.getString("Login.InvalidIdOrPasswordMessage"), null);
              FacesContext.getCurrentInstance().addMessage(null, facesMessage);
          }
          return result;
      }
      

      which is invoked from the facelet:

      <h:form id="LoginForm" acceptcharset="UTF-8">
          <p>
              <h:outputLabel for="userid" value="#{msg['Login.userid.label']}" />
              <h:inputText id="userid" value="#{loginBean.userId}" />
          </p>
          <p>
              <h:outputLabel for="password" value="#{msg['Login.password.label']}" />
              <h:inputSecret id="password" value="#{loginBean.password}" />
          </p>
              <h:commandButton id="submit" value="#{msg['Login.submit.label']}"
                  action="#{loginBean.authenticate}" />
      </h:form>
      

      Note the use of the POST-REDIRECT-GET pattern for the case where the authentication is successful. I've left a bit of code that relates to invalidation of the current session before redirection, to prevent session fixation attacks. The binding of the User entity to the Principal would be done in new session, as long as it is done in a session scoped bean.

      这篇关于绑定用户实体和GlassFish Principal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆