grails-spring-security-rest 插件和悲观锁定
Posted
技术标签:
【中文标题】grails-spring-security-rest 插件和悲观锁定【英文标题】:grails-spring-security-rest plugin and pesimistic lockdown 【发布时间】:2015-04-09 21:58:55 【问题描述】:我有一个主要是 REST API 的 Grails 项目。我定义的端点在没有弹簧安全的情况下工作正常,所有 url 都可以访问并且响应正常。 因此,转向身份验证,我安装了 grails-spring-security-rest 插件。 这是配置:
Config.groovy
grails.plugin.springsecurity.filterChain.chainMap = [
'/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter', // Stateless chain
'/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter' // Traditional chain
]
grails.plugin.springsecurity.userLookup.userDomainClassName = 'transportados.backend.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'transportados.backend.UserRole'
grails.plugin.springsecurity.authority.className = 'transportados.backend.Role'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
'/': ['permitAll'],
'/index': ['permitAll'],
'/index.gsp': ['permitAll'],
'/**/js/**': ['permitAll'],
'/**/css/**': ['permitAll'],
'/**/images/**': ['permitAll'],
'/**/favicon.ico': ['permitAll']
UrlMappings.groovy
class UrlMappings
static mappings =
"/$controller/$action?/$id?(.$format)?"
constraints
// apply constraints here
/*******************************************************************************************************************
*********************************************** API v1 **********************************************************
*******************************************************************************************************************/
// Shipments
"/api/v1/shipments"(controller: "Shipment", action: [POST: 'save', GET: 'index'], namespace:'v1')
"/api/v1/shipments/$id"(controller: "Shipment", action: [PUT: 'update', GET: 'show', DELETE:'delete'], namespace:'v1')
// Carrier
"/api/v1/carriers"(controller: "Carrier", action: [POST: 'save', GET: 'index'], namespace:'v1')
"/api/v1/carriers/$id"(controller: "Carrier", action: [PUT: 'update', GET: 'show', DELETE:'delete'], namespace:'v1')
// Item
"/api/v1/items"(controller: "Item", action: [POST: 'save', GET: 'index'], namespace:'v1')
"/api/v1/items/$id"(controller: "Item", action: [PUT: 'update', GET: 'show', DELETE:'delete'], namespace:'v1')
// Quote
"/api/v1/quotes"(controller: "Quote", action: [POST: 'save', GET: 'index'], namespace:'v1')
"/api/v1/quotes/$id"(controller: "Quote", action: [PUT: 'update', GET: 'show', DELETE:'delete'], namespace:'v1')
// Review
"/api/v1/reviews"(controller: "Review", action: [POST: 'save', GET: 'index'], namespace:'v1')
"/api/v1/reviews/$id"(controller: "Review", action: [PUT: 'update', GET: 'show', DELETE:'delete'], namespace:'v1')
// User
"/api/v1/users"(controller: "User", action: [POST: 'save', GET: 'index'], namespace:'v1')
"/api/v1/users/$id"(controller: "User", action: [PUT: 'update', GET: 'show', DELETE:'delete'], namespace:'v1')
// Vehicle
"/api/v1/vehicles"(controller: "Vehicle", action: [POST: 'save', GET: 'index'], namespace:'v1')
"/api/v1/vehicles/$id"(controller: "Vehicle", action: [PUT: 'update', GET: 'show', DELETE:'delete'], namespace:'v1')
"/"(view:"/index")
"500"(view:'/error')
资源http://localhost:8080/api/login 工作正常,如果凭据正常,则给我一个有效的令牌。
当尝试使用它访问以下控制器时,我被 FilterSecurityInterceptor 拒绝访问
package transportados.backend
import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional
import grails.plugin.springsecurity.annotation.Secured
@Secured(['ROLE_ADMIN'])
@Transactional(readOnly = true)
class ShipmentController
static namespace = 'v1'
static responseFormats = ['json', 'xml']
//static allowedMethods = [index: "GET", save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max)
params.max = Math.min(max ?: 10, 100)
respond Shipment.list(params), [status: OK]
def show(Shipment shipment)
respond shipment
@Transactional
def save(Shipment shipmentInstance)
if (shipmentInstance == null)
render status: NOT_FOUND
return
shipmentInstance.validate()
if (shipmentInstance.hasErrors())
render status: NOT_ACCEPTABLE
return
shipmentInstance.save flush:true
respond shipmentInstance, [status: CREATED]
@Transactional
def update(Shipment shipmentInstance)
if (shipmentInstance == null)
render status: NOT_FOUND
return
shipmentInstance.validate()
if (shipmentInstance.hasErrors())
render status: NOT_ACCEPTABLE
return
shipmentInstance.save flush:true
respond shipmentInstance, [status: OK]
@Transactional
def delete(Shipment shipmentInstance)
if (shipmentInstance == null)
render status: NOT_FOUND
return
shipmentInstance.delete flush:true
render status: NO_CONTENT
这些是日志:
|Server running. Browse to http://localhost:8080/
2015-02-09 21:04:43,549 [http-bio-8080-exec-6] DEBUG matcher.AntPathRequestMatcher - Checking match of request : '/api/v1/shipments'; against '/api/v1/**'
2015-02-09 21:04:43,550 [http-bio-8080-exec-6] DEBUG web.FilterChainProxy - /api/v1/shipments at position 1 of 7 in additional filter chain; firing Filter: 'MutableLogoutFilter'
2015-02-09 21:04:43,550 [http-bio-8080-exec-6] DEBUG web.FilterChainProxy - /api/v1/shipments at position 2 of 7 in additional filter chain; firing Filter: 'RestAuthenticationFilter'
2015-02-09 21:04:43,561 [http-bio-8080-exec-6] DEBUG rest.RestAuthenticationFilter - Actual URI is /api/v1/shipments; endpoint URL is /api/login
2015-02-09 21:04:43,561 [http-bio-8080-exec-6] DEBUG web.FilterChainProxy - /api/v1/shipments at position 3 of 7 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2015-02-09 21:04:43,561 [http-bio-8080-exec-6] DEBUG web.FilterChainProxy - /api/v1/shipments at position 4 of 7 in additional filter chain; firing Filter: 'GrailsAnonymousAuthenticationFilter'
2015-02-09 21:04:43,561 [http-bio-8080-exec-6] DEBUG web.FilterChainProxy - /api/v1/shipments at position 5 of 7 in additional filter chain; firing Filter: 'RestTokenValidationFilter'
2015-02-09 21:04:43,564 [http-bio-8080-exec-6] DEBUG bearer.BearerTokenReader - Looking for bearer token in Authorization header, query string or Form-Encoded body parameter
2015-02-09 21:04:43,566 [http-bio-8080-exec-6] DEBUG bearer.BearerTokenReader - Found bearer token in Authorization header
2015-02-09 21:04:43,567 [http-bio-8080-exec-6] DEBUG rest.RestTokenValidationFilter - Token found: eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MjM1MjkwMzAsInN1YiI6Im1lIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJpYXQiOjE0MjM1MjU0MzB9.CLUxW5reqfnn-UDUtNul7CTRg4O5GIuz4zeY1UghQn
2015-02-09 21:04:43,567 [http-bio-8080-exec-6] DEBUG rest.RestTokenValidationFilter - Trying to authenticate the token
2015-02-09 21:04:43,581 [http-bio-8080-exec-6] DEBUG rest.RestAuthenticationProvider - Trying to validate token eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MjM1MjkwMzAsInN1YiI6Im1lIiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJpYXQiOjE0MjM1MjU0MzB9.CLUxW5reqfnn-UDUtNul7CTRg4O5GIuz4zeY1UghQn
2015-02-09 21:04:43,602 [http-bio-8080-exec-6] DEBUG jwt.JwtTokenStorageService - Parsed an HMAC signed JWT
2015-02-09 21:04:43,688 [http-bio-8080-exec-6] DEBUG jwt.JwtTokenStorageService - Successfully verified JWT
2015-02-09 21:04:43,692 [http-bio-8080-exec-6] DEBUG rest.RestAuthenticationProvider - Authentication result: com.odobo.grails.plugin.springsecurity.rest.RestAuthenticationToken@40a2eeba: Principal: org.springframework.security.core.userdetails.User@d98: Username: me; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN
2015-02-09 21:04:43,692 [http-bio-8080-exec-6] DEBUG rest.RestTokenValidationFilter - Token authenticated. Storing the authentication result in the security context
2015-02-09 21:04:43,692 [http-bio-8080-exec-6] DEBUG rest.RestTokenValidationFilter - Authentication result: com.odobo.grails.plugin.springsecurity.rest.RestAuthenticationToken@40a2eeba: Principal: org.springframework.security.core.userdetails.User@d98: Username: me; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN
2015-02-09 21:04:43,694 [http-bio-8080-exec-6] DEBUG rest.RestTokenValidationFilter - Continuing the filter chain
2015-02-09 21:04:43,695 [http-bio-8080-exec-6] DEBUG web.FilterChainProxy - /api/v1/shipments at position 6 of 7 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2015-02-09 21:04:43,695 [http-bio-8080-exec-6] DEBUG web.FilterChainProxy - /api/v1/shipments at position 7 of 7 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2015-02-09 21:04:43,696 [http-bio-8080-exec-6] DEBUG intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /api/v1/shipments; Attributes: [_DENY_]
2015-02-09 21:04:43,696 [http-bio-8080-exec-6] DEBUG intercept.FilterSecurityInterceptor - Previously Authenticated: com.odobo.grails.plugin.springsecurity.rest.RestAuthenticationToken@40a2eeba: Principal: org.springframework.security.core.userdetails.User@d98: Username: me; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_ADMIN
2015-02-09 21:04:43,696 [http-bio-8080-exec-6] DEBUG hierarchicalroles.RoleHierarchyImpl - getReachableGrantedAuthorities() - From the roles [ROLE_ADMIN] one can reach [ROLE_ADMIN] in zero or more steps.
2015-02-09 21:04:43,703 [http-bio-8080-exec-6] DEBUG access.ExceptionTranslationFilter - Access is denied (user is not anonymous); delegating to AccessDeniedHandler
Message: Access is denied
Line | Method
->> 47 | decide in grails.plugin.springsecurity.access.vote.AuthenticatedVetoableDecisionManager
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 102 | processFilterChain in com.odobo.grails.plugin.springsecurity.rest.RestTokenValidationFilter
| 68 | doFilter . . . . . in ''
| 53 | doFilter in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
| 122 | doFilter . . . . . in com.odobo.grails.plugin.springsecurity.rest.RestAuthenticationFilter
| 82 | doFilter in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
| 1145 | runWorker . . . . in java.util.concurrent.ThreadPoolExecutor
| 615 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 744 | run . . . . . . . in java.lang.Thread
对正在发生的事情有任何想法吗?这让我有点疯狂:S
谢谢!
【问题讨论】:
【参考方案1】:UrlMappings 中的命名空间是问题所在。 删除 urlmappings 中的命名空间定义和控制器中的静态变量“命名空间”后,生活又美好了。
【讨论】:
【参考方案2】:Spring Security Core 插件不支持命名空间控制器:https://***.com/a/21896846/2923710
或者,您可以尝试在 Config.groovy 中使用 interceptUrlMap
,如 http://grails-plugins.github.io/grails-spring-security-core/guide/requestMappings.html#configGroovyMap 中指定的那样
【讨论】:
以上是关于grails-spring-security-rest 插件和悲观锁定的主要内容,如果未能解决你的问题,请参考以下文章