Basically, you will use Spring's RequestHeaderAuthenticationFilter to get the user ID from the request headers and PreAuthenticatedAuthenticationProvider which is for use with any system where the user has already been authenticated (in this case by Shibboleth). This example also makes use of a custom MockRequestHeaderAuthenticationFilter which allows you to pass in a user ID when you're in the development environment to make testing easier.
Of course, first thing is you need to install Spring Security plugin and set its properties so it knows about your domain classes. Then, in conf/spring/resources.groovy you will need to set up a few beans:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
beans = { | |
userDetailsServiceWrapper(org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper) { | |
userDetailsService = ref('userDetailsService') | |
} | |
preauthAuthenticationProvider(org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider) { | |
preAuthenticatedUserDetailsService = ref('userDetailsServiceWrapper') | |
} | |
shibAuthFilter(org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter) { | |
principalRequestHeader = 'eppn' //this is the shib header that contains the user ID | |
checkForPrincipalChanges = true | |
invalidateSessionOnPrincipalChange = true | |
continueFilterChainOnUnsuccessfulAuthentication = false | |
authenticationManager = ref('authenticationManager') | |
} | |
//needed for mock authentication on local dev tier | |
mockTestFilter(com.example.test.util.MockRequestHeaderAuthenticationFilter) { | |
mockPrincipal = 'test' //Change this if you want to log in as a different user | |
checkForPrincipalChanges = true | |
invalidateSessionOnPrincipalChange = true | |
continueFilterChainOnUnsuccessfulAuthentication = false | |
authenticationManager = ref('authenticationManager') | |
} | |
} |
In conf/Config.groovy, you need to let Spring Security know to use your preauthAuthenticationProvider:
grails.plugins.springsecurity.providerNames = ['preauthAuthenticationProvider']
This block in conf/BootStrap.groovy actually registers the filter for each environment. For development and test we want the mock filter and for QA and production, we use the real filter:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
environments { | |
development { | |
SpringSecurityUtils.clientRegisterFilter('mockTestFilter', SecurityFilterPosition.PRE_AUTH_FILTER.order + 10) | |
} | |
test { | |
SpringSecurityUtils.clientRegisterFilter('mockTestFilter', SecurityFilterPosition.PRE_AUTH_FILTER.order + 10) | |
} | |
qa { | |
SpringSecurityUtils.clientRegisterFilter('shibAuthFilter', SecurityFilterPosition.PRE_AUTH_FILTER.order + 10) | |
} | |
production { | |
SpringSecurityUtils.clientRegisterFilter('shibAuthFilter', SecurityFilterPosition.PRE_AUTH_FILTER.order + 10) | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.test.util | |
import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter | |
class MockRequestHeaderAuthenticationFilter extends RequestHeaderAuthenticationFilter { | |
String mockPrincipal | |
@Override | |
protected Object getPreAuthenticatedPrincipal(javax.servlet.http.HttpServletRequest request) { | |
mockPrincipal | |
} | |
} |
Thanks Ed, that was very helpful so thanks for writing this up.
ReplyDeleteI've also documented my own version on my blog for others interested in seeing various solutions to the same problem
http://pwu-developer.blogspot.com.au/2015/12/grails-2x-and-shibboleth-authentication.html
Cheers