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.

This commit is contained in:
Luke Taylor 2010-04-15 23:21:09 +01:00
parent f5468087c2
commit ee1fd1bc50
11 changed files with 198 additions and 43 deletions

View File

@ -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'
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

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

View File

@ -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>

View File

@ -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>
<p><a href="secure/index.jsp">Secure page</a></p>
<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
<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>
<h3>Technical Information</h3>
<p>
Your principal object is....: <%= request.getUserPrincipal() %>
</p>
<p><a href="j_spring_security_logout">Logout</a>
</body>
</html>
</html>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}