consolidated client credential filter beans
(note: imports magic from secoauth)pull/803/head
parent
4f12fab56b
commit
ba51df0c37
|
@ -22,10 +22,12 @@
|
|||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
|
||||
|
||||
|
@ -80,8 +82,8 @@
|
|||
<security:intercept-url pattern="/token" access="isAuthenticated()" />
|
||||
<security:http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
|
||||
<!-- include this only if you need to authenticate clients via request parameters -->
|
||||
<security:custom-filter ref="clientAssertionTokenEndpointFilter" after="PRE_AUTH_FILTER" /> <!-- this one has to go first -->
|
||||
<security:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
|
||||
<security:custom-filter ref="clientAssertionEndpointFilter" after="PRE_AUTH_FILTER" /> <!-- this one has to go first -->
|
||||
<security:custom-filter ref="clientCredentialsEndpointFilter" after="BASIC_AUTH_FILTER" />
|
||||
<security:custom-filter ref="corsFilter" after="SECURITY_CONTEXT_FILTER" />
|
||||
<security:access-denied-handler ref="oauthAccessDeniedHandler" />
|
||||
</security:http>
|
||||
|
@ -135,9 +137,9 @@
|
|||
authentication-manager-ref="clientAuthenticationManager">
|
||||
<security:http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
|
||||
<!-- <security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" /> -->
|
||||
<security:custom-filter ref="clientAssertionIntrospectionEndpointFilter" after="PRE_AUTH_FILTER" /> <!-- this one has to go first -->
|
||||
<security:custom-filter ref="clientAssertionEndpointFilter" after="PRE_AUTH_FILTER" /> <!-- this one has to go first -->
|
||||
<security:custom-filter ref="corsFilter" after="SECURITY_CONTEXT_FILTER" />
|
||||
<security:custom-filter ref="clientCredentialsIntrospectionEndpointFilter" after="BASIC_AUTH_FILTER" />
|
||||
<security:custom-filter ref="clientCredentialsEndpointFilter" after="BASIC_AUTH_FILTER" />
|
||||
</security:http>
|
||||
|
||||
<security:http pattern="/#{T(org.mitre.oauth2.web.RevocationEndpoint).URL}**"
|
||||
|
@ -147,9 +149,9 @@
|
|||
authentication-manager-ref="clientAuthenticationManager">
|
||||
<security:http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
|
||||
<!-- <security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" /> -->
|
||||
<security:custom-filter ref="clientAssertionRevocationEndpointFilter" after="PRE_AUTH_FILTER" /> <!-- this one has to go first -->
|
||||
<security:custom-filter ref="clientAssertionEndpointFilter" after="PRE_AUTH_FILTER" /> <!-- this one has to go first -->
|
||||
<security:custom-filter ref="corsFilter" after="SECURITY_CONTEXT_FILTER" />
|
||||
<security:custom-filter ref="clientCredentialsRevocationEndpointFilter" after="BASIC_AUTH_FILTER" />
|
||||
<security:custom-filter ref="clientCredentialsEndpointFilter" after="BASIC_AUTH_FILTER" />
|
||||
</security:http>
|
||||
|
||||
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
|
||||
|
@ -164,36 +166,26 @@
|
|||
|
||||
<bean id="oauth2ExceptionTranslator" class="org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator" />
|
||||
|
||||
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
|
||||
<property name="authenticationManager" ref="clientAuthenticationManager" />
|
||||
<property name="filterProcessesUrl" value="/token"/>
|
||||
</bean>
|
||||
|
||||
<bean id="clientCredentialsIntrospectionEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
|
||||
<property name="authenticationManager" ref="clientAuthenticationManager" />
|
||||
<property name="filterProcessesUrl" value="/introspect"/>
|
||||
<bean id="clientAuthMatcher" class="org.mitre.openid.connect.filter.MultiUrlRequestMatcher">
|
||||
<constructor-arg name="filterProcessesUrls">
|
||||
<set>
|
||||
<value>/introspect</value>
|
||||
<value>/revoke</value>
|
||||
<value>/token</value>
|
||||
</set>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="clientCredentialsRevocationEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
|
||||
<bean id="clientCredentialsEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
|
||||
<property name="authenticationManager" ref="clientAuthenticationManager" />
|
||||
<property name="filterProcessesUrl" value="/revoke"/>
|
||||
<property name="requiresAuthenticationRequestMatcher" ref="clientAuthMatcher" />
|
||||
</bean>
|
||||
|
||||
<bean id="clientAssertionTokenEndpointFilter" class="org.mitre.openid.connect.assertion.JWTBearerClientAssertionTokenEndpointFilter">
|
||||
<bean id="clientAssertionEndpointFilter" class="org.mitre.openid.connect.assertion.JWTBearerClientAssertionTokenEndpointFilter">
|
||||
<constructor-arg name="additionalMatcher" ref="clientAuthMatcher" />
|
||||
<property name="authenticationManager" ref="clientAssertionAuthenticationManager" />
|
||||
<property name="filterProcessesUrl" value="/token" />
|
||||
</bean>
|
||||
|
||||
<bean id="clientAssertionIntrospectionEndpointFilter" class="org.mitre.openid.connect.assertion.JWTBearerClientAssertionTokenEndpointFilter">
|
||||
<property name="authenticationManager" ref="clientAssertionAuthenticationManager" />
|
||||
<property name="filterProcessesUrl" value="/introspect" />
|
||||
</bean>
|
||||
|
||||
<bean id="clientAssertionRevocationEndpointFilter" class="org.mitre.openid.connect.assertion.JWTBearerClientAssertionTokenEndpointFilter">
|
||||
<property name="authenticationManager" ref="clientAssertionAuthenticationManager" />
|
||||
<property name="filterProcessesUrl" value="/revoke" />
|
||||
</bean>
|
||||
|
||||
<security:authentication-manager id="clientAuthenticationManager">
|
||||
<security:authentication-provider user-service-ref="clientUserDetailsService" />
|
||||
</security:authentication-manager>
|
||||
|
|
|
@ -22,14 +22,23 @@ package org.mitre.openid.connect.assertion;
|
|||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.mockito.AdditionalMatchers;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
|
||||
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
|
||||
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
|
@ -41,14 +50,34 @@ import com.nimbusds.jwt.JWTParser;
|
|||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class JWTBearerClientAssertionTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter {
|
||||
public class JWTBearerClientAssertionTokenEndpointFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
public JWTBearerClientAssertionTokenEndpointFilter() {
|
||||
super();
|
||||
private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
|
||||
|
||||
public JWTBearerClientAssertionTokenEndpointFilter(RequestMatcher additionalMatcher) {
|
||||
super(new ClientAssertionRequestMatcher(additionalMatcher));
|
||||
// If authentication fails the type is "Form"
|
||||
((OAuth2AuthenticationEntryPoint) authenticationEntryPoint).setTypeName("Form");
|
||||
}
|
||||
|
||||
public JWTBearerClientAssertionTokenEndpointFilter(String path) {
|
||||
super(path);
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
|
||||
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException exception) throws IOException, ServletException {
|
||||
if (exception instanceof BadCredentialsException) {
|
||||
exception = new BadCredentialsException(exception.getMessage(), new BadClientCredentialsException());
|
||||
}
|
||||
authenticationEntryPoint.commence(request, response, exception);
|
||||
}
|
||||
});
|
||||
setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
|
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
// no-op - just allow filter chain to continue to token endpoint
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,42 +103,38 @@ public class JWTBearerClientAssertionTokenEndpointFilter extends ClientCredentia
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the "client_assertion_type" and "client_assertion" parameters are present and contain the right values.
|
||||
*/
|
||||
@Override
|
||||
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
|
||||
// check for appropriate parameters
|
||||
String assertionType = request.getParameter("client_assertion_type");
|
||||
String assertion = request.getParameter("client_assertion");
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain chain, Authentication authResult) throws IOException, ServletException {
|
||||
super.successfulAuthentication(request, response, chain, authResult);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(assertionType) || Strings.isNullOrEmpty(assertion)) {
|
||||
return false;
|
||||
} else if (!assertionType.equals("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")) {
|
||||
return false;
|
||||
private static class ClientAssertionRequestMatcher implements RequestMatcher {
|
||||
|
||||
private RequestMatcher additionalMatcher;
|
||||
|
||||
public ClientAssertionRequestMatcher(RequestMatcher additionalMatcher) {
|
||||
this.additionalMatcher = additionalMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
// check for appropriate parameters
|
||||
String assertionType = request.getParameter("client_assertion_type");
|
||||
String assertion = request.getParameter("client_assertion");
|
||||
|
||||
|
||||
// Can't call to superclass here b/c client creds would break for lack of client_id
|
||||
// return super.requiresAuthentication(request, response);
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
int pathParamIndex = uri.indexOf(';');
|
||||
|
||||
if (pathParamIndex > 0) {
|
||||
// strip everything after the first semi-colon
|
||||
uri = uri.substring(0, pathParamIndex);
|
||||
if (Strings.isNullOrEmpty(assertionType) || Strings.isNullOrEmpty(assertion)) {
|
||||
return false;
|
||||
} else if (!assertionType.equals("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return additionalMatcher.matches(request);
|
||||
}
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
return uri.endsWith(getFilterProcessesUrl());
|
||||
}
|
||||
|
||||
return uri.endsWith(request.getContextPath() + getFilterProcessesUrl());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2015 The MITRE Corporation
|
||||
* and the MIT Kerberos and Internet Trust Consortium
|
||||
*
|
||||
* 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.mitre.openid.connect.filter;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class MultiUrlRequestMatcher implements RequestMatcher {
|
||||
private final Set<String> filterProcessesUrls;
|
||||
|
||||
public MultiUrlRequestMatcher(Set<String> filterProcessesUrls) {
|
||||
for (String filterProcessesUrl : filterProcessesUrls) {
|
||||
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
|
||||
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
|
||||
}
|
||||
this.filterProcessesUrls = ImmutableSet.copyOf(filterProcessesUrls);
|
||||
}
|
||||
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
String uri = request.getRequestURI();
|
||||
int pathParamIndex = uri.indexOf(';');
|
||||
|
||||
if (pathParamIndex > 0) {
|
||||
// strip everything after the first semi-colon
|
||||
uri = uri.substring(0, pathParamIndex);
|
||||
}
|
||||
|
||||
if ("".equals(request.getContextPath())) {
|
||||
// if any one of the URLs match, return true
|
||||
for (String filterProcessesUrl : filterProcessesUrls) {
|
||||
if (uri.endsWith(filterProcessesUrl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String filterProcessesUrl : filterProcessesUrls) {
|
||||
if (uri.endsWith(request.getContextPath() + filterProcessesUrl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue