Announcement Announcement Module
Collapse
No announcement yet.
Sharing authentication between webapp & rich client Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Sharing authentication between webapp & rich client

    Hi,

    Here's the scenario.

    My webapp had been integrated with Acegi for handling authentication and now I want to integrate a webstart rich client to the webapp.

    For the convinience of users, it's natural that we want to share the web browser's session with the rich client. Inspired by http://forum.springframework.org/showthread.php?t=13983, I added the web browser's session ID to the JNLP, so that whenever the rich client makes RMI through HTTP (which services are also guarded by Acegi) the rich client can be seen as if it had been authenticated against the server-side.

    At one of the service available to the rich client, I have a service, exposed to HTTP RMI using Spring, which returns the user's current session to the rich client. It is simply

    Code:
    return (User) SecureContextUtils.getSecureContext().getAuthentication().getPrincipal();
    which is exactly the same as what I used on web controllers. However, I found it always returns an anonymousUser to me, instead of the authenticated User object which I expected.

    Is there anything I missed? I also think just using the session ID to share the authentication session between the web browser and the rich client seems not a good practice. Is there any better way to do so?

    Thanks in advance.
    Last edited by robyn; May 16th, 2006, 03:49 AM.

  • #2
    Would you please post some of your server-side XML and the Java code you're using? It's difficult to offer specific suggestions without a clearer idea of your configuration and approach.

    Comment


    • #3
      My web.xml: (snippet)

      Code:
      <filter>
          <filter-name>Acegi Filter Chain Proxy</filter-name>
          <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
          <init-param>
              <param-name>targetClass</param-name>
              <param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
          </init-param>
      </filter>
      
      <filter-mapping>
          <filter-name>Acegi Filter Chain Proxy</filter-name>
          <url-pattern>*.action</url-pattern>
      </filter-mapping>
      	
      <filter-mapping>
          <filter-name>Acegi Filter Chain Proxy</filter-name>
          <url-pattern>/system_login</url-pattern>
      </filter-mapping>
      
      <filter-mapping>
          <filter-name>Acegi Filter Chain Proxy</filter-name>
          <url-pattern>/meeting-service/*</url-pattern>
      </filter-mapping>	
      
      <servlet>
          <servlet-name>action</servlet-name>
          <servlet-class>webwork.dispatcher.ServletDispatcher</servlet-class>
          <load-on-startup>1</load-on-startup>
      </servlet>
      	
      <servlet-mapping>
          <servlet-name>action</servlet-name>
          <url-pattern>*.action</url-pattern>
      </servlet-mapping>
      
      <servlet>
          <servlet-name>meeting</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <load-on-startup>2</load-on-startup>		
      </servlet>
      	
      <servlet-mapping>
          <servlet-name>meeting</servlet-name>
          <url-pattern>/meeting-service/*</url-pattern>
      </servlet-mapping>
      My security.xml, where all Acegi-related stuff are configured (snippet again):
      Code:
      <bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
          <property name="filterInvocationDefinitionSource">
              <value>
              PATTERN_TYPE_APACHE_ANT
                  /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter
              </value>
          </property>
      </bean>
      	
      <bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
          <property name="providers">
              <list>
                  <ref local="daoAuthenticationProvider"/>
                  <ref local="anonymousAuthenticationProvider"/>
                  <ref local="rememberMeAuthenticationProvider"/>
              </list>
          </property>
      </bean>
      
      <bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
          <property name="authenticationManager"><ref bean="authenticationManager"/></property>
          <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
          <property name="objectDefinitionSource">
              <value>
                  PATTERN_TYPE_APACHE_ANT
                  /Logout.action=ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_STUDENT,ROLE_TEACHER
                  /LoginFailed.action*=ROLE_ANONYMOUS
                  /Homepage.action=ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_STUDENT,ROLE_TEACHER
                  /ForgetPassword.action=ROLE_ANONYMOUS
                  /*.action=ROLE_ADMIN,ROLE_STUDENT,ROLE_TEACHER
                  /meeting-service/**=ROLE_ADMIN,ROLE_STUDENT,ROLE_TEACHER
              </value>
          </property>
      </bean>
      All other stuff in security are essentially copying the sample, with some tweaks like using my own DAO for daoAuthenticationProvider, login URL, page to redirect upon success authentication, etc.

      meeting-servlet's bean definition:
      Code:
      <bean name="/UserSession" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
          <property name="service">
              <bean class="meeting.web.service.UserSessionImpl" />
          </property>
          <property name="serviceInterface">
              <value>meeting.web.service.UserSession</value>
          </property>
      </bean>
      At the JNLP sent to client I also send in the sessionID as one of the properties.

      At the webstart client I created a factory class for creating proxies with Spring's HttpInvokerClientInterceptor and ProxyFactory:
      Code:
      import org.springframework.aop.framework.ProxyFactory;
      import org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor;
      
      public class HttpRmiProxyFactory
      &#123;
      	public static Object createProxy&#40;Class clazz, String url&#41;
      	&#123;
      		HttpInvokerClientInterceptor interceptor = new HttpInvokerClientInterceptor&#40;&#41;;
      		interceptor.setServiceUrl&#40;url&#41;;
      		return ProxyFactory.getProxy&#40;clazz, interceptor&#41;; 
      	&#125;
      &#125;
      Where URLs are in the form like
      Code:
      http&#58;//server/meeting-service/UserSession?JSESSIONID=<sessionID>
      Inside UserSessionImpl's getCurrentUser() method:
      Code:
      public User getCurrentUser&#40;&#41;
      &#123;
          log.debug&#40;"Current user session&#58; " + SecureContextUtils.getSecureContext&#40;&#41;.getAuthentication&#40;&#41;.getPrincipal&#40;&#41;&#41;;
          if&#40;SecureContextUtils.getSecureContext&#40;&#41;.getAuthentication&#40;&#41;.getPrincipal&#40;&#41; instanceof User&#41;
          &#123;
              return &#40;User&#41; SecureContextUtils.getSecureContext&#40;&#41;.getAuthentication&#40;&#41;.getPrincipal&#40;&#41;;
          &#125;
          else return null;
      &#125;
      I switched on debug logging level for net.sf.acegisecurity.ui and net.sf.acegisecurity.providers.

      Upon login from the web browser I have
      Code:
      08&#58;22&#58;22,975 DEBUG &#91;AbstractProcessingFilter.doFilter&#93; Request is to process authentication
      08&#58;22&#58;22,975 DEBUG &#91;AbstractProcessingFilter.doFilter&#93; Request is to process authentication
      08&#58;22&#58;22,975 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.dao.Da
      oAuthenticationProvider
      08&#58;22&#58;22,975 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.dao.Da
      oAuthenticationProvider
      08&#58;22&#58;22,975 DEBUG &#91;EhCacheBasedUserCache.getUserFromCache&#93; Cache hit&#58; false; username&#58; admin
      08&#58;22&#58;22,975 DEBUG &#91;EhCacheBasedUserCache.getUserFromCache&#93; Cache hit&#58; false; username&#58; admin
      08&#58;22&#58;23,241 DEBUG &#91;EhCacheBasedUserCache.putUserInCache&#93; Cache put&#58; admin
      08&#58;22&#58;23,241 DEBUG &#91;EhCacheBasedUserCache.putUserInCache&#93; Cache put&#58; admin
      08&#58;22&#58;23,241 INFO  &#91;LoggerListener.onApplicationEvent&#93; Authentication success for user&#58; admin; details&#58; net.sf.acegisecu
      rity.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.worker1
      
      08&#58;22&#58;23,241 INFO  &#91;LoggerListener.onApplicationEvent&#93; Authentication success for user&#58; admin; details&#58; net.sf.acegisecu
      rity.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.worker1
      
      08&#58;22&#58;23,241 DEBUG &#91;AbstractProcessingFilter.successfulAuthentication&#93; Authentication success&#58; net.sf.acegisecurity.prov
      iders.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - userID ="admin", accountCode="5722-3776-
      2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.acegisecurity.ui.WebAuthenticationDetails@5b540e&#58; Re
      moteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.worker1; Granted Authorities&#58; ROLE_ADMIN, ROLE_GLO
      BAL_PUBLIC_FILE_READ
      08&#58;22&#58;23,241 DEBUG &#91;AbstractProcessingFilter.successfulAuthentication&#93; Authentication success&#58; net.sf.acegisecurity.prov
      iders.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - userID ="admin", accountCode="5722-3776-
      2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.acegisecurity.ui.WebAuthenticationDetails@5b540e&#58; Re
      moteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.worker1; Granted Authorities&#58; ROLE_ADMIN, ROLE_GLO
      BAL_PUBLIC_FILE_READ
      08&#58;22&#58;23,256 DEBUG &#91;AbstractProcessingFilter.successfulAuthentication&#93; Updated ContextHolder to contain the following Au
      thentication&#58; 'net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - userID ="adm
      in", accountCode="5722-3776-2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.acegisecurit
      y.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.worker1; G
      ranted Authorities&#58; ROLE_ADMIN, ROLE_GLOBAL_PUBLIC_FILE_READ'
      08&#58;22&#58;23,256 DEBUG &#91;AbstractProcessingFilter.successfulAuthentication&#93; Updated ContextHolder to contain the following Au
      thentication&#58; 'net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - userID ="adm
      in", accountCode="5722-3776-2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.acegisecurit
      y.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.worker1; G
      ranted Authorities&#58; ROLE_ADMIN, ROLE_GLOBAL_PUBLIC_FILE_READ'
      08&#58;22&#58;23,256 DEBUG &#91;AbstractProcessingFilter.successfulAuthentication&#93; Redirecting to target URL from HTTP Session &#40;or d
      efault&#41;&#58; /Dashboard.action
      08&#58;22&#58;23,256 DEBUG &#91;AbstractProcessingFilter.successfulAuthentication&#93; Redirecting to target URL from HTTP Session &#40;or d
      efault&#41;&#58; /Dashboard.action
      08&#58;22&#58;23,350 DEBUG &#91;RememberMeProcessingFilter.doFilter&#93; ContextHolder not replaced with remember-me token, as ContextHo
      lder already contained&#58; 'net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - us
      erID ="admin", accountCode="5722-3776-2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.ac
      egisecurity.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.
      worker1; Granted Authorities&#58; ROLE_ADMIN, ROLE_GLOBAL_PUBLIC_FILE_READ'
      08&#58;22&#58;23,350 DEBUG &#91;RememberMeProcessingFilter.doFilter&#93; ContextHolder not replaced with remember-me token, as ContextHo
      lder already contained&#58; 'net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - us
      erID ="admin", accountCode="5722-3776-2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.ac
      egisecurity.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.
      worker1; Granted Authorities&#58; ROLE_ADMIN, ROLE_GLOBAL_PUBLIC_FILE_READ'
      08&#58;22&#58;23,350 DEBUG &#91;AnonymousProcessingFilter.doFilter&#93; ContextHolder not replaced with anonymous token, as ContextHolde
      r already contained&#58; 'net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - userI
      D ="admin", accountCode="5722-3776-2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.acegi
      security.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.wor
      ker1; Granted Authorities&#58; ROLE_ADMIN, ROLE_GLOBAL_PUBLIC_FILE_READ'
      08&#58;22&#58;23,350 DEBUG &#91;AnonymousProcessingFilter.doFilter&#93; ContextHolder not replaced with anonymous token, as ContextHolde
      r already contained&#58; 'net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken@1eb8f6d&#58; Username&#58; User - userI
      D ="admin", accountCode="5722-3776-2953"; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; false; Details&#58; net.sf.acegi
      security.ui.WebAuthenticationDetails@5b540e&#58; RemoteIpAddress&#58; 127.0.0.1; SessionId&#58; 819A9D625DE6571EF0D4B3A11A11FAAE.wor
      ker1; Granted Authorities&#58; ROLE_ADMIN, ROLE_GLOBAL_PUBLIC_FILE_READ'
      08&#58;22&#58;23,350 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.dao.Da
      oAuthenticationProvider
      08&#58;22&#58;23,350 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.dao.Da
      oAuthenticationProvider
      08&#58;22&#58;23,350 DEBUG &#91;EhCacheBasedUserCache.getUserFromCache&#93; Cache hit&#58; true; username&#58; admin
      08&#58;22&#58;23,350 DEBUG &#91;EhCacheBasedUserCache.getUserFromCache&#93; Cache hit&#58; true; username&#58; admin
      Then I launched the webstart client, where the JNLP is generated dynamically by JSP, with the webstart cache cleared. Then I got
      Code:
      08&#58;24&#58;48,709 DEBUG &#91;RememberMeProcessingFilter.doFilter&#93; Replaced ContextHolder with remember-me token&#58; 'null'
      08&#58;24&#58;48,709 DEBUG &#91;RememberMeProcessingFilter.doFilter&#93; Replaced ContextHolder with remember-me token&#58; 'null'
      08&#58;24&#58;48,709 DEBUG &#91;AnonymousProcessingFilter.doFilter&#93; Replaced ContextHolder with anonymous token&#58; 'net.sf.acegisecuri
      ty.providers.anonymous.AnonymousAuthenticationToken@1cba031&#58; Username&#58; anonymousUser; Password&#58; &#91;PROTECTED&#93;; Authenticat
      ed&#58; true; Details&#58; null; Granted Authorities&#58; ROLE_ANONYMOUS'
      08&#58;24&#58;48,709 DEBUG &#91;AnonymousProcessingFilter.doFilter&#93; Replaced ContextHolder with anonymous token&#58; 'net.sf.acegisecuri
      ty.providers.anonymous.AnonymousAuthenticationToken@1cba031&#58; Username&#58; anonymousUser; Password&#58; &#91;PROTECTED&#93;; Authenticat
      ed&#58; true; Details&#58; null; Granted Authorities&#58; ROLE_ANONYMOUS'
      08&#58;24&#58;48,709 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.anonym
      ous.AnonymousAuthenticationProvider
      08&#58;24&#58;48,709 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.anonym
      ous.AnonymousAuthenticationProvider
      08&#58;24&#58;48,725 DEBUG &#91;AuthenticationProcessingFilterEntryPoint.commence&#93; Redirecting to&#58; http&#58;//localhost/Home
      page.action
      08&#58;24&#58;48,725 DEBUG &#91;AuthenticationProcessingFilterEntryPoint.commence&#93; Redirecting to&#58; http&#58;//localhost/Home
      page.action
      08&#58;24&#58;48,725 DEBUG &#91;RememberMeProcessingFilter.doFilter&#93; ContextHolder not replaced with remember-me token, as ContextHo
      lder already contained&#58; 'net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken@1cba031&#58; Username&#58; anonym
      ousUser; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; true; Details&#58; null; Granted Authorities&#58; ROLE_ANONYMOUS'
      08&#58;24&#58;48,725 DEBUG &#91;RememberMeProcessingFilter.doFilter&#93; ContextHolder not replaced with remember-me token, as ContextHo
      lder already contained&#58; 'net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken@1cba031&#58; Username&#58; anonym
      ousUser; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; true; Details&#58; null; Granted Authorities&#58; ROLE_ANONYMOUS'
      08&#58;24&#58;48,725 DEBUG &#91;AnonymousProcessingFilter.doFilter&#93; ContextHolder not replaced with anonymous token, as ContextHolde
      r already contained&#58; 'net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken@1cba031&#58; Username&#58; anonymous
      User; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; true; Details&#58; null; Granted Authorities&#58; ROLE_ANONYMOUS'
      08&#58;24&#58;48,725 DEBUG &#91;AnonymousProcessingFilter.doFilter&#93; ContextHolder not replaced with anonymous token, as ContextHolde
      r already contained&#58; 'net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationToken@1cba031&#58; Username&#58; anonymous
      User; Password&#58; &#91;PROTECTED&#93;; Authenticated&#58; true; Details&#58; null; Granted Authorities&#58; ROLE_ANONYMOUS'
      08&#58;24&#58;48,725 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.anonym
      ous.AnonymousAuthenticationProvider
      08&#58;24&#58;48,725 DEBUG &#91;ProviderManager.doAuthentication&#93; Authentication attempt using net.sf.acegisecurity.providers.anonym
      ous.AnonymousAuthenticationProvider
      It seems I could not access the HTTP remoting service from the webstart client using the session ID alone.

      I do know my webstart client has problems that it eventually needs a way to authenticate itself against the server anyway (since webstart will cache the files). But what if the user had been logged in using his/her web browser? It's where I have the thought of sharing session ID between the webstart client and the web browser.

      P.S. Still using 0.8.0.

      Thanks again.

      Comment


      • #4
        OK, now it makes more sense.

        I like your general approach, which as I understand is:

        1. Login using a webapp, meaning of course you end up with a HttpSession server-side.
        2. Make a request to an MVC Controller which responds by dynamically generating a JNLP that contains a ;jsessionid in the URL.
        3. Have your Web Start client make a request to /UserSession over HttpInvoker, and presenting the correct ;jsessionid as per the JNLP.
        4. Have /UserSession reply with a UserDetails.
        5. On the client, use the UserDetails for information on the principal's GrantedAuthority[]s, their username, and their password.

        This should work. It looks like your key problem is step 3. I suggest the ;jsessionid is not being presented to the server. You might need to write a debugging filter to see the HttpSession IDs of each request.

        Comment


        • #5
          Yeap... and your message reminded me where the blindspot is.

          The problem is that ;jsessionid, where I was using ?JSESSIONID instead.

          Thanks again for your time.

          Comment


          • #6
            I use above approach to share authentication between web app and rich clients (applets), too. It works well. I want to add some point about ;jsessionid issue. That syntax is almost de-facto among app. servers, but in websphere 6.0 I was able to make it working by using ?JSESSIONID, after a long struggle with was admin console. Just a little reminder

            Regards

            Kenan Sevindik
            http://www.jroller.com/page/ksevindik

            Comment


            • #7
              How to change the sessionId notation

              You can change the ?JSESSIONID notation via IBM Admin Console per deployed modules.

              for v6.0.x:
              Applications > Enterprise Applications > $your_application$ > Session Management > Enable Cookies (click on it) > Cookie Name

              Cheers..

              Comment

              Working...
              X