Authentication of SOAP messages - spring-security

I have searched for a good example and cannopt find one. I want to take the username and password from the SOAP header, and set the spring security context after I authenticate using our exisiting service methods. I have implemented the Wss4jSecurityInterceptor and it validates the header element. WHat I need to do in the callback, or some other mechanism, is create an uthetication context so I can access it later in our endpoint.
However, I dont think that the callback is the correct place to do it, as I keep getting password supplied no password errors. I am new to spring security and integration.
Config:
<bean id="SOAPSecurityInterceptor" class="com.ps.snt.ws.interceptor.SOAPSecurityInterceptor">
<property name="validationActions" value="UsernameToken"/>
<property name="validationCallbackHandler" ref="callbackHandler"/>
</bean>
<bean id="callbackHandler" class="com.ps.snt.ws.interceptor.SOAPSecurityValidationCallbackHandler">
</bean>
callback:
public class SOAPSecurityValidationCallbackHandler extends SimplePasswordValidationCallbackHandler {
#Override
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
System.out.println("In security callback " + callback.getPassword());
boolean valid = true;
String token = callback.getIdentifier();
String password = callback.getPassword();
Integer zoneID = null;
String username = null;
StringBuffer errorMessages = new StringBuffer();
if(StringUtils.isEmpty(token)) {
errorMessages.append("Username token cannot be empty");
valid = false;
} else {
Pattern pattern = Pattern.compile("^[\\w]+\\d\\d\\d\\d\\d");
Matcher matcher = pattern.matcher(token);
if(!matcher.matches()) {
valid = false;
errorMessages.append("Username token must be in the format 'user#zone'.");
}
else {
String[] parts = token.split("#");
username = parts[0];
zoneID = Integer.parseInt(parts[1]);
}
}
if(StringUtils.isEmpty(password)) {
errorMessages.append("Password cannot be empty.");
valid = false;
}
if(valid && username != null && zoneID != null) {
LoginService loginService = new LoginService();
LoginContextDO loginContextDO = loginService.getAuthenticatedLoginContext(username, password, zoneID);
AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
authentication.setDetails(loginContextDO);
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
System.out.println("Authetnication failed!");
}
}
}
My requirements are simple:
- validate the SOAP header (works)
- retrieve the username and password
- call our legacy service to create our login context
- set the spring security context (with logincontext as details) so I can use later in an endpoint
What mechanism can I use to validate the soap header and set a security context from that header?

SpringSecurityPasswordValidationCallbackHandler is for you. From Spring WS docs:
The SpringSecurityPasswordValidationCallbackHandler validates plain text and digest passwords using a Spring Security UserDetailService to operate. It uses this service to retrieve the (digest of ) the password of the user specified in the token. The (digest of) the password contained in this details object is then compared with the digest in the message. If they are equal, the user has successfully authenticated, and a UsernamePasswordAuthenticationToken is stored in theSecurityContextHolder. You can set the service using the userDetailsService. Additionally, you can set a userCache property, to cache loaded user details.
<beans>
<bean class="org.springframework.ws.soap.security.wss4j.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>

Related

LDAP AD Spring Security authentication fails when domain does not match userPrincipalName ending

I am using ActiveDirectoryLdapAuthenticationProvider to authenticate against an AD server. It works fine for most environments, but there is an issue when the Domain Name is not the same as the ending of the userPrincipalName of individual users.
I have setup the following properties:
xxx.api.auth.ad.domain:foo.bar.com
xxx.api.auth.ad.url:ldaps://yyy.foo.bar.com:636
And the users have userPrincipalName set as john.doe#bar.com. Notice the difference with the Domain Name
Then in my LoginService I have this:
private AuthResultDto loginAD(LoginDto login) {
String adDomain = env.getProperty("xxx.api.auth.ad.domain");
String adUrl = env.getProperty("xxx.api.auth.ad.url");
ActiveDirectoryLdapAuthenticationProvider provider =
new ActiveDirectoryLdapAuthenticationProvider(adDomain, adUrl);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(new InetOrgPersonContextMapper());
Authentication auth = provider
.authenticate(new UsernamePasswordAuthenticationToken(login.getEmail(), login.getPassword()));
if (!auth.isAuthenticated())
throw new CustomMessageException("Invalid Login");
...
}
In this case, the authentication fails with the message Active Directory authentication failed: Supplied password was invalid.
spring-ldap-core:2.3.2.RELEASE
spring-security-ldap:5.0.3.RELEASE
The problem seems to be that createBindPrincipal() inside ActiveDirectoryLdapAuthenticationProvider checks if the username ends with the domain and if not, it appends it. This results in the username becoming john.doe#bar.com#foo.bar.com
Unfortunately, ActiveDirectoryLdapAuthenticationProvider is final, so cannot override it. The solution we went with is to not pass down the domain, but the rootDN instead (constructed by copying code from ActiveDirectoryLdapAuthenticationProvider)
private AuthResultDto loginAD(LoginDto login) {
String adDomain = env.getProperty("xxx.api.auth.ad.domain");
String adUrl = env.getProperty("xxx.api.auth.ad.url");
ActiveDirectoryLdapAuthenticationProvider provider =
new ActiveDirectoryLdapAuthenticationProvider(null, adUrl, rootDnFromDomain(adDomain));
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(new InetOrgPersonContextMapper());
Authentication auth = provider
.authenticate(new UsernamePasswordAuthenticationToken(login.getEmail(), login.getPassword()));
if (!auth.isAuthenticated())
throw new CustomMessageException("Invalid Login");
...
}
// copied from ActiveDirectoryLdapAuthenticationProvider
private String rootDnFromDomain(String domain) {
String[] tokens = StringUtils.tokenizeToStringArray(domain, ".");
StringBuilder root = new StringBuilder();
for (String token : tokens) {
if (root.length() > 0) {
root.append(',');
}
root.append("dc=").append(token);
}
return root.toString();
}

Wildfly 8 custom login module accessing HttpServletRequest params

I am investigating the feasibility of using a wildfly custom login module.
The client will pass the mobile device id to the server as part of the login. I will check that the username and password are correct in the usual way then I need to check that the mobile device is approved to use the service.
The idea is that I will have a restful webservice method login that calls HttpServletRequest.login(u, p).
How do I get hold of the mobile device id inside the login module in the HttpServletRequest?
I could login and if that succeeds then test the device id in the webservice and if that is not approved, log the user out. But that seems rather messy.
What is the correct way of doing this?
EDIT
FEED BACK: I did it the way chris suggested. I implemented my own version of the CallBackHandler and an implementation of the Callback interface, inside the login method of my login module I do the following:
public boolean login() throws LoginException {
boolean login = super.login();
if (login) {
UuidCallback uuidCallback = new UuidCallback();
try {
super.callbackHandler.handle(new Callback[]{uuidCallback});
} catch (Exception e) {
LoginException le = new LoginException("Failed to get uuid");
le.initCause(e);
throw le;
}
System.out.print("Device UUID: "+uuidCallback.getUuid());
}
return login;
}
Inside the web service login method :
#Path("/login")
#Produces({ "application/json" })
public class LoginWebService {
#POST
public Response login(#Context HttpServletRequest request) throws LoginException {
CallbackHandler callbackHandler = new MyCallbackHandler(request.getParameter("username"), request.getParameter("password"), request.getParameter("uuid"));
Subject subject = new Subject();
LoginContext loginContext = new LoginContext("securityDomain", new subject, callbackHandler);
loginContext.login();
MyPrincipal principal = subject.getPrincipals(MyPrincipal.class).iterator().next();
}
}
You could also just set the uuid on the callback handler and then call getUUID() on the callback handler inside the LoginModule.login method. But I opted to go with the design even though it does not quite make sense to me.
I was still getting 403 when logged in and trying to access protected resources it turns out that if auth-constraint/role-name is *, you must supply at least one security-role.
<login-config>
<auth-method>FORM</auth-method>
<realm-name>mydomain</realm-name>
</login-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>rest</web-resource-name>
<url-pattern>/rest/app/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<!-- after login there is a 403 on protected resources if no role and role-name * -->
<security-role>
<role-name>user</role-name>
</security-role>
All my users have a role user, which gives them access. I was able to get it working by excluding the security-role but then auth-constraint/role-name must be set to a literal role, in my case: "user"
I would suggesting creating a LoginContext and pass thru an implementation of a CallbackHandler. In the callbackhandler cater for the extra UUID property.
While this code works for me, you will need to update it to cater for the extra UUID property
public class NamePasswordCallbackHandler implements CallbackHandler {
private final String username;
private final String password;
private NamePasswordCallbackHandler(String username, String password) {
this.username = username;
this.password = password;
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback current : callbacks) {
if (current instanceof NameCallback) {
((NameCallback) current).setName(username);
} else if (current instanceof PasswordCallback) {
((PasswordCallback) current).setPassword(password.toCharArray());
} else {
throw new UnsupportedCallbackException(current);
}
}
}
}
I created my own implementation of the Configuration object (shown below as JBossJaasConfiguration).
Then pass thru this callbackhandler to your LoginContext:
CallbackHandler cbh = new NamePasswordCallbackHandler(username, password);
Configuration config = new JBossJaasConfiguration("mysqldomain");
LoginContext loginContext = new LoginContext("mycontext", new Subject(), cbh, config);
loginContext.login();
The property "mysqldomain" relates to the security-domain name in your standalone.xml
<subsystem xmlns="urn:jboss:domain:security:1.2">
<security-domains>
<security-domain name="mysqldomain" cache-type="default">
<authentication>
<login-module code="com.soccerx.security.DatabaseServerLoginRealm" flag="required">
<module-option name="dsJndiName" value="java:jboss/datasources/SoccerSoftwareDS"/>
<module-option name="principalsQuery" value="select userId, tenantId, password, salt from User where username=? and StatusId != 2"/>
<module-option name="rolesQuery" value="select Role, 'Roles' from User where Username=?"/>
<module-option name="password-stacking" value="useFirstPass"/>
<module-option name="principalClass" value="com.soccerx.security.DatabasePrincipal"/>
</login-module>
</authentication>
</security-domain>
<security-domains>
</subsystem>
While this is not a complete solution for your needs, I expect you can modify it to ensure that login fails should the UUID not match.
Note: You will need to cater for this in your CustomDatabaseLoginRealm, as defined in your standalone.xml. Meaning access the username/password/uuid and compare it to the values in the database.
More more documentation see here
Instead of calling HttpServletRequest.login() method directly from your restful webservice, you could configure a security-domain in Wildfly.
This security-domain should be referenced in your webapp web.xml file login-config element, with a security-constraint, to enable JAAS
Here is an example of web.xml file which declares a security-constraint and a login-config (with BASIC authentication method)
And a Wildfly standalone.xml configuration sample for security-domain configuration, using a standard provided Database login-module, not a custom one:
<security-domain name="securityDomain" cache-type="default">
<authentication>
<login-module code="Database" flag="required">
<module-option name="dsJndiName" value="java:/TestDS"/>
<module-option name="principalsQuery" value="select password from User where login = ? and (disabled is null or disabled = 0) and activated = 1"/>
<module-option name="rolesQuery" value="select name,'Roles' from Role r, User_Role ur, User u where u.login=? and u.id = ur.userId and r.id = ur.roleId"/>
<module-option name="hashAlgorithm" value="SHA-256"/>
<module-option name="hashEncoding" value="base64"/>
<module-option name="unauthenticatedIdentity" value="guest"/>
</login-module>
</authentication>
</security-domain>
Then, in your REST resource, you just have to use the #RolesAllowed or #PermitAll annotations to restrict access or not doing also authorization.

Custom SAMLUserDetailsService not populating custom UserDetails

I have a Spring project and I'm converting my current authentication to use SAML2.
I have everything working as far as authentication, but I'm having difficulty in getting the SAML2 extension to insert my custom UserDetails object into the Spring Security Context authentication object.
I have a custom UserDetailsService, defined below:
public class SAMLAuthManager implements SAMLUserDetailsService {
private static final Logger logger = Logger.getLogger(JDBCAuthManager.class);
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
logger.info("Credential attributes: " + credential.getAttributes());
for (int x = 0; x < credential.getAttributes().size(); x++) {
Attribute attr = credential.getAttributes().get(x);
List<XMLObject> attrValues = attr.getAttributeValues();
StringBuilder strBuilder = new StringBuilder();
for (int g = 0; g < attrValues.size(); g++) {
XMLObject currObj = attrValues.get(g);
strBuilder.append(currObj.toString()).append(",");
}
strBuilder.deleteCharAt(strBuilder.length() - 1);
logger.info(attr.getFriendlyName() + ", " + strBuilder.toString());
}
String username = credential.getNameID().getValue();
userWrapper.setStaff(s);
logger.info("Returning wrapper: " + userWrapper);
return userWrapper;
} else {
return null;
}
}
}
I have also configured this userDetails in my security context config:
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<property name="userDetails" ref="samlUserDetails" />
</bean>
However, when I inspect the SecurityContextHolder, post authentication, this line:
SecurityContextHolder.getContext().getAuthentication().getCredentials();
returns an object of type org.springframework.security.saml.SAMLCredential.
I checked to see if Spring populated the Principal with the custom object (SecurityContextHolder.getContext().getAuthentication().getPrincipal()) but it did not, that's just a String with the username populated.
Any ideas?
Thanks
The principal is by default forced to be String (in order to always permit replication of Principal which was earlier an un-serializable NameID).
This can be changed by setting forcePrincipalAsString in SAMLAuthenticationProvider to false, which will make Spring SAML include your object provided by SAMLUserDetailsService as principal in the Authentication object.
The result of call to SAMLUserDetailsService is always available under SecurityContextHolder.getContext().getAuthentication().getDetails().

How to set expire_in in OAUTH 2.0?

I am using OAuth 2.0 with spring for token generation and I want to set expire_in manually so token can expire as per my criteria. Any one help me?
This is my response:
{
access_token: "c7a6cb95-1506-40e7-87d1-ddef0a239f64"
token_type: "bearer"
expires_in: 43199
scope: "read"
}
It can be set with a ClientBuilder obtained from a ClientDetailsServiceConfigurer.
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret("secret")
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.scopes("app")
.accessTokenValiditySeconds(30);
}
// ... additional configuration
}
or directly on DefaultTokenServices depending on your need.
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// optionally here you could just get endpoints.getConsumerTokenService()
// and cast to DefaultTokenServices and just set values needed
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
tokenServices.setAccessTokenValiditySeconds(60);
endpoints.tokenServices(tokenServices);
}
}
configure your oauth configuration changing your Bean TokenServices and setting accessTokenValiditySeconds property :
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="accessTokenValiditySeconds" value="1" />
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
You can also configure the DefaultTokenServices in the application.yaml file.
security:
oauth2:
client:
clientId: client-id
clientSecret: client-secret
authorized-grant-types: authorization_code,refresh_token,password
scope: openid
access-token-validity-seconds: 30
Create a custom class of AuthorizationCodeAccessTokenProvider and override the parent
public method obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
In the overridden method of your custom class, call upon the program logic of its parent class:
DefaultOAuth2AccessToken token = super.obtainAccessToken(details, request);
This will return an AccessToken.
Now, you just have to manipulate the expired value of that token directly, by providing a timestamp from the past
token.setExpiresIn(int timestamp)
Also was searching for this answer and tried proposed solution from DeezCashews. But it didn't work for me, because there is a part of code which firstly check if this value is set in in column access_token_validity table oauth_client_details and only then greps value from tokenServices. So if your "expires_in" is set in oauth_client_details table, then you need to change it there.
Code which checks validity property in db :
protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) {
if (clientDetailsService != null) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
Integer validity = client.getAccessTokenValiditySeconds();
if (validity != null) {
return validity;
}
}
return accessTokenValiditySeconds;
}
If you are using grails security oauth2 provider
you can only change grails-app/conf/spring/resources.groovy
import org.springframework.security.oauth2.provider.token.DefaultTokenServices
// Place your Spring DSL code here
beans = {
tokenServices(DefaultTokenServices){
accessTokenValiditySeconds = 600;
tokenStore = ref('tokenStore')
supportRefreshToken = true;
clientDetailsService = ref('clientDetailsService')
}
}
As such I don't think there is any policy to do that so. But there is one way which can lead to success.
Just use refresh_token API to make the current access_token invalid. :D
Simple is that.
public interface OAuth2AccessToken {
public static String BEARER_TYPE = "Bearer";
public static String OAUTH2_TYPE = "OAuth2";
/**
* The access token issued by the authorization server. This value is REQUIRED.
*/
public static String ACCESS_TOKEN = "access_token";
/**
* The type of the token issued as described in <a
* href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-7.1">Section 7.1</a>. Value is case insensitive.
* This value is REQUIRED.
*/
public static String TOKEN_TYPE = "token_type";
/**
* The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will
* expire in one hour from the time the response was generated. This value is OPTIONAL.
*/
public static String EXPIRES_IN = "expires_in";
/**
* The refresh token which can be used to obtain new access tokens using the same authorization grant as described
* in Section 6. This value is OPTIONAL.
*/
public static String REFRESH_TOKEN = "refresh_token";
/**
* The scope of the access token as described by <a
* href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.3">Section 3.3</a>
*/
public static String SCOPE = "scope";
/**
* The additionalInformation map is used by the token serializers to export any fields used by extensions of OAuth.
* #return a map from the field name in the serialized token to the value to be exported. The default serializers
* make use of Jackson's automatic JSON mapping for Java objects (for the Token Endpoint flows) or implicitly call
* .toString() on the "value" object (for the implicit flow) as part of the serialization process.
*/
Map<String, Object> getAdditionalInformation();
Set<String> getScope();
OAuth2RefreshToken getRefreshToken();
String getTokenType();
boolean isExpired();
Date getExpiration();
int getExpiresIn();
String getValue();
}

Spring Security 2.0.x - Filter based on role type + Session based bean state

We have an application where users with proxy rights need to be able to see links in an application.
For example, we might have:
<s:intercept-url pattern="/resourceManager.htm" access=" ROLE_ADMIN_GROUP, ROLE_PROXY"/>
If the user has the role of proxy, but not the admin role, I need to present them with a page telling them that they need to be in proxy mode to see this page. Additionally, I need to check the permissions of the user they proxy to, to verify they have the correct role.
We have multiple pages, so I'd like to like to do this logic in a filter, so we can apply the logic across the board.
I'm mocking this up in pseudo code while I continue to research.
class Filter
{
protected void doFilterHttp()
{
//proxy summary is session based object
if(proxySummary.isProxyMode())
{
user = proxySummary.getProxiedUser()
//here load user's authorities
//will have to look at ldap authorities populator, but I should be able to work this part out
}
if(user.getGrantedAuthorities.contains("Role_Proxy"))
{
//Is there any way to tell possible valid roles for a url?
if(url.getPossibleRoles() intersect user.getGrantedAuthorities().size == 1 &&
intersection.contains(Role_Proxy))
{ redirectToProxyPage(); }
}
}
What's the best way to get any metadata for the url I'm attempting to access?
If there is no way to get information on all allowable roles for a url, then I imagine I would have to do it at the page.
Would upgrading to Spring Security 3 give me any more flexibility?
I ended up creating a runAsManager implementation that ran as the proxy user if in proxy mode. Otherwise, if the user only had a proxy role for the link, they were redirected. The runAsManager only modified the authentication object when in proxy mode.
I've included snippets of each class, so as not to make the post too long.
RunAsProxy Snippet
public Authentication buildRunAs(Authentication authentication, Object object,
ConfigAttributeDefinition config) {
//probably need to do something to cache the proxied user's roles
if(proxySummary.isProxyMode())
{
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);
String dn = proxySummary.getLoggedInUser();
String [] tmp = { "uid", "cn" };
DirContextOperations user = template.retrieveEntry(dn, tmp);
GrantedAuthority[] proxiedAuthorities = authoritiesPopulator.getGrantedAuthorities(user, user.getStringAttribute("cn").toString());
return new RunAsUserToken(this.key, authentication.getPrincipal(), authentication.getCredentials(),
proxiedAuthorities, authentication.getClass());
}
return null;
}
Interceptor Code -> extends AbstractSecurityInterceptor implements Filter, Ordered
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//same code as from proxy security interceptor here ...
//config attributes are the roles assigned to a link
ConfigAttributeDefinition cad = ((DefaultFilterInvocationDefinitionSource)objectDefinitionSource).lookupAttributes(fi.getRequestUrl());
if(cad != null)
{
HashSet<String> configAttributes = new HashSet<String>();
for(Object ca: cad.getConfigAttributes())
{
configAttributes.add(((ConfigAttribute)ca).getAttribute());
}
SecurityContext sc = SecurityContextHolder.getContext();
HashSet<String> authorities = new HashSet<String>();
for(GrantedAuthority ga: sc.getAuthentication().getAuthorities())
{
authorities.add(ga.getAuthority());
}
//intersection and remaining available roles to determine
//if they just have the proxy role
authorities.retainAll(configAttributes);
if(authorities.size() == 1 && authorities.contains("ROLE_PROXY"))
{
//redirect to page telling them to proxy
((HttpServletResponse)fi.getResponse()).sendRedirect("jsp/doProxy.jsp");
}
//System.out.println(cad);
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
//other boilderplate code
}
Spring Setup
<bean id="proxySecurityInterceptor" class="org.springframework.security.intercept.web.ProxySecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="_accessManager"/>
<property name="proxySummary" ref="proxySummary" />
<property name="runAsManager" ref="runAsProxy" />
<property name="objectDefinitionSource">
<s:filter-invocation-definition-source>
<s:intercept-url pattern="/groupManager.htm*" access="ROLE_GLOBAL_ADMIN, ROLE_ADMIN_GROUP, ROLE_PROXY"/>
</s:filter-invocation-definition-source>
</property>
<s:custom-filter after="FILTER_SECURITY_INTERCEPTOR" />
</bean>

Resources