Ups, that was beers :-) 2009/1/23 Borut Bolčina <borut.bolc...@gmail.com>
> 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 >>>>> >>>>> >>>> >> >