Browse Source

SEC-1431: Modify OpenID sample to use a custom UserDetailsService which allows any user to authenticate, allocating them a standard role and "registers" their ID in a map, allowing it to be retrieved in subsequent logins.

Luke Taylor 15 years ago
parent
commit
ee1fd1bc50

+ 4 - 1
samples/openid/openid.gradle

@@ -4,10 +4,13 @@ apply plugin: 'war'
 apply plugin: 'jetty'
 
 dependencies {
+    compile project(':spring-security-core'),
+            project(':spring-security-openid')
+
     providedCompile 'javax.servlet:servlet-api:2.5@jar'
 
     runtime project(':spring-security-web'),
             project(':spring-security-config'),
-            project(':spring-security-openid'),
+            project(':spring-security-taglibs'),
             'log4j:log4j:1.2.15@jar'
 }

+ 5 - 0
samples/openid/pom.xml

@@ -25,6 +25,11 @@
             <artifactId>spring-security-web</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-taglibs</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>spring-security-openid</artifactId>

+ 47 - 0
samples/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetails.java

@@ -0,0 +1,47 @@
+package org.springframework.security.samples.openid;
+
+import java.util.Collection;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+
+/**
+ * Customized {@code UserDetails} implementation.
+ *
+ * @author Luke Taylor
+ * @since 3.1
+ */
+public class CustomUserDetails extends User {
+    private String email;
+    private String name;
+    private boolean newUser;
+
+    public CustomUserDetails(String username, Collection<GrantedAuthority> authorities) {
+        super(username, "unused", authorities);
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public boolean isNewUser() {
+        return newUser;
+    }
+
+    public void setNewUser(boolean newUser) {
+        this.newUser = newUser;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
+

+ 103 - 0
samples/openid/src/main/java/org/springframework/security/samples/openid/CustomUserDetailsService.java

@@ -0,0 +1,103 @@
+package org.springframework.security.samples.openid;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.openid.OpenIDAttribute;
+import org.springframework.security.openid.OpenIDAuthenticationToken;
+
+/**
+ * Custom UserDetailsService which accepts any OpenID user, "registering" new users in a map so they can be welcomed
+ * back to the site on subsequent logins.
+ *
+ * @author Luke Taylor
+ * @since 3.1
+ */
+public class CustomUserDetailsService implements UserDetailsService, AuthenticationUserDetailsService<OpenIDAuthenticationToken> {
+
+    private Map<String, CustomUserDetails> registeredUsers = new HashMap<String, CustomUserDetails>();
+
+    private static final List<GrantedAuthority> DEFAULT_AUTHORITIES = AuthorityUtils.createAuthorityList("ROLE_USER");
+
+    /**
+     * Implementation of {@code UserDetailsService}. We only need this to satisfy the {@code RememberMeServices}
+     * requirements.
+     */
+    public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
+        UserDetails user = registeredUsers.get(id);
+
+        if (user == null) {
+            throw new UsernameNotFoundException(id);
+        }
+
+        return user;
+    }
+
+    /**
+     * 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();
+
+        CustomUserDetails user = registeredUsers.get(id);
+
+        if (user != null) {
+            return user;
+        }
+
+        String email = null;
+        String firstName = null;
+        String lastName = null;
+        String fullName = null;
+
+        List<OpenIDAttribute> 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("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();
+        }
+
+        user = new CustomUserDetails(id, DEFAULT_AUTHORITIES);
+        user.setEmail(email);
+        user.setName(fullName);
+
+        registeredUsers.put(id, user);
+
+        user = new CustomUserDetails(id, DEFAULT_AUTHORITIES);
+        user.setEmail(email);
+        user.setName(fullName);
+        user.setNewUser(true);
+
+        return user;
+    }
+}

+ 0 - 6
samples/openid/src/main/java/zzz/Dummy.java

@@ -1,6 +0,0 @@
-package zzz;
-/**
- * @author Luke Taylor
- */
-public class Dummy {
-}

+ 9 - 4
samples/openid/src/main/webapp/WEB-INF/applicationContext-security.xml

@@ -14,10 +14,13 @@
         <intercept-url pattern="/**" access="ROLE_USER"/>
         <intercept-url pattern="/openidlogin.jsp*" filters="none"/>
         <logout/>
-        <openid-login login-page="/openidlogin.jsp" authentication-failure-url="/openidlogin.jsp?login_error=true">
+        <openid-login login-page="/openidlogin.jsp" user-service-ref="registeringUserService"
+                authentication-failure-url="/openidlogin.jsp?login_error=true">
             <attribute-exchange>
-                <openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" count="2"/>
-                <openid-attribute name="name" type="http://schema.openid.net/namePerson/friendly" />
+                <openid-attribute name="email" type="http://axschema.org/contact/email" required="true" count="1"/>
+                <openid-attribute name="firstname" type="http://axschema.org/namePerson/first" />
+                <openid-attribute name="lastname" type="http://axschema.org/namePerson/last" />
+                <openid-attribute name="fullname" type="http://axschema.org/namePerson" />
             </attribute-exchange>
         </openid-login>
         <remember-me token-repository-ref="tokenRepo"/>
@@ -28,11 +31,13 @@
 
     <authentication-manager alias="authenticationManager"/>
 
+    <b:bean id="registeringUserService" class="org.springframework.security.samples.openid.CustomUserDetailsService" />
+<!--
     <user-service id="userService">
         <user name="http://luke.taylor.myopenid.com/" authorities="ROLE_SUPERVISOR,ROLE_USER" />
         <user name="http://luke.taylor.openid.cn/" authorities="ROLE_SUPERVISOR,ROLE_USER" />
         <user name="http://raykrueger.blogspot.com/" authorities="ROLE_SUPERVISOR,ROLE_USER" />
         <user name="http://spring.security.test.myopenid.com/" authorities="ROLE_SUPERVISOR,ROLE_USER" />
     </user-service>
-
+ -->
 </b:beans>

+ 21 - 6
samples/openid/src/main/webapp/index.jsp

@@ -1,11 +1,26 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt"%>
+<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
+
 <html>
 <body>
-<h1>Home Page</h1>
-<p>Anyone can view this page.</p>
 
-<p>Your principal object is....: <%= request.getUserPrincipal() %></p>
+<h1>OpenID Sample Home Page</h1>
+
+<sec:authentication property='principal.newUser' var='isNew' />
+<p>
+Welcome<c:if test="${!isNew}"> back,</c:if> <sec:authentication property='principal.name' />!
+</p>
+<c:if test="${isNew}">
+<p>
+As a first time user of this site, your OpenID identity has been registered
+by the application and will be recognized if you return.
+</p>
+</c:if>
 
-<p><a href="secure/index.jsp">Secure page</a></p>
-<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
+<h3>Technical Information</h3>
+<p>
+Your principal object is....: <%= request.getUserPrincipal() %>
+</p>
+<p><a href="j_spring_security_logout">Logout</a>
 </body>
-</html>
+</html>

+ 0 - 9
samples/openid/src/main/webapp/secure/extreme/index.jsp

@@ -1,9 +0,0 @@
-<html>
-<body>
-<h1>VERY Secure Page</h1>
-This is a protected page. You can only see me if you are a supervisor.
-
-<p><a href="../../">Home</a>
-<p><a href="../../j_spring_security_logout">Logout</a>
-</body>
-</html>

+ 0 - 15
samples/openid/src/main/webapp/secure/index.jsp

@@ -1,15 +0,0 @@
-<html>
-<body>
-<h1>Secure Page</h1>
-This is a protected page. You can get to me if you've been remembered,
-or if you've authenticated this session.<br><br>
-
-<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %>
-	You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br><br>
-<% } %>
-
-
-<p><a href="../">Home</a>
-<p><a href="../j_spring_security_logout">Logout</a>
-</body>
-</html>

+ 2 - 2
samples/tutorial/src/main/webapp/secure/index.jsp

@@ -30,7 +30,7 @@ or if you've authenticated this session.
 </table>
 
 
-<p><a href="../">Home</a>
-<p><a href="../j_spring_security_logout">Logout</a>
+<p><a href="../">Home</a></p>
+<p><a href="../j_spring_security_logout">Logout</a></p>
 </body>
 </html>

+ 7 - 0
web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java

@@ -204,6 +204,13 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
         this.authenticationManager = authenticationManager;
     }
 
+    /**
+     * If set to {@code true}, any {@code AuthenticationException} raised by the {@code AuthenticationManager} will be
+     * swallowed, and the request will be allowed to proceed, potentially using alternative authentication mechanisms.
+     * If {@code false} (the default), authentication failure will result in an immediate exception.
+     *
+     * @param shouldContinue set to {@code true} to allow the request to proceed after a failed authentication.
+     */
     public void setContinueFilterChainOnUnsuccessfulAuthentication(boolean shouldContinue) {
         continueFilterChainOnUnsuccessfulAuthentication = shouldContinue;
     }