Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security with Openid and Database Integration

Tags:

I am very new to Spring and Spring Security, and hoping someone can help me to solve the following problem.

What I want to achieve is to extract user's username and email address after this user is successfully authenticated by OpenID provider(gmail), then check with the database in order to load the usermodel for this user.

In my spring-security.xml, i have

 <?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"     xsi:schemaLocation="http://www.springframework.org/schema/beans                            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd                            http://www.springframework.org/schema/context                            http://www.springframework.org/schema/context/spring-context-3.0.xsd                            http://www.springframework.org/schema/security                             http://www.springframework.org/schema/security/spring-security-3.0.xsd">      <security:authentication-manager alias="openIDAuthenticationManager" />      <bean id="authenticationSuccessHandler" class="org.school.openid.service.YouEatAuthenticationSuccessHandler">         <property name="defaultTargetUrl" value="/krams/main/common" />         <property name="attributes2UserDetails" ref="openIDAttributes2UserDetails" />     </bean>      <security:http >         <security:anonymous enabled="false" />         <security:logout />         <security:openid-login user-service-ref="userDetailsServiceOpenIDImpl" authentication-success-handler-ref="authenticationSuccessHandler"             login-page="/krams/auth/login" authentication-failure-url="/krams/auth/login?error=true">             <security:attribute-exchange>                 <security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />                 <security:openid-attribute name="firstName" type="http://axschema.org/namePerson/first" required="true" />                 <security:openid-attribute name="lastName" type="http://axschema.org/namePerson/last" required="true" />             </security:attribute-exchange>         </security:openid-login>     </security:http>      <bean id="openIDAttributes2UserDetails" class="org.school.openid.service.OpenIDAttributes2UserDetailsImpl" />      <bean id="userDetailsServiceOpenIDImpl" class="org.school.openid.service.UserDetailsServiceOpenIDImpl" />  </beans> 

my problem is at UserDetailsServiceOpenIDImpl.java is

 public class UserDetailsServiceOpenIDImpl implements UserDetailsService {      public UserDetails loadUserByUsername(String username)             throws UsernameNotFoundException, DataAccessException {         System.out.println(username);         //extract username and email address, HOW?     } } 

The print statement prints out something like

 https://www.google.com/accounts/o8/id?id=AItOawlq2C3EdFAuqp-ski_xkgB8jsEKbe-mZE 

My questions are

(1) How could I extract the username and the email address from the returned url (also, I am not even sure if the username and email address returned correctly)?

(2) By running the debug on Eclipse, the YouEatAuthenticationSuccessHandler seems not called when the url (https://www.google.com/accounts/o8/id?id=AItOawlq2C3EdFAuqp-ski_xkgB8jsEKbe-mZE) returned.

Thanks.

Edit: thanks for the link http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html#ns-openid.

It says that "The attribute values are returned as part of the authentication process and can be accessed afterwards using the following code:..."

I have added

 OpenIDAuthenticationToken token = (OpenIDAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(); List attributes = token.getAttributes(); 

into loadUserByUsername method. But the "token" object is null.

Edit 2 By following page https://fisheye.springsource.org/browse/spring-security/samples/openid/src/main/webapp/WEB-INF/applicationContext-security.xml?hb=true, i am able to extra the name and email address for the user. My spring-security.xml

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"     xsi:schemaLocation="http://www.springframework.org/schema/beans                            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd                            http://www.springframework.org/schema/context                            http://www.springframework.org/schema/context/spring-context-3.0.xsd                            http://www.springframework.org/schema/security                             http://www.springframework.org/schema/security/spring-security-3.1.xsd">     <security:authentication-manager alias="openIDAuthenticationManager" />     <security:http pattern="/krams/auth/login" security="none"/>     <security:http auto-config="true" access-denied-page="/krams/auth/denied">         <security:intercept-url pattern="/krams/main/*" access="ROLE_USER" />         <security:anonymous enabled="false" />         <security:logout              invalidate-session="true"             logout-success-url="/krams/auth/login"             logout-url="/krams/auth/logout"/>         <security:openid-login              user-service-ref="registeringUserService"             login-page="/krams/auth/login"              authentication-failure-url="/krams/auth/login?error=true"              default-target-url="/krams/main/common">             <security:attribute-exchange identifier-match="https://www.google.com/.*">                 <security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />                 <security:openid-attribute name="firstName" type="http://axschema.org/namePerson/first" required="true" />                 <security:openid-attribute name="lastName" type="http://axschema.org/namePerson/last" required="true" />             </security:attribute-exchange>             <security:attribute-exchange identifier-match=".*yahoo.com.*">                 <security:openid-attribute name="email" type="http://axschema.org/contact/email" required="true"/>                 <security:openid-attribute name="fullname" type="http://axschema.org/namePerson" required="true" />             </security:attribute-exchange>         </security:openid-login>         <!-- if remember is needed          <security:remember-me token-repository-ref="tokenRepo"/>          -->            </security:http>     <bean id="tokenRepo" class="org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl" /> <!--     A custom UserDetailsService which will allow any user to authenticate and "register" their IDs in an internal map     for use if they return to the site. This is the most common usage pattern for sites which use OpenID.  -->    <bean id="registeringUserService" class="org.school.openid.service.CustomUserDetailsService" /> </beans> 

My CustomUserDetailsService.java

 public class CustomUserDetailsService implements AuthenticationUserDetailsService {      /*     private final Map registeredUsers = new HashMap();     */        private static final List DEFAULT_AUTHORITIES = AuthorityUtils.createAuthorityList("ROLE_USER");     protected static Logger logger = Logger.getLogger("service");         /**      * Implementation of {@code AuthenticationUserDetailsService} which allows full access to the submitted      * {@code Authentication} object. Used by the OpenIDAuthenticationProvider.      */     public UserDetails loadUserDetails(OpenIDAuthenticationToken token) {         String id = token.getIdentityUrl();         String email = null;         String firstName = null;         String lastName = null;         String fullName = null;         List attributes = token.getAttributes();         for (OpenIDAttribute attribute : attributes) {             if (attribute.getName().equals("email")) {                 email = attribute.getValues().get(0);             }             if (attribute.getName().equals("firstName")) {                 firstName = attribute.getValues().get(0);             }             if (attribute.getName().equals("lastName")) {                 lastName = attribute.getValues().get(0);             }             if (attribute.getName().equals("fullname")) {                 fullName = attribute.getValues().get(0);             }         }         if (fullName == null) {             StringBuilder fullNameBldr = new StringBuilder();             if (firstName != null) {                 fullNameBldr.append(firstName);             }             if (lastName != null) {                 fullNameBldr.append(" ").append(lastName);             }             fullName = fullNameBldr.toString();         }         CustomUserDetails user = new CustomUserDetails(id,fullName,email, DEFAULT_AUTHORITIES);                 logger.debug("Set username " + fullName + " email " + email);         return user;     } } 

My CustomUserDetails.java

 public class CustomUserDetails extends User {     private static final long serialVersionUID = 1L;     private String email;     private String name;     public CustomUserDetails(String id,String name, String email,Collection authorities) {         super(name, "unused", true,true,true,true,authorities);         this.email = email;         this.name = name;     }     public String getEmail() {         return email;     }      public void setEmail(String email) {         this.email = email;     }     public void setName(String name) {         this.name = name;     }      public String getName() {         return name;     } } 

And

 ... <repository>     <id>org.springframework.maven.milestone</id>     <name>Spring Maven Milestone Repository</name>     <url>http://maven.springframework.org/milestone</url> </repository> ... <dependency>     <groupId>org.springframework.security</groupId>     <artifactId>spring-security-core</artifactId>     <version>3.1.0.RC1</version> </dependency> <dependency>     <groupId>org.springframework.security</groupId>     <artifactId>spring-security-web</artifactId>     <version>3.1.0.RC1</version>     <type>jar</type>         <scope>compile</scope> </dependency> <dependency>       <groupId>org.springframework.security</groupId>       <artifactId>spring-security-config</artifactId>       <version>3.1.0.RC1</version>     </dependency>         <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-openid</artifactId>         <version>3.1.0.RC1</version>     </dependency>     <dependency>     <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.0.5.RELEASE</version> </dependency>     <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-openid</artifactId>         <version>3.1.0.RC1</version>         <type>pom</type>         <scope>compile</scope>     </dependency> 

Hope can save you some time.

like image 816
user200340 Avatar asked Sep 05 '11 13:09

user200340


People also ask

Why Spring Security OAuth is deprecated?

Since Spring Security doesn't provide Authorization Server support, migrating a Spring Security OAuth Authorization Server is out of scope for this document.

Is Spring Security deprecated?

End of Life Notice. The Spring Security OAuth project has reached end of life and is no longer actively maintained by VMware, Inc. This project has been replaced by the OAuth2 support provided by Spring Security and Spring Authorization Server.

Is OpenID 2.0 deprecated?

Deprecated. The OpenID 1.0 and 2.0 protocols have been deprecated and users are encouraged to migrate to OpenID Connect, which is supported by spring-security-oauth2 . Filter which processes OpenID authentication requests.


1 Answers

As I see that question text itself contains the answer. I am just pulling it out and posting it as the answer for the sake of clarity to other developers with the same problem. It took me a while to figure out that question has the answer!

To access user email & name, you need to add below configuration in the security xml file.

<security:attribute-exchange identifier-match="https://www.google.com/.*">     <security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />     <security:openid-attribute name="firstName" type="http://axschema.org/namePerson/first" required="true" />     <security:openid-attribute name="lastName" type="http://axschema.org/namePerson/last" required="true" /> </security:attribute-exchange> <security:attribute-exchange identifier-match=".*yahoo.com.*">     <security:openid-attribute name="email" type="http://axschema.org/contact/email" required="true"/>     <security:openid-attribute name="fullname" type="http://axschema.org/namePerson" required="true" /> </security:attribute-exchange> 

Thereafter, it will be accessible in the AuthenticationUserDetailsService class, as shown below.

public UserDetails loadUserDetails(OpenIDAuthenticationToken token) {     String id = token.getIdentityUrl();         :         :     List attributes = token.getAttributes();     for (OpenIDAttribute attribute : attributes) {         if (attribute.getName().equals("email")) {             email = attribute.getValues().get(0);         }         if (attribute.getName().equals("firstName")) {             firstName = attribute.getValues().get(0);         }         if (attribute.getName().equals("lastName")) {             lastName = attribute.getValues().get(0);         }         if (attribute.getName().equals("fullname")) {             fullName = attribute.getValues().get(0);         }     }         :         :     // form and return user object } 

Considering that we use more of Java-based configuration/beans now, translating these XML configuration to Java-based configuration shouldn't be a problem.

Hope it helps.

like image 66
Shishir Kumar Avatar answered Sep 22 '22 13:09

Shishir Kumar