Hi Borut,

the problem was that both the OpenIdProcessingFilter and the filter used for form-based authentication both were configured for the same url, namely spring-security.check.url. Therefore both filters tried to process the credentials entered, which had to fail. I updated the wiki article. Basically it's all about introducing a new symbol, spring-security.openidcheck.url with the value "/j_spring_openid_security_check" and configuring the login page and the OpenId processing filter to use this instead of the spring-security.check.url. Then you can have to forms on your login page, one for openid login and the other for form-based logins and everything should work.

Cheers,

Uli


Borut Bolčina schrieb:
Sure!

2008/12/12 Ulrich Stärk <u...@spielviel.de>

Please give me some time to have a look, atm I'm busy writing a paper for a
conference in january.

Cheers,

Uli

Borut Bolčina schrieb:

 Hello Ulrich,
I hope you don't mind me writing you directly. I am trying to implement
OpenID and Username/Password authentication. I read your great article at
T5
wiki.

My goal: authenticate via DaoAuthenticationProvider if user inputs
username
and password OR authenticate via OpenIDAuthenticationProvider if user
enters
openid url.

The problem: OpenIDAuthenticationProvider is trying to authenticate
eventhough it is contributed as second provider.

Can you please have a look at the AppModule code and suggest a correction?

AppModule.java
============
import java.io.IOException;

import
nu.localhost.tapestry5.springsecurity.services.SpringSecurityServices;
import

nu.localhost.tapestry5.springsecurity.services.internal.HttpServletRequestFilterWrapper;

import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.ServiceBinder;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.InjectService;
import org.apache.tapestry5.ioc.annotations.Local;
import org.apache.tapestry5.ioc.annotations.Value;
import org.apache.tapestry5.services.HttpServletRequestFilter;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.RequestFilter;
import org.apache.tapestry5.services.RequestHandler;
import org.apache.tapestry5.services.Response;
import org.slf4j.Logger;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.providers.AuthenticationProvider;
import org.springframework.security.providers.dao.SaltSource;
import org.springframework.security.providers.encoding.PasswordEncoder;
import

org.springframework.security.providers.openid.OpenIDAuthenticationProvider;
import

org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter;
import org.springframework.security.ui.rememberme.RememberMeServices;
import org.springframework.security.userdetails.UserDetailsService;

/**
 * This module is automatically included as part of the Tapestry IoC
Registry, it's a good place to configure and extend
 * Tapestry, or to place your own service definitions.
 */
public class AppModule {
   public static void bind(ServiceBinder binder) {
       binder.bind(PersistenceManager.class,
PersistenceManagerImpl.class);
   }

   public static void
contributeApplicationDefaults(MappedConfiguration<String, String>
configuration) {
       configuration.add(SymbolConstants.SUPPORTED_LOCALES,
"sl_SI,sr,en");

       // The factory default is true but during the early stages of an
application
       // overriding to false is a good idea. In addition, this is often
overridden
       // on the command line as -Dtapestry.production-mode=false
       configuration.add(SymbolConstants.PRODUCTION_MODE, "false");
       configuration.add(SymbolConstants.COMPRESS_WHITESPACE, "false");

       configuration.add("spring-security.failure.url", "/login/failed");
       // configuration.add( "spring-security.accessDenied.url",
"/forbidden" );
       // configuration.add(
       // "spring-security.check.url",
       // "/j_spring_security_check" );
       configuration.add("spring-security.target.url", "/index");
       // configuration.add( "spring-security.afterlogout.url", "/" );
       // configuration.add( "spring-security.rememberme.key",
"REMEMBERMEKEY" );
       configuration.add("spring-security.loginform.url", "/login");
       // configuration.add( "spring-security.force.ssl.login", "false" );
       // configuration.add( "spring-security.anonymous.key",
"acegi_anonymous" );
       // configuration.add(
       // "spring-security.anonymous.attribute",
       // "anonymous,ROLE_ANONYMOUS" );
       configuration.add( "spring-security.password.salt", "DEADBEEF" );
   }

   /**
    * This is a service definition, the service will be named
"TimingFilter". The interface, RequestFilter, is used
    * within the RequestHandler service pipeline, which is built from the
RequestHandler service configuration.
    * Tapestry IoC is responsible for passing in an appropriate Logger
instance. Requests for static resources are
    * handled at a higher level, so this filter will only be invoked for
Tapestry related requests.
    *
    * <p>
    * Service builder methods are useful when the implementation is inline
as an inner class (as here) or require some
    * other kind of special initialization. In most cases, use the static
bind() method instead.
    *
    * <p>
    * If this method was named "build", then the service id would be taken
from the service interface and would be
    * "RequestFilter". Since Tapestry already defines a service named
"RequestFilter" we use an explicit service id
    * that we can reference inside the contribution method.
    */
   public RequestFilter buildTimingFilter(final Logger log) {
       return new RequestFilter() {
           public boolean service(Request request, Response response,
RequestHandler handler) throws IOException {
               long startTime = System.currentTimeMillis();

               try {
                   // The responsibility of a filter is to invoke the
corresponding method
                   // in the handler. When you chain multiple filters
together, each filter
                   // received a handler that is a bridge to the next
filter.

                   return handler.service(request, response);
               } finally {
                   long elapsed = System.currentTimeMillis() - startTime;

                   log.info(String.format("Request time: %d ms",
elapsed));
               }
           }
       };
   }

   /**
    * This is a contribution to the RequestHandler service configuration.
This is how we extend Tapestry using the
    * timing filter. A common use for this kind of filter is transaction
management or security. The @Local annotation
    * selects the desired service by type, but only from the same module.
Without @Local, there would be an error due
    * to the other service(s) that implement RequestFilter (defined in
other modules).
    */
   public void
contributeRequestHandler(OrderedConfiguration<RequestFilter>
configuration, @Local RequestFilter filter) {
       // Each contribution to an ordered configuration has a name, When
necessary, you may
       // set constraints to precisely control the invocation order of the
contributed filter
       // within the pipeline.

       configuration.add("Timing", filter);
   }

   /* COMMON UserDetailsService */
   public static UserDetailsService buildUserDetailsService(@Inject
PersistenceManager persistenceManager, final Logger log) {
       return new UserDetailsServiceImpl(persistenceManager,log);
   }

   /* USERNAME, PASSWORD */
//    public static UserDetailsService
buildUserDetailsWithUsernameAndPasswordService(/*...@inject PasswordEncoder
encoder,
//            @Inject SaltSource salt, */final Logger log) {
//        return new UserDetailsWithUsernameAndPasswordService(/*encoder,
salt, */log);
//    }

   /* OPENID */
//    public static UserDetailsService buildUserDetailsWithOpenIDService()
{
//        return new UserDetailsWithOpenIDServiceImpl();
//    }

   public static OpenIDAuthenticationProvider
buildOpenIDAuthenticationProvider(
           @InjectService("UserDetailsWithOpenIDService")
UserDetailsService userDetailsService) throws Exception {
       OpenIDAuthenticationProvider provider = new
OpenIDAuthenticationProvider();

       provider.setUserDetailsService(userDetailsService);
       provider.afterPropertiesSet();

       return provider;
   }

   public static void
contributeProviderManager(OrderedConfiguration<AuthenticationProvider>
configuration,

           @InjectService("DaoAuthenticationProvider")
AuthenticationProvider daoAuthenticationProvider,
           @InjectService("OpenIDAuthenticationProvider")
AuthenticationProvider openIdAuthenticationProvider) {

       configuration.add("daoAuthenticationProvider",
daoAuthenticationProvider);
       configuration.add("openIDAuthenticationProvider",
openIdAuthenticationProvider);
   }


   public static OpenIDAuthenticationProcessingFilter
buildRealOpenIDAuthenticationProcessingFilter(
           @SpringSecurityServices final AuthenticationManager manager,
           @SpringSecurityServices final RememberMeServices
rememberMeServices,
           @Inject @Value("${spring-security.check.url}") final String
authUrl,
           @Inject @Value("${spring-security.target.url}") final String
targetUrl,
           @Inject @Value("${spring-security.failure.url}") final String
failureUrl) throws Exception {
       OpenIDAuthenticationProcessingFilter filter = new
OpenIDAuthenticationProcessingFilter();

       filter.setAuthenticationManager(manager);
       filter.setAuthenticationFailureUrl(failureUrl);
       filter.setDefaultTargetUrl(targetUrl);
       filter.setFilterProcessesUrl(authUrl);
       filter.setRememberMeServices(rememberMeServices);
       filter.afterPropertiesSet();

       return filter;
   }

   public static HttpServletRequestFilter
buildOpenIDAuthenticationProcessingFilter(
           final OpenIDAuthenticationProcessingFilter filter) {
       return new HttpServletRequestFilterWrapper(filter);
   }

   public static void contributeHttpServletRequestHandler(
           OrderedConfiguration<HttpServletRequestFilter> configuration,

           @InjectService("OpenIDAuthenticationProcessingFilter")
HttpServletRequestFilter openIDAuthenticationProcessingFilter) {
       configuration.add("openIDAuthenticationProcessingFilter",

       openIDAuthenticationProcessingFilter,

       "before:springSecurityAuthenticationProcessingFilter",

       "after:springSecurityHttpSessionContextIntegrationFilter");
   }

}



Is there a problem in this last method
("before:springSecurityAuthenticationProcessingFilter")? Both forms (for
u/p
and openid url) are on the same page.

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>
   <head>
       <title>Najdi.si prijavna stran</title>
       <link rel="stylesheet" type="text/css"
href="${asset:context:css/iopenid.css}" />
   </head>
   <body>
       <t:layout xmlns:t="
http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";>

           <div style="margin-left: 50px">
               <t:block t:id="loginWithUserNameAndPassword">
                   <p>
                       Prijavite se z uporabniškim imenom in geslom ali z
                       <a t:type="actionlink" t:id="refreshOpenIDZone"
href="#" t:zone="loginZone">OpenID</a>
                   </p>
                   <form xmlns:t="
http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";
action="${loginCheckUrl}" method="POST">
                       <t:if test="failed">
                           Napačno uporabniško ime ali geslo!
                           <br />
                       </t:if>
                       <label class="username"
for="j_username">uporabniško
ime:</label>
                       <input class="username" name="j_username"
type="text" size="10" maxlength="30" />
                       <label class="password"
for="j_password">geslo:</label>
                       <input class="password" name="j_password"
type="password" size="10" maxlength="30" />
                       <input id="submit" class="submit" type="submit"
value="log in" />
                   </form>
                   <p>Še nimate Najdi.si računa? <a t:type="pagelink"
t:page="Register" href="#">Registrirajte se!</a></p>
                   <p><a t:type="pagelink" t:page="RetrievePassword"
href="#">Pozabil sem geslo.</a></p>
               </t:block>
               <t:block t:id="loginWithOpenID">
                   <p>
                       Prijavite se z
                       <a t:type="actionlink"
t:id="refreshUsernamePasswordZone" href="#"
t:zone="loginZone">uporabniškim
imenom in
                           geslom</a>
                       ali z OpenID
                   </p>
                   <form xmlns:t="
http://tapestry.apache.org/schema/tapestry_5_0_0.xsd";
action="${loginCheckUrl}" method="POST">
                       <t:if test="failed">
                           Napaka!
                           <br />
                       </t:if>
                       <label class="username"
for="j_username">OpenID:</label>
                       <input class="username" name="j_username"
type="text" size="30" />
                       <input id="submit" class="submit" type="submit"
value="log in" />
                   </form>
                   <p>Še nimate Najdi.si računa? Ob <a t:type="pagelink"
t:page="Register" href="#">registraciji</a> dobite tudi Najdi.si
OpenID.</p>
               </t:block>
               <t:zone t:id="loginZone">
                   <t:delegate to="loginWithUserNameAndPassword" />
               </t:zone>
               <br />

           </div>

       </t:layout>
   </body>
</html>


public class Login {
   @Inject
   @Property
   private Block loginWithUserNameAndPassword;

   @Inject
   @Property
   private Block loginWithOpenID;

   @Inject
   @Property
   private Request _request;

   void onActionFromRefreshPage() {
       // Nothing to do - the page will get fresh times as it is rendered.
   }

   Block onActionFromRefreshOpenIDZone() {
       // Check this is an AJAX link - if the link is clicked before the
DOM is fully loaded then AJAX behaviour won't
       // be there so don't try returning a Block. See
https://issues.apache.org/jira/browse/TAP5-1 .
       if (!_request.isXHR()) {
           return null;
       }

       // Return the zone we want rendered. Without Ajax we'd typically
return the page we want rendered.
       return loginWithOpenID;
   }

   Block onActionFromRefreshUsernamePasswordZone() {
       // Check this is an AJAX link - if the link is clicked before the
DOM is fully loaded then AJAX behaviour won't
       // be there so don't try returning a Block. See
https://issues.apache.org/jira/browse/TAP5-1 .
       if (!_request.isXHR()) {
           return null;
       }

       // Return the zone we want rendered. Without Ajax we'd typically
return the page we want rendered.
       return loginWithUserNameAndPassword;
   }

   @Inject
   @Value("${spring-security.check.url}")
   private String checkUrl;

   @Inject
   private Request request;

   private boolean failed = false;

   public boolean isFailed() {
       return failed;
   }

   public String getLoginCheckUrl() {
       return request.getContextPath() + checkUrl;
   }

   void onActivate(String extra) {
       if (extra.equals("failed")) {
           failed = true;
       }
   }

I would be really gratefull for your help!

Regards,
Borut




---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org

Reply via email to