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.
Since Spring Security doesn't provide Authorization Server support, migrating a Spring Security OAuth Authorization Server is out of scope for this document.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With