Wednesday, January 27, 2010

Automating User Registrations with OpenID and Spring Security 3.0 - Part 3

Preparing heavily for DevNexus 2010, I did not have as much time as I hoped for in order to continue my series on using OpenID with the new SpringSecurity 3.0 (See Part 2 for details) Thus, today I would just like to cut and paste some of the code from my little 'home-POC'. In the coming weeks I still hope to incorporate it properly into jRecruiter.

Here is the JSP snippet for the OpenID login form:
<div id="openid-registration">
  <form name='oidf' action='/jrecruiter-web/j_spring_openid_security_check' method='POST'>
    <fieldset id="openIdLoginSection">
          <legend>Login with OpenID Identity</legend>
          <div class="required">
            <label for="openid_identifier">Identity</label>
            <s:textfield id="openid_identifier" name="openid_identifier" required="true" maxlength="80" tabindex="1" size="30"/>
          </div>
          <div class="submit">
            <input type="submit" value="Login"/>
          </div>
    </fieldset>
  </form>              
</div>


Here is the relevant piece of my Spring context file:

<security:http  auto-config="true" access-decision-manager-ref="accessDecisionManager" >
  <security:intercept-url pattern="/s/admin/**" access="ADMIN"                        requires-channel="https"/>
  <security:intercept-url pattern="/**"         access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="any" />

  <security:form-login login-page="/login.html" default-target-url="/admin/index.html"
                       authentication-failure-url="/login.html?status=error" />
  <security:logout logout-url="/logout.html" invalidate-session="true" logout-success-url="/show.jobs.html"/>
  <security:session-management>
     <security:concurrency-control max-sessions="1" error-if-maximum-exceeded="false"/>
  </security:session-management>
  <security:custom-filter ref="openIDFilter"                   position="OPENID_FILTER" />
</security:http>

<bean id="openIDFilter" class="org.jrecruiter.web.security.RegistrationAwareOpenIDAuthenticationFilter">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="consumer" ref="attributeAwareOpenIDConsumer"/>
  <property name="authenticationSuccessHandler"        ref="openIDFilterSuccess"/>
  <property name="authenticationFailureHandler"        ref="openIDFilterFailure"/>
  <property name="registrationTargetUrlRequestHandler" ref="openIDFilterRedirectToRegistration"/>
</bean>

<bean id="openIDFilterRedirectToRegistration" class="org.jrecruiter.web.security.RegistrationTargetUrlRequestHandler">
  <property name="defaultTargetUrl" value="/registration/signup.html"/>
</bean>
<bean id="openIDFilterSuccess" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
  <property name="defaultTargetUrl" value="/admin/index.html"/>
</bean>
 
<bean id="openIDFilterFailure" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
  <property name="defaultFailureUrl" value="/login.html?status=error"/>
</bean>

<bean id="attributeAwareOpenIDProvider" class="org.jrecruiter.web.security.AttributeAwareOpenIDProvider" scope="prototype">
  <constructor-arg ref="userService"/>
</bean>

<bean id="attributeAwareOpenIDConsumer" class="org.jrecruiter.web.security.AttributeAwareOpenIDConsumer"/>


I did some customization: public class AttributeAwareOpenIDConsumer extends OpenID4JavaConsumer {
  public AttributeAwareOpenIDConsumer() throws ConsumerException {
         super(Arrays.asList(UsedOpenIdAttribute.FIRST_NAME.getOpenIdAttribute(),
                                     UsedOpenIdAttribute.LAST_NAME.getOpenIdAttribute(),
                                     UsedOpenIdAttribute.EMAIL.getOpenIdAttribute(),
                                     UsedOpenIdAttribute.AX_FIRST_NAME.getOpenIdAttribute(),
                                     UsedOpenIdAttribute.AX_LAST_NAME.getOpenIdAttribute(),
                                     UsedOpenIdAttribute.NAME_PERSON.getOpenIdAttribute()));
  }
}
I also created a custom class AttributeAwareOpenIDProvider which extends org.springframework.security.openid.OpenIDAuthenticationProvider. It overrides public Authentication authenticate(Authentication authentication) Thus, I can hook into the actual authentication process and inject my own logic.

For example,  if the OpenID authentication succeeds it does not necessarily mean that your account exists, yet. Therefore, if OpenID authentication succeeds (if (status == OpenIDAuthenticationStatus.SUCCESS)), I try loading the user from my application's database (userDetailsService). If then a UsernameNotFoundException is thrown I collect a series of OpenID attributes from the org.springframework.security.openid.OpenIDAuthenticationToken. Once finished, I am throwing a custom AuthenticationSucessButMissingRegistrationException. I use it to redirect to the registration page and pre-populate the registration form with some of the collected OpenID attributes.

I hope this gives you some ideas of how you can integrate OpenID into your SpringSecurity infrastructure. I am myself still in the early learning phase regarding OpenID and I still need to figure out how to best manage multiple authentication realms within my application. But that may be a reason for another blog post.

7 comments:

Abdul Khaliq said...

Hi Gunnar,

I had read ur post about implementing openID with spring security. However things will be more clearer if u share some code.

Abdul Khaliq

Abdul Khaliq said...

hi,

I have used ur example of AttriuteAwareOpneIDConsumer for fetching the attributes from google site my class file looks like this

public class AttributeAwareOpenIDConsumer extends OpenID4JavaConsumer{

public AttributeAwareOpenIDConsumer() throws ConsumerException
{
super(Arrays.asList(new OpenIDAttribute("email","http://axschema.org/contact/email"),
new OpenIDAttribute("firstname","http://axschema.org/namePerson/first"),
new OpenIDAttribute("lastname","http://axschema.org/namePerson/last"),
new OpenIDAttribute("country","http://axschema.org/contact/country/home"),
new OpenIDAttribute("language","http://axschema.org/pref/language")));
}

}
but it dosent semms to ftech nay attributes from the site

any help?

Abdul Khaliq

Anonymous said...

Gunnar, would you be able to elaborate more on your RegistrationAwareOpenIDAuthenticationFilter? I'm attempting to emulate your design (though I'm new to spring/openID), so any help would be appreciated...

Abdul Khaliq said...

My application security context file








the custom AttributeAwareOpenIDConsumer file
public class AttributeAwareOpenIDConsumer extends OpenID4JavaConsumer{

public AttributeAwareOpenIDConsumer() throws ConsumerException
{
super(Arrays.asList(new OpenIDAttribute("email","http://axschema.org/contact/email"),
new OpenIDAttribute("firstname","http://axschema.org/namePerson/first"),
new OpenIDAttribute("lastname","http://axschema.org/namePerson/last"),
new OpenIDAttribute("country","http://axschema.org/contact/country/home"),
new OpenIDAttribute("language","http://axschema.org/pref/language")));
}

}

the myOpenIDFilter beeans overrides the default openID filter behaviour which is done in the tag.

marcelstoer said...

So much information on Spring OpenID is outdated so quickly...I recommend sticking with the sample application at http://repo1.maven.org/maven2/org/springframework/security/spring-security-samples-openid/3.0.3.RELEASE/spring-security-samples-openid-3.0.3.RELEASE.war as a reference. Also, I found http://www.packtpub.com/article/opening-up-to-openid-with-spring-security quite helpful.

Phoenix said...

Hi, great blog. I should have seen it earlier. We recently implement spring security 3 with openID as well. I take a less flexible, but simpler approach.
http://zggame.blogspot.com/2010/12/openid-spring-mvc-3-spring-security-3.html

Phoenix said...

Hi, great blog. I should have seen it earlier. We recently implement spring security 3 with openID as well. I take a less flexible, but simpler approach.
http://zggame.blogspot.com/2010/12/openid-spring-mvc-3-spring-security-3.html