100 bears for Ulrich! It works. You made my day. Thanks,
-Borut 2009/1/23 Ulrich Stärk <u...@spielviel.de> > 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 >>>> >>>> >>> >