13 Replies Latest reply on Sep 25, 2013 9:35 PM by ybxiang.china

    Logging Out of a Security Domain

    muchomagic

      We've configured our web applications to use a database authentication module within a security domain in JBoss AS 7. We also configured SSO within the virtual server and for all the applications in their jboss-web.xml. It all works great, except logging out. I've read many places that invaliding the HTTP session is sufficient to log a user out, but that doesn't work.

       

      Some of the configuration:

       

                      <security-domain name="abc-security-domain" cache-type="infinispan">

                          <authentication>

                              <login-module code="custom.package.DatabaseServerLoginModule" flag="required">

                                  <module-option name="dsJndiName" value="java:/jdbc/myds"/>

                                  <module-option name="principalsQuery" value="exec h_Get_UserAccount_Password ?"/>

                                  <module-option name="rolesQuery" value="exec h_Get_UserRoles ?, 0"/>

                                  <module-option name="clearLoginAttemptsQuery" value="exec h_Reset_UserAccount_LoginAttempts ?"/>

                              </login-module>

                          </authentication>

                      </security-domain>

       

                  <virtual-server name="default-host" enable-welcome-root="false">

                      <alias name="localhost"/>

                      <sso reauthenticate="false"/>

                  </virtual-server>

       

      Application jboss-web.xml:

       

      <jboss-web>

        <security-domain flushOnSessionInvalidation="true">abc-security-domain</security-domain>

        <context-root>/</context-root>

        <valve>

          <class-name>org.apache.catalina.authenticator.SingleSignOn</class-name>

        </valve>

      </jboss-web>

       

       

      Application web.xml:

       

          <login-config>

                <auth-method>FORM</auth-method>

                <realm-name>abc-security-domain</realm-name>

                 <form-login-config>

                  <form-login-page>/login.html</form-login-page>

                                    <form-error-page>/login.html</form-error-page>

               </form-login-config>      

         </login-config>

       

       

      The logout code:

       

      public class LogoutServlet extends HttpServlet {

         

      protected void doGet(HttpServletRequest request,

                  HttpServletResponse response) throws ServletException, IOException {

              response.setHeader("Cache-Control", "no-cache, no-store");

              response.setHeader("Pragma", "no-cache");

              response.setHeader("Expires", new java.util.Date().toString());

              request.getSession().invalidate();

              request.logout();

              response.sendRedirect("/");

          }

      }

       

      That does not work at all. After hitting that logout servlet, a user can continue to use the application just like they had before, with all of the roles they had before. They are not reprompted for username/password.

       

      After doing some research, it seems like I may need to log them out out of their LoginContext. Since I don't manually create the LoginContext, I can't find any way to retrieve it. I tried creating a stateful EJB and injecting the LoginContext into it, but the variable is always null.  If I inject the SessionContext into the EJB, it injects fine, but I can't find a way to get the LoginContext from the SessionContext.

       

       

      @Stateful

      public class EJB3Bean implements EJB3 {

       

                @Resource

                private LoginContext lc;

       

        @Resource

        private SessionContext sc;

       

       

                public void run() {

             System.out.println(lc == null); //always true

                               System.out.println(sc == null); //always false

        }

      }

       

       

      If anyone can help me figure out how to log a person out of a security domain, that would be very helpful. Thanks.

        • 1. Re: Logging Out of a Security Domain
          ybxiang.china

          Please read comments of my Log out servlet( it works well for me) carefully.

           

           

           

           

          package com.ybxiang.forum.servlet;

           

          import java.io.IOException;

          import java.util.logging.Logger;

           

          import javax.servlet.ServletException;

          import javax.servlet.annotation.WebServlet;

          import javax.servlet.http.HttpServlet;

          import javax.servlet.http.HttpServletRequest;

          import javax.servlet.http.HttpServletResponse;

           

          import com.ybxiang.forum.common.KnownJaasRoles;

           

          /**

          * http://www.technicaladvices.com/2012/07/08/the-effective-java-logout-servlet-code/

          *

          * The servlet must be put into <security-constraint> <web-resource-collection> in web.xml, if not, request.getUserPrincipal() will be null!

          *

          * 参见:com.ybxiang.forum.jsfmbean.JSFHelper.printFacesExternalContext()

          */

          @WebServlet("/logoutServlet")

          public class LogoutServlet extends HttpServlet {

              private static final long serialVersionUID = 1L;

              static final Logger logger = Logger.getLogger(LogoutServlet.class.getName());

           

              protected void doGet(HttpServletRequest request,

                      HttpServletResponse response) throws ServletException, IOException {

                  //********************** print JAAS info **********************//

                  boolean printJaasInfo = false;

                  if(printJaasInfo){

                      try{

                          //logger.info("LogoutServlet>request.getServletPath():"+request.getServletPath());//faces

                          //logger.info("LogoutServlet>request.getClass().getName():"+r1.getClass().getName());//org.apache.catalina.connector.RequestFacade

                          //logger.info("LogoutServlet>isAdministrator:"+request.isUserInRole(KnownJaasRoles.ADMINISTRATOR));//logged in with ybxiang:true

                          //logger.info("LogoutServlet>remoteUser:"+request.getRemoteUser());//ybxiang

                          logger.info("LogoutServlet>userPrincipalName:"+(request.getUserPrincipal()==null?"null":request.getUserPrincipal().getName()));//ybxiang

                      }catch(Exception e){

                          e.printStackTrace();

                      }

                  }

           

           

                  //********************** log out(clean something) **********************//

                  response.setHeader("Cache-Control", "no-cache, no-store");

                  response.setHeader("Pragma", "no-cache");

                  response.setHeader("Expires", new java.util.Date().toString());//http://www.coderanch.com/t/541412/Servlets/java/Logout-servlet-button 

                  //response.setHeader("Expires", "0")//http://www.coderanch.com/t/541412/Servlets/java/Logout-servlet-button

                  //

                  if(request.getSession(false)!=null){

                      request.getSession(false).invalidate();//remove session.

                  }

                  if(request.getSession()!=null){

                      request.getSession().invalidate();//remove session.

                  }

           

                  request.logout();//JAAS log out (from servlet specification)! It is a MUST!

           

                  //********************** print JAAS info again **********************//

                  if(printJaasInfo){

                      try{

                          //logger.info("LogoutServlet>request.getServletPath():"+request.getServletPath());//faces

                          //logger.info("LogoutServlet>request.getClass().getName():"+r1.getClass().getName());//org.apache.catalina.connector.RequestFacade

                          //logger.info("LogoutServlet>isAdministrator:"+request.isUserInRole(KnownJaasRoles.ADMINISTRATOR));//logged in with ybxiang:true

                          //logger.info("LogoutServlet>remoteUser:"+request.getRemoteUser());//ybxiang

                          logger.info("LogoutServlet>userPrincipalName:"+(request.getUserPrincipal()==null?"null":request.getUserPrincipal().getName()));//ybxiang

                      }catch(Exception e){

                          e.printStackTrace();

                      }

                  }

           

           

                  //********************** redirect **********************//

                  /**

                   * Here, if we redirect response to a secured page (example:request.getContextPath()+"/faces/console.xhtml"),

                   * then

                   * (a)<auth-method>BASIC</auth-method> will redirect secured page to login page and login automatically with username and password that are cached in web browser.

                   * (b)<auth-method>FORM</auth-method> will redirect secured page to login page too, but will NOT login automatically with username and password that are cached in web browser.

                   *

                   * Here, if we redirect response to a non-secured page, then the non-secured page is displayed (normal case).

                   */

                  response.sendRedirect(request.getContextPath());

              }

          }

          • 2. Re: Logging Out of a Security Domain
            ybxiang.china

            In EJB,

            (a) you can get Principle info like this:

            String callerPrincipalName =  ejbContext.getCallerPrincipal().getName();

             

            (b) you can check security like this:

             

            (1) ejbContext.isCallerInRole(KnownJaasRoles.ADMINISTRATOR)

             

            or

             

            (2)


            @RolesAllowed({KnownJaasRoles.ADMINISTRATOR})

            public void recycleTopic(Long topicId){


            Topic topic = em.find(Topic.class, topicId);


            Topic.recycle(topic);


            em.merge(topic);


            //


            em.createNamedQuery(Post.NamedQuery_name_RecyclePost)


            .setParameter(Post.NamedQuery_param_RecyclePost_topicId, topicId)


            .executeUpdate();

            }

             

             

            Why do you use LoginContext directly? Moreover, LoginContext is inconvenient.

            I think you are on the wrong way.

             

            • 3. Re: Logging Out of a Security Domain
              ybxiang.china

              In JSF MBean:

               

                  ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();

               

                  System.out.println("externalContext.getUserPrincipal().getName = "+externalContext.getUserPrincipal()==null?"null":externalContext.getUserPrincipal().getName());//authenticated user name

               

                  System.out.println("externalContext.isUserInRole(\"Administrator\") = "+externalContext.isUserInRole("Administrator"));//ybxiang is Administrator

               

               

              In Servlet/JSP/JSF web Page:

                                  <font color="red">
                                  <h:outputText rendered="#{request.getUserPrincipal() != null}" value="Welcome #{request.getUserPrincipal().getName()}" />
                                  <h:outputText rendered="#{request.isUserInRole('Administrator')}" value=" (Administrator)" />
                                  </font>

               

               

              I think you had better do NOT use LoginContext directly.

              • 4. Re: Logging Out of a Security Domain
                ybxiang.china

                GOOD LUCK my friend!

                • 5. Re: Logging Out of a Security Domain
                  blabno

                  nice! Do you know how to log someone else out?

                  I.e. as admin bans user?

                  • 6. Re: Logging Out of a Security Domain
                    ybxiang.china

                    I am studying it, I think it is NOT easy for web applications and EJB applications to kick off logged in users.

                     

                    In our product, we inject a session token to EJB calling, save it into DB. If we want to kick off some online users, we just remove the session token.

                     

                     

                    I think we can do the same thing in web applications: inject a special session token(we generat it!), remove it if the user is ban.

                    I will NOT use this method unless I can not find better method in 2 weeks.

                    • 7. Re: Logging Out of a Security Domain
                      blabno

                      Have you got a code sample?

                      I've created my custom Token login module for AS7 which grabs token from "Authorization" http request header and compares it with DB entry. The problem is that JAAS (or security domain, or whatever it is) is caching login result.

                      • 8. Re: Logging Out of a Security Domain
                        ybxiang.china

                        Have you got a code sample?

                        ~~~~~~~I am trying to find better solution. I have not coded it yet.

                         

                        The problem is that JAAS (or security domain, or whatever it is) is caching login result.

                        ~~~~~~~I am facing same issue. I think you can redirect the banned user to logout servlet(just a work around, I am trying to find better solution.)

                         

                        After I find a sound solution, I will post here.

                        • 9. Re: Logging Out of a Security Domain
                          ybxiang.china

                          Hi guy,

                           

                          I found the best solution:

                           

                          (1) set the LOGGED-IN user locked, and flush him in JAAS Cache, When he visit any page, JBoss will redirect him to such page:

                          JBWEB000065: HTTP Status 500 -


                          JBWEB000309: type JBWEB000067: Status report

                          JBWEB000068: message

                          JBWEB000069: description JBWEB000145: The server encountered an internal error that prevented it from fulfilling this request.

                           

                           

                           

                           

                          And, You can define error code in web.xml like this:

                          <error-page>
                          <error-code>500</error-code>
                          <location>/faces/lock-force-offline.xhtml</location>
                          </error-page>

                           

                           

                          The only problem is:

                          this error code definition does NOT work!

                          So I am discussing it here: https://community.jboss.org/thread/231547

                          • 10. Re: Logging Out of a Security Domain
                            ybxiang.china

                            This solution's complete code:

                             

                             

                            1. User.java

                            NOTE, we define a locked attribute!

                             

                            public class User implements Serializable{

                                private static final long serialVersionUID = 1L;

                                //...   

                             

                                private Long id;

                                private Date createDate = new Date();

                                private String username;

                                private String hashedPassword;

                                private String email ="";

                                private Date birthday = null;

                                private Boolean activated = false;

                                private Boolean locked = false;

                                private Date lockReleaseDate = new Date();

                             

                                //getter/setter...

                            }

                             

                             

                             

                            2. security domain definition in standalone.xml

                             

                            NOTE: we have "locked=false" in principalsQuery.

                             


                            <subsystem xmlns="urn:jboss:domain:security:1.2">

                            <security-domains>

                            <security-domain name="ybxiang-forum-jaas-security-domain" cache-type="default">

                            <authentication>

                            <login-module code="Remoting" flag="optional">

                            <module-option name="password-stacking" value="useFirstPass"/>

                            </login-module>

                            <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">

                            <module-option name="password-stacking" value="useFirstPass"/>

                            <module-option name="dsJndiName" value="java:jboss/datasources/ybxiangForumMySqlDataSource"/>

                            <module-option name="principalsQuery" value="SELECT hashedPassword FROM User WHERE username=? and activated=true and locked=false"/>

                            <module-option name="rolesQuery" value="SELECT DISTINCT r.name, 'Roles' FROM User u, User_UserGroup ug, UserGroup_JaasRole gr, JaasRole r WHERE u.id=ug.user_id AND ug.usergroup_id=gr.usergroup_id AND gr.jaasrole_id=r.id AND u.username=?"/>

                            <module-option name="hashAlgorithm" value="SHA-256"/>

                            <module-option name="hashEncoding" value="Base64"/>

                            <module-option name="hashCharset" value="UTF-8"/>

                            <module-option name="unauthenticatedIdentity" value="guest"/>

                            </login-module>

                            </authentication>

                            </security-domain>



                            </subsystem>

                             

                            4. web.xml in our WAR application

                             

                             

                            <?xml version="1.0" encoding="UTF-8"?>

                            <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

                                <context-param>

                                  <param-name>PARAMETER_ENCODING</param-name>

                                  <param-value>UTF-8</param-value>

                                </context-param>

                                <!-- Production,Development,UnitTest,SystemTest,Extension -->

                                <context-param>

                                    <param-name>javax.faces.PROJECT_STAGE</param-name>

                                    <param-value>Production</param-value>

                                </context-param>

                                <context-param>

                                    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>

                                    <!-- do NOT use 'server' value, because it will cause javax.faces.application.ViewExpiredException -->

                                    <param-value>client</param-value>

                                </context-param>

                                <!-- JBWEB000065: HTTP Status 500 - JBAS013323: Invalid User -->

                                <!-- -->

                                <error-page>

                                    <error-code>500</error-code>

                                    <location>/faces/lock-force-offline.xhtml</location>

                                </error-page>

                                ...

                            </web-app>

                             

                            3. Now, Let's suppose one bad user is keeping post garbage in our web site, We(admin) can lock him and force him offline:

                            3.1 lock him and flush his info in JAAS Cache:

                             


                            @RolesAllowed({KnownJaasRoles.ADMINISTRATOR})

                            public void lockUser(Long userId, Long days){


                            User u = em.find(User.class, userId);


                            u.setLocked(true);


                            u.setLockReleaseDate(new Date(System.currentTimeMillis()+days*86400000));//one day: 86400000 ms


                            em.merge(u);


                            //


                            cacheService.updateUserCache_ONLY_by_UserSessionOrLocalEJB(u);


                            jaasCacheSession.flushJavaarmForumSecurityDomainJaasCache(u.getUsername());//OK




                            }

                             

                             


                            @RolesAllowed({KnownJaasRoles.ADMINISTRATOR})

                            public void flushJaasCache(String securityDomain, String jaasUsername) {


                            try {



                            Object[] params = { jaasUsername };



                            String[] signature = { "java.lang.String" };

                             




                            javax.management.MBeanServerConnection mbeanServerConnection




                            = java.lang.management.ManagementFactory





                            .getPlatformMBeanServer();



                            javax.management.ObjectName mbeanName = new javax.management.ObjectName(





                            "jboss.as:subsystem=security,security-domain="







                            + securityDomain);



                            mbeanServerConnection.invoke(mbeanName, "flushCache", params,





                            signature);


                            } catch (Exception e) {



                            throw new SecurityException(e);


                            }

                            }

                            @RolesAllowed({KnownJaasRoles.ADMINISTRATOR})

                            public void flushJavaarmForumSecurityDomainJaasCache(){


                            flushJaasCache(coreService.getJavaarmForumJaasSecurityDomain());

                            }

                            /**

                            * 为某用户增加/删除权限之后,下面的方法无法清洗该用户对应的JAAS Cache!

                            */

                            @RolesAllowed({KnownJaasRoles.ADMINISTRATOR})

                            public void flushJavaarmForumSecurityDomainJaasCache(String jaasUsername){


                            flushJaasCache(coreService.getJavaarmForumJaasSecurityDomain(),jaasUsername);

                            }

                             

                             

                             

                             

                            3.2 now, the bad user will be redirect to /faces/lock-force-offline.xhtml if he access our web site. We can force him offline in lock-force-offline.xhtml like this:

                             

                             

                            <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

                                "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

                            <ui:composition xmlns="http://www.w3.org/1999/xhtml"

                                            xmlns:ui="http://java.sun.com/jsf/facelets"

                                            xmlns:f="http://java.sun.com/jsf/core"

                                            xmlns:h="http://java.sun.com/jsf/html"

                                            template="/template/template_gohome.xhtml">

                             

                                <ui:define name="title">

                                    <h:outputText value="#{messages['ybxiang.javaarm.user.locked.force.offline.warning']}" />

                                </ui:define>

                             

                                <ui:define name="headMetaData">

                                </ui:define>

                             

                                <ui:define name="body">

                                    <h:form id="form" >

                                        <br />

                                        <h:outputText style="color:red;" value="#{messages['ybxiang.javaarm.user.locked.force.offline.warning']}" />

                             

                                        <script type="text/javascript">

                                            window.setInterval(function(){window.top.location.href="http://#{request.serverName}/logoutServlet";}, 2000);

                                        </script>

                                    </h:form>

                                </ui:define>

                            </ui:composition>

                             

                             

                             

                            I have resolved this problem perfectly!

                             

                            Good luck!

                            • 11. Re: Logging Out of a Security Domain
                              blabno

                              Could you fix formating, as parts of code are  missing. You probably forgot to substitute < with &amp;lt;

                              • 12. Re: Logging Out of a Security Domain
                                ybxiang.china

                                Please read my article: JBoss AS 7.2.0 - Java EE application development - 09.How to monitor online users and kick user off line.

                                 

                                And I hope other articles are useful to you:

                                JBoss AS 7.2.0 - Java EE application development - 00.Sumarry (English Version)

                                 

                                 

                                If you still can NOT solve your problem, please send email to me: ybxiang@gmail.com. I send some source code to you directly.

                                (I really dislike the JBoss Rich Text Editor! I think Yahoo YUI is better. I use it in my web)

                                • 13. Re: Logging Out of a Security Domain
                                  ybxiang.china

                                  as parts of code are  missing.

                                  ~~~~~~~I removed them. They are trivial codes.