我有一个使用 Groovy 2.0 的 Grails 2.2.3 项目。我使用 Spring Security 将其设置为使用 CAS 进行身份验证,使用 LDAP 进行用户角色。当我运行应用程序时,一切都按预期进行:任何人都允许访问/appcontext/,并且/appcontext/admin/下的任何内容均由 CAS 和 LDAP 的管理员角色保护。我现在正在尝试使用最新版本的 Grails 和 Groovy。我安装了 GGTS 3.4.0.RELEASE 并使用 Grails 2.3.0 和 Groovy 2.1。我创建了一个新项目,制作了一个简单的域类和 Controller ,并添加到安全设置中。
以下是我使用 Grails 2.2.3 和 Groovy 2.0 通过 GGTS 3.3.0.RELEASE 运行应用程序时的输出:(请注意“服务器正在运行”消息的位置)
| Loading Grails 2.2.3
| Configuring classpath.
| Environment set to development.....
| Packaging Grails application.....
| Running Grails application
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security CAS ...
... finished configuring Spring Security CAS
Configuring Spring Security LDAP ...
... finished configuring Spring Security LDAP
| Server running. Browse to http://localhost:8080/appcontext
以下是我使用 Grails 2.3.0 和 Groovy 2.1 运行 GGTS 3.4.0.RELEASE 应用程序时的输出(请注意“服务器正在运行”消息的位置):
| Loading Grails 2.3.0
| Configuring classpath.
| Environment set to development.....
| Packaging Grails application.....
| Compiling 1 source files.....
| Running Grails application
| Server running. Browse to http://localhost:8080/appcontext
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security LDAP ...
... finished configuring Spring Security LDAP
Error initializing the application: No bean named 'casAuthenticationProvider' is defined
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'casAuthenticationProvider' is defined
at SpringSecurityCoreGrailsPlugin$_createBeanList_closure22.doCall(SpringSecurityCoreGrailsPlugin.groovy:686)
at SpringSecurityCoreGrailsPlugin.createBeanList(SpringSecurityCoreGrailsPlugin.groovy:686)
at SpringSecurityCoreGrailsPlugin$_closure4.doCall(SpringSecurityCoreGrailsPlugin.groovy:615)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
| Error 2013-10-15 11:33:02,925 [localhost-startStop-1] ERROR context.GrailsContextLoader - Error initializing the application: No bean named 'casAuthenticationProvider' is defined
Message: No bean named 'casAuthenticationProvider' is defined
Line | Method
->> 686 | doCall in SpringSecurityCoreGrailsPlugin$_createBeanList_closure22
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 615 | doCall in SpringSecurityCoreGrailsPlugin$_closure4
| 303 | innerRun . . . in java.util.concurrent.FutureTask$Sync
| 138 | run in java.util.concurrent.FutureTask
| 886 | runTask . . . in java.util.concurrent.ThreadPoolExecutor$Worker
| 908 | run in ''
^ 662 | run . . . . . in java.lang.Thread
schema export unsuccessful
org.h2.jdbc.JdbcSQLException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-170]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
at org.h2.message.DbException.get(DbException.java:169)
at org.h2.message.DbException.get(DbException.java:146)
at org.h2.message.DbException.get(DbException.java:135)
at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1391)
at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1366)
at org.h2.jdbc.JdbcConnection.getAutoCommit(JdbcConnection.java:424)
at java.lang.Thread.run(Thread.java:662)
| Error 2013-10-15 11:33:03,071 [Thread-9] ERROR hbm2ddl.SchemaExport - schema export unsuccessful
Message: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-170]
Line | Method
->> 329 | getJdbcSQLException in org.h2.message.DbException
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 169 | get in ''
| 146 | get . . . . . . . . in ''
| 135 | get in ''
| 1391 | checkClosed . . . . in org.h2.jdbc.JdbcConnection
| 1366 | checkClosed in ''
| 424 | getAutoCommit . . . in ''
^ 662 | run in java.lang.Thread
| Error Forked Grails VM exited with error
这是我的基本安全设置:
conf/spring/resources.groovy
import org.apache.commons.lang.StringEscapeUtils
// Place your Spring DSL code here
beans = {
// load ldap roles from spring security
def ldapUrl = StringEscapeUtils.escapeJava('${ldap.defaultUrl}')
def ldapUser = StringEscapeUtils.escapeJava('${ldap.username}')
def ldapPassword = StringEscapeUtils.escapeJava('${ldap.password}')
def ldapBase = StringEscapeUtils.escapeJava('${ldap.base}')
def ldapRoleSearchBase = StringEscapeUtils.escapeJava('${ldap.roleSearchBase}')
initialDirContextFactory(org.springframework.security.ldap.DefaultSpringSecurityContextSource, ldapUrl){
userDn = ldapUser
password = ldapPassword
}
ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
ldapBase, 'sAMAccountName={0}', initialDirContextFactory){ }
ldapAuthoritiesPopulator(org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator,
initialDirContextFactory, ldapRoleSearchBase){
groupRoleAttribute = 'cn'
groupSearchFilter = 'member={0}'
searchSubtree = true
rolePrefix = 'ROLE_'
convertToUpperCase = true
ignorePartialResultException = true
}
userDetailsService(org.springframework.security.ldap.userdetails.LdapUserDetailsService,ldapUserSearch,ldapAuthoritiesPopulator){ }
}
conf/Config.groovy
def appName = grails.util.Metadata.current.getApplicationName()
environments {
development {
grails.logging.jul.usebridge = true
host.ip = "12.34.56.78"
host.port = "8080"
host.securePort = "8080"
ldap.username = "ldapUsername"
ldap.password = "ldapPassword"
ldap.base = "DC=foo,DC=company,DC=com"
ldap.roleSearchBase = "OU=bar,DC=foo,DC=company,DC=com"
ldap.defaultUrl = "ldap://123.45.67.89:389"
ldap.urls = "ldap://123.45.67.89:389 ldap://123.45.67.89:389"
cas.url = "https://sso.company.com/cas/"
cas.loginUrl = "https://sso.company.com/cas/login"
cas.logoutUrl = "https://sso.company.com/cas/logout"
grails.plugins.springsecurity.cas.serviceUrl = 'http://${host.ip}:${host.securePort}/' + appName +'/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.proxyCallbackUrl = 'http://${host.ip}:${host.securePort}/' + appName +'/secure/receptor'
}
production {
grails.logging.jul.usebridge = false
grails.plugins.springsecurity.cas.serviceUrl = 'https://${host.ip}:${host.securePort}/' + appName +'/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.proxyCallbackUrl = 'https://${host.ip}:${host.securePort}/' + appName +'/secure/receptor'
}
}
//spring security core config
grails.plugins.springsecurity.providerNames = ['casAuthenticationProvider']
grails.plugins.springsecurity.rejectIfNoRule = true
grails.plugins.springsecurity.securityConfigType = "InterceptUrlMap"
grails.plugins.springsecurity.interceptUrlMap = [
'/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/admin/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/admin/logout/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/admin/**': ['hasAnyRole("ROLE_ADMIN")'],
'/**': ['IS_AUTHENTICATED_ANONYMOUSLY']
]
//cas config
grails.plugins.springsecurity.cas.loginUri = 'login'
grails.plugins.springsecurity.cas.serverUrlPrefix = '${cas.url}'
grails.plugins.springsecurity.cas.proxyReceptorUrl = '/secure/receptor'
conf/BuildConfig.groovy
compile ":spring-security-core:1.2.7.3"
compile ":spring-security-cas:1.0.5"
compile ":spring-security-ldap:1.0.6"
编辑 使用下面接受的答案的建议,我能够正确配置 Spring Security CAS,但我的 Controller 仍然不安全。我认为这与奇怪的加载顺序有关,应用程序服务器说它正在运行,然后加载 Spring Security、LDAP 和 CAS。一位同事建议取出我的 InterceptUrlMap 并使用 @Secured 注释来查看是否是加载顺序(因为在一切启动并运行后 InterceptUrlMap 无法更新)。我摆脱了rejectIfNoRule、securityConfigType 和interceptUrlMap 设置,并向 Controller 添加了@Secured(['ROLE_ADMIN']) 。该应用程序现在可以按预期工作,并且该 Controller 已受到保护。
因此,Grails 2.3.0 和 Spring Security 的事件顺序仍然存在问题,但这是一种解决方法。
最佳答案
我也看到同样的事情。看起来 CAS 插件默认来自 DefaultCasSecurityConfig.groovy在 Grails 2.3.0 下没有正确合并。可能值得一个 JIRA。同时,您可以通过将默认值添加到 Config.groovy(覆盖您的环境)来继续前进:
grails.plugins.springsecurity.cas.active = true
grails.plugins.springsecurity.cas.loginUri = null // must be set, e.g. '/login'
grails.plugins.springsecurity.cas.sendRenew = false
grails.plugins.springsecurity.cas.serviceUrl = null // must be set, e.g. 'http://localhost:8080/myapp/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.serverUrlPrefix = null // must be set, e.g. 'http://localhost:9090/cas'
grails.plugins.springsecurity.cas.serverUrlEncoding = 'UTF-8'
grails.plugins.springsecurity.cas.key = 'grails-spring-security-cas'
grails.plugins.springsecurity.cas.artifactParameter = 'ticket'
grails.plugins.springsecurity.cas.serviceParameter = 'service'
grails.plugins.springsecurity.cas.filterProcessesUrl = '/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.proxyCallbackUrl = null // should be set, e.g. 'http://localhost:8080/myapp/secure/receptor'
grails.plugins.springsecurity.cas.proxyReceptorUrl = null // should be set, e.g. '/secure/receptor'
grails.plugins.springsecurity.cas.useSingleSignout = true
关于Grails:Spring Security CAS 在 2.2.3 中工作,但在 2.3.0 中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19385581/