SEC-1536: added JAAS API Integration, updated doc, updated jaas sample
This commit is contained in:
parent
0217e98bdb
commit
de819378fc
|
@ -17,6 +17,7 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
|
|||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.jaas.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
|
@ -52,6 +53,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
|||
checkForDuplicates(SessionManagementFilter.class, filters);
|
||||
checkForDuplicates(BasicAuthenticationFilter.class, filters);
|
||||
checkForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);
|
||||
checkForDuplicates(JaasApiIntegrationFilter.class, filters);
|
||||
checkForDuplicates(ExceptionTranslationFilter.class, filters);
|
||||
checkForDuplicates(FilterSecurityInterceptor.class, filters);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import static org.springframework.security.config.http.HttpSecurityBeanDefinitio
|
|||
import static org.springframework.security.config.http.SecurityFilters.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
|
@ -35,6 +34,7 @@ import org.springframework.security.web.access.expression.WebExpressionVoter;
|
|||
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.jaas.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
|
@ -90,6 +90,7 @@ class HttpConfigurationBuilder {
|
|||
private BeanReference sessionStrategyRef;
|
||||
private RootBeanDefinition sfpf;
|
||||
private BeanDefinition servApiFilter;
|
||||
private BeanDefinition jaasApiFilter;
|
||||
private final String portMapperName;
|
||||
private BeanReference fsi;
|
||||
private BeanReference requestCache;
|
||||
|
@ -123,6 +124,7 @@ class HttpConfigurationBuilder {
|
|||
createSessionManagementFilters();
|
||||
createRequestCacheFilter();
|
||||
createServletApiFilter();
|
||||
createJaasApiFilter();
|
||||
createChannelProcessingFilter();
|
||||
createFilterSecurityInterceptor(authenticationManager);
|
||||
}
|
||||
|
@ -337,7 +339,22 @@ class HttpConfigurationBuilder {
|
|||
servApiFilter = new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the jaas-api integration filter if required
|
||||
private void createJaasApiFilter() {
|
||||
final String ATT_JAAS_API_PROVISION = "jaas-api-provision";
|
||||
final String DEF_JAAS_API_PROVISION = "false";
|
||||
|
||||
String provideJaasApi = httpElt.getAttribute(ATT_JAAS_API_PROVISION);
|
||||
if (!StringUtils.hasText(provideJaasApi)) {
|
||||
provideJaasApi = DEF_JAAS_API_PROVISION;
|
||||
}
|
||||
|
||||
if ("true".equals(provideJaasApi)) {
|
||||
jaasApiFilter = new RootBeanDefinition(JaasApiIntegrationFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void createChannelProcessingFilter() {
|
||||
ManagedMap<BeanDefinition,BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
|
||||
|
||||
|
@ -514,6 +531,10 @@ class HttpConfigurationBuilder {
|
|||
filters.add(new OrderDecorator(servApiFilter, SERVLET_API_SUPPORT_FILTER));
|
||||
}
|
||||
|
||||
if (jaasApiFilter != null) {
|
||||
filters.add(new OrderDecorator(jaasApiFilter, JAAS_API_SUPPORT_FILTER));
|
||||
}
|
||||
|
||||
if (sfpf != null) {
|
||||
filters.add(new OrderDecorator(sfpf, SESSION_MANAGEMENT_FILTER));
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ enum SecurityFilters {
|
|||
BASIC_AUTH_FILTER,
|
||||
REQUEST_CACHE_FILTER,
|
||||
SERVLET_API_SUPPORT_FILTER,
|
||||
JAAS_API_SUPPORT_FILTER,
|
||||
REMEMBER_ME_FILTER,
|
||||
ANONYMOUS_FILTER,
|
||||
SESSION_MANAGEMENT_FILTER,
|
||||
|
|
|
@ -289,6 +289,9 @@ http.attlist &=
|
|||
http.attlist &=
|
||||
## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to "true".
|
||||
attribute servlet-api-provision {boolean}?
|
||||
http.attlist &=
|
||||
## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to "false".
|
||||
attribute jaas-api-provision {boolean}?
|
||||
http.attlist &=
|
||||
## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.
|
||||
attribute access-decision-manager-ref {xsd:token}?
|
||||
|
|
|
@ -759,6 +759,11 @@
|
|||
<xs:documentation>Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to "true".</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="jaas-api-provision" type="security:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to "false".</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="access-decision-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests.</xs:documentation>
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
|
|||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.jaas.JaasApiIntegrationFilter
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter
|
||||
|
@ -568,6 +569,14 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
|
|||
getFilter(BasicAuthenticationFilter).authenticationDetailsSource == adsr
|
||||
getFilter(X509AuthenticationFilter).authenticationDetailsSource == adsr
|
||||
}
|
||||
|
||||
def includeJaasApiIntegrationFilter() {
|
||||
xml.http(['auto-config':'true','jaas-api-provision':'true'])
|
||||
createAppContext()
|
||||
expect:
|
||||
getFilter(JaasApiIntegrationFilter.class) != null
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MockEntryPoint extends LoginUrlAuthenticationEntryPoint {
|
||||
|
|
|
@ -70,6 +70,13 @@
|
|||
<classname>SecurityContextHolderAwareRequestFilter</classname> bean to the
|
||||
stack. Defaults to "true".</para>
|
||||
</section>
|
||||
<section xml:id="nsa-jaas-api-provision">
|
||||
<title><literal>jaas-api-provision</literal></title>
|
||||
<para>If available, runs the request as the <literal>Subject</literal> acquired from
|
||||
the <classname>JaasAuthenticationToken</classname> which is implemented by
|
||||
adding a <classname>JaasApiIntegrationFilter</classname> bean to the stack.
|
||||
Defaults to "false".</para>
|
||||
</section>
|
||||
<section xml:id="nsa-path-type">
|
||||
<title><literal>request-matcher</literal></title>
|
||||
<para> Defines the <interfacename>RequestMatcher</interfacename> strategy used in
|
||||
|
|
|
@ -213,4 +213,20 @@ JAASTest {
|
|||
</bean>
|
||||
]]></programlisting></para>
|
||||
</section>
|
||||
<section xml:id="jaas-apiprovision">
|
||||
<info>
|
||||
<title xml:id="jaas-api-provision">Running as a Subject</title>
|
||||
</info>
|
||||
<para>If configured, the <classname>JaasApiIntegrationFilter</classname> will attempt to
|
||||
run as the <literal>Subject</literal> on the
|
||||
<classname>JaasAuthenticationToken</classname>. This means that the
|
||||
<literal>Subject</literal> can be accessed using:
|
||||
<programlisting language="java"><![CDATA[
|
||||
Subject subject = Subject.getSubject(AccessController.getContext());
|
||||
]]></programlisting>
|
||||
This integration can easily be configured using the
|
||||
<link xlink:href="#nsa-jaas-api-provision">jaas-api-provision</link> attribute. This
|
||||
feature is useful when integrating with legacy or external API's that rely on the
|
||||
JAAS Subject being populated.</para>
|
||||
</section>
|
||||
</chapter>
|
|
@ -691,6 +691,11 @@ List<OpenIDAttribute> attributes = token.getAttributes();</programlisting>The
|
|||
<entry><literal>SecurityContextHolderAwareFilter</literal></entry>
|
||||
<entry><literal>http/@servlet-api-provision</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>JAAS_API_SUPPORT_FILTER</entry>
|
||||
<entry><literal>JaasApiIntegrationFilter</literal></entry>
|
||||
<entry><literal>http/@jaas-api-provision</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry> REMEMBER_ME_FILTER </entry>
|
||||
<entry><classname>RememberMeAuthenticationFilter</classname></entry>
|
||||
|
|
|
@ -133,7 +133,8 @@ Success! Your web filters appear to be properly configured!
|
|||
<title>JAAS Sample</title>
|
||||
<para>The JAAS sample is very simple example of how to use a JAAS LoginModule with Spring Security. The provided LoginModule will
|
||||
successfully authenticate a user if the username equals the password otherwise a LoginException is thrown. The AuthorityGranter
|
||||
used in this example always grants the role ROLE_USER.</para>
|
||||
used in this example always grants the role ROLE_USER. The sample application also demonstrates how to run as the JAAS Subject
|
||||
returned by the LoginModule by setting <link xlink:href="#nsa-jaas-api-provision">jaas-api-provision</link> equal to "true".</para>
|
||||
</section>
|
||||
<section xml:id="preauth-sample">
|
||||
<title>Pre-Authentication Sample</title>
|
||||
|
|
|
@ -151,6 +151,13 @@
|
|||
using it to install a Spring Security aware
|
||||
<literal>HttpServletRequestWrapper</literal> into your servlet container</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The <classname>JaasApiIntegrationFilter</classname>, if a
|
||||
<classname>JaasAuthenticationToken</classname> is in the
|
||||
<classname>SecurityContextHolder</classname> this will process the
|
||||
<classname>FilterChain</classname> as the <classname>Subject</classname> in the
|
||||
<classname>JaasAuthenticationToken</classname></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><classname>RememberMeAuthenticationFilter</classname>, so that if no earlier
|
||||
authentication processing mechanism updated the
|
||||
|
|
|
@ -13,7 +13,6 @@ dependencies {
|
|||
|
||||
runtime project(':spring-security-web'),
|
||||
project(':spring-security-config'),
|
||||
project(':spring-security-taglibs'),
|
||||
"org.springframework:spring-context-support:$springVersion",
|
||||
"javax.servlet:jstl:$jstlVersion",
|
||||
"org.slf4j:jcl-over-slf4j:$slf4jVersion",
|
||||
|
|
|
@ -67,7 +67,7 @@ public class UsernameEqualsPasswordLoginModule implements LoginModule {
|
|||
}
|
||||
|
||||
public boolean login() throws LoginException {
|
||||
if (username != null && !username.equals(password)) {
|
||||
if (username == null || !username.equals(password)) {
|
||||
throw new LoginException("username is not equal to password");
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,9 @@ public class UsernameEqualsPasswordLoginModule implements LoginModule {
|
|||
public String getName() {
|
||||
return username;
|
||||
}
|
||||
public String toString() {
|
||||
return "Principal [name="+getName()+"]";
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
|
||||
|
||||
|
||||
<sec:http auto-config="true" use-expressions="true">
|
||||
<sec:http auto-config="true" use-expressions="true" jaas-api-provision="true">
|
||||
<sec:intercept-url pattern="/secure/**" access="isAuthenticated()"/>
|
||||
</sec:http>
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<%@ page import="javax.security.auth.Subject" %>
|
||||
<%@ page import="java.security.AccessController" %>
|
||||
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
|
||||
<html>
|
||||
<body>
|
||||
|
@ -9,6 +11,9 @@ Anyone can view this page.
|
|||
Your principal object is....: <%= request.getUserPrincipal() %>
|
||||
</p>
|
||||
<p>
|
||||
Subject.getSubject(AccessController.getContext()) is....: <%= Subject.getSubject(AccessController.getContext()) %>
|
||||
</p>
|
||||
<p>
|
||||
<sec:authorize url='/secure/index.jsp'>You can currently access "/secure" URLs.</sec:authorize>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<%@ page import="javax.security.auth.Subject" %>
|
||||
<%@ page import="java.security.AccessController" %>
|
||||
<%@ page import="org.springframework.security.core.context.SecurityContextHolder" %>
|
||||
<%@ page import="org.springframework.security.core.Authentication" %>
|
||||
<%@ page import="org.springframework.security.core.GrantedAuthority" %>
|
||||
|
@ -10,6 +12,15 @@
|
|||
|
||||
<h3>Security Debug Information</h3>
|
||||
|
||||
<%
|
||||
|
||||
Subject subject = Subject.getSubject(AccessController.getContext());
|
||||
if(subject != null) { %>
|
||||
<p>
|
||||
Subject.getSubject(AccessController.getContext()) is....: <%= subject %>
|
||||
</p>
|
||||
<%} %>
|
||||
|
||||
<%
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth != null) { %>
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright 2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.web.authentication.jaas;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.springframework.security.authentication.jaas.JaasAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A <code>Filter</code> which attempts to obtain a JAAS <code>Subject</code>
|
||||
* and continue the <code>FilterChain</code> running as that
|
||||
* <code>Subject</code>.
|
||||
* </p>
|
||||
* <p>
|
||||
* By using this <code>Filter</code> in conjunction with Spring's
|
||||
* <code>JaasAuthenticationProvider</code> both Spring's
|
||||
* <code>SecurityContext</code> and a JAAS <code>Subject</code> can be populated
|
||||
* simultaneously. This is useful when integrating with code that requires a
|
||||
* JAAS <code>Subject</code> to be populated.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @see #doFilter(ServletRequest, ServletResponse, FilterChain)
|
||||
* @see #obtainSubject(ServletRequest)
|
||||
*/
|
||||
public class JaasApiIntegrationFilter extends GenericFilterBean {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private boolean createEmptySubject;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Attempts to obtain and run as a JAAS <code>Subject</code> using
|
||||
* {@link #obtainSubject(ServletRequest)}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If the <code>Subject</code> is <code>null</code> and
|
||||
* <tt>createEmptySubject</tt> is <code>true</code>, an empty, writeable
|
||||
* <code>Subject</code> is used. This allows for the <code>Subject</code> to
|
||||
* be populated at the time of login. If the <code>Subject</code> is
|
||||
* <code>null</code>, the <code>FilterChain</code> continues with no
|
||||
* additional processing. If the <code>Subject</code> is not
|
||||
* <code>null</code>, the <code>FilterChain</code> is ran with
|
||||
* {@link Subject#doAs(Subject, PrivilegedExceptionAction)} in conjunction
|
||||
* with the <code>Subject</code> obtained.
|
||||
* </p>
|
||||
*/
|
||||
public final void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
Subject subject = obtainSubject(request);
|
||||
if (subject == null && createEmptySubject) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Subject returned was null and createEmtpySubject is true; creating new empty subject to run as.");
|
||||
}
|
||||
subject = new Subject();
|
||||
}
|
||||
if (subject == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Subject is null continue running with no Subject.");
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
final PrivilegedExceptionAction<Object> continueChain = new PrivilegedExceptionAction<Object>() {
|
||||
public Object run() throws IOException, ServletException {
|
||||
chain.doFilter(request, response);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Running as Subject " + subject);
|
||||
}
|
||||
try {
|
||||
Subject.doAs(subject, continueChain);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw new ServletException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the <code>Subject</code> to run as or <code>null</code> if no
|
||||
* <code>Subject</code> is available.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default implementation attempts to obtain the <code>Subject</code>
|
||||
* from the <code>SecurityContext</code>'s <code>Authentication</code>. If
|
||||
* it is of type <code>JaasAuthenticationToken</code> and is authenticated,
|
||||
* the <code>Subject</code> is returned from it. Otherwise,
|
||||
* <code>null</code> is returned.
|
||||
* </p>
|
||||
*
|
||||
* @param request
|
||||
* the current <code>ServletRequest</code>
|
||||
* @return the Subject to run as or <code>null</code> if no
|
||||
* <code>Subject</code> is available.
|
||||
*/
|
||||
protected Subject obtainSubject(ServletRequest request) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Attempting to obtainSubject using authentication : " + authentication);
|
||||
}
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
if (!authentication.isAuthenticated()) {
|
||||
return null;
|
||||
}
|
||||
if (!(authentication instanceof JaasAuthenticationToken)) {
|
||||
return null;
|
||||
}
|
||||
JaasAuthenticationToken token = (JaasAuthenticationToken) authentication;
|
||||
LoginContext loginContext = token.getLoginContext();
|
||||
if (loginContext == null) {
|
||||
return null;
|
||||
}
|
||||
return loginContext.getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets <code>createEmptySubject</code>. If the value is <code>true</code>,
|
||||
* and {@link #obtainSubject(ServletRequest)} returns <code>null</code>, an
|
||||
* empty, writeable <code>Subject</code> is created instead. Otherwise no
|
||||
* <code>Subject</code> is used. The default is <code>false</code>.
|
||||
*
|
||||
* @param createEmptySubject
|
||||
* the new value
|
||||
*/
|
||||
public final void setCreateEmptySubject(boolean createEmptySubject) {
|
||||
this.createEmptySubject = createEmptySubject;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright 2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.web.authentication.jaas;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.TextInputCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.jaas.JaasAuthenticationToken;
|
||||
import org.springframework.security.authentication.jaas.TestLoginModule;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
* Tests the JaasApiIntegrationFilter.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class JaasApiIntegrationFilterTest {
|
||||
//~ Instance fields ================================================================================================
|
||||
private JaasApiIntegrationFilter filter;
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
private Authentication token;
|
||||
private Subject authenticatedSubject;
|
||||
private Configuration testConfiguration;
|
||||
private CallbackHandler callbackHandler;
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@Before
|
||||
public void onBeforeTests() throws Exception {
|
||||
this.filter = new JaasApiIntegrationFilter();
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
|
||||
authenticatedSubject = new Subject();
|
||||
authenticatedSubject.getPrincipals().add(new Principal() {
|
||||
public String getName() {
|
||||
return "principal";
|
||||
}
|
||||
});
|
||||
authenticatedSubject.getPrivateCredentials().add("password");
|
||||
authenticatedSubject.getPublicCredentials().add("username");
|
||||
callbackHandler = new CallbackHandler() {
|
||||
@Override
|
||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
for (Callback callback : callbacks) {
|
||||
if (callback instanceof NameCallback) {
|
||||
((NameCallback) callback).setName("user");
|
||||
} else if (callback instanceof PasswordCallback) {
|
||||
((PasswordCallback) callback).setPassword("password".toCharArray());
|
||||
} else if (callback instanceof TextInputCallback) {
|
||||
// ignore
|
||||
} else {
|
||||
throw new UnsupportedCallbackException(callback, "Unrecognized Callback " + callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
testConfiguration = new Configuration() {
|
||||
public void refresh() {
|
||||
}
|
||||
|
||||
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
|
||||
return new AppConfigurationEntry[] { new AppConfigurationEntry(TestLoginModule.class.getName(),
|
||||
LoginModuleControlFlag.REQUIRED, new HashMap<String, String>()) };
|
||||
}
|
||||
};
|
||||
LoginContext ctx = new LoginContext("SubjectDoAsFilterTest", authenticatedSubject, callbackHandler,
|
||||
testConfiguration);
|
||||
ctx.login();
|
||||
token = new JaasAuthenticationToken("username", "password", AuthorityUtils.createAuthorityList("ROLE_ADMIN"),
|
||||
ctx);
|
||||
|
||||
// just in case someone forgot to clear the context
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@After
|
||||
public void onAfterTests() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a Subject was not setup in some other manner.
|
||||
*/
|
||||
@Test
|
||||
public void currentSubjectNull() {
|
||||
assertNull(Subject.getSubject(AccessController.getContext()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void obtainSubjectNullAuthentication() {
|
||||
assertNullSubject(filter.obtainSubject(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void obtainSubjectNonJaasAuthentication() {
|
||||
Authentication authentication = new TestingAuthenticationToken("un", "pwd");
|
||||
authentication.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
assertNullSubject(filter.obtainSubject(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void obtainSubjectNullLoginContext() {
|
||||
token = new JaasAuthenticationToken("un", "pwd", AuthorityUtils.createAuthorityList("ROLE_ADMIN"), null);
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
assertNullSubject(filter.obtainSubject(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void obtainSubjectNullSubject() throws Exception {
|
||||
LoginContext ctx = new LoginContext("obtainSubjectNullSubject", null, callbackHandler, testConfiguration);
|
||||
assertNull(ctx.getSubject());
|
||||
token = new JaasAuthenticationToken("un", "pwd", AuthorityUtils.createAuthorityList("ROLE_ADMIN"), ctx);
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
assertNullSubject(filter.obtainSubject(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void obtainSubject() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
assertEquals(authenticatedSubject, filter.obtainSubject(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterCurrentSubjectPopulated() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
assertJaasSubjectEquals(authenticatedSubject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterAuthenticationNotAuthenticated() throws Exception {
|
||||
// Authentication is null, so no Subject is populated.
|
||||
token.setAuthenticated(false);
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
assertJaasSubjectEquals(null);
|
||||
filter.setCreateEmptySubject(true);
|
||||
assertJaasSubjectEquals(new Subject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterAuthenticationNull() throws Exception {
|
||||
assertJaasSubjectEquals(null);
|
||||
filter.setCreateEmptySubject(true);
|
||||
assertJaasSubjectEquals(new Subject());
|
||||
}
|
||||
|
||||
//~ Helper Methods ====================================================================================================
|
||||
|
||||
private void assertJaasSubjectEquals(final Subject expectedValue) throws Exception {
|
||||
MockFilterChain chain = new MockFilterChain() {
|
||||
public void doFilter(ServletRequest request, ServletResponse response) {
|
||||
// See if the subject was updated
|
||||
Subject currentSubject = Subject.getSubject(AccessController.getContext());
|
||||
assertEquals(expectedValue, currentSubject);
|
||||
|
||||
// run so we know the chain was executed
|
||||
super.doFilter(request, response);
|
||||
}
|
||||
};
|
||||
filter.doFilter(request, response, chain);
|
||||
// ensure that the chain was actually invoked
|
||||
assertNotNull(chain.getRequest());
|
||||
}
|
||||
|
||||
private void assertNullSubject(Subject subject) {
|
||||
assertNull("Subject is expected to be null, but is not. Got " + subject, subject);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,8 @@ dependencies {
|
|||
|
||||
provided 'javax.servlet:servlet-api:2.5'
|
||||
|
||||
testCompile 'commons-codec:commons-codec:1.3',
|
||||
testCompile project(':spring-security-core').sourceSets.test.classes,
|
||||
'commons-codec:commons-codec:1.3',
|
||||
"org.springframework:spring-test:$springVersion"
|
||||
testRuntime "hsqldb:hsqldb:$hsqlVersion"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue