您的位置:首页 > 编程语言 > Java开发

Spring Security Core Plugin - 5. 配置到安全URL的请求映射

2013-11-30 20:27 176 查看
你可以从以下的方式中选择一种来配置怎样保护你的 URL 访问,目的是将访问者的 URL 与此 URL 安全控制映射起来。

=>@Secured annotations (default approach)

=>A
simple Map in Config.groovy

=>Requestmap
domain class instances stored in the database

以上方式同时只能使用其中一个,通过配置 securityConfigType 属性来实现,其值必须是 SecurityConfigType 枚举类型,或者是该枚举类型的 String 名称。

悲观锁定模式

大多数应用系统通常是 Public 访问权限,只有其中一部分页面需要指定不同的认证。这种情况下,直观的做法是让所有的 URL 都默认开发,然后逐项的配置限制控制。如果你的系统以安全为主,你可以使用悲观锁定模式来拒绝所有没有 路径-角色(URL-Role) 配置的访问。

要设定悲观锁定模式,在 grails-app/conf/Config.groovy 文件中添加如下一行:

grails.plugins.springsecurity.rejectIfNoRule = true

这样,所有的没有设定规则的 URL 都将拒绝任何访问。

URL 和 认证

不管何种方式,你将配置一个映射来控制 URL 和可以访问此 URL 的角色的匹配,例如:/admin/user/** 需要一个 ROLE_ADMIN 角色。你可以结合 IS_AUTHENTICATED_ANONYMOUSLY、IS_AUTHENTICATED_REMEMBERED、和 IS_AUTHENTICATED_FULLY 标志一起使用。一个或多个 投票者(Voters)将验证基于此标志上的角色定义:

=> IS_AUTHENTICATED_ANONYMOUSLY

表示此 URL 可以被任何人访问。默认情况下,AnonymousAuthenticationFilter 将使用 ‘anonymous’ 身份来标识那些没有验证身份的人。这个标志意味着可以接受任何身份,包括 anonymous。

=> IS_AUTHENTICATED_REMEMBERED

需要用户曾经在登录时点击了“记住我”(译者注:即cookie认证)或者是一个明确的登录身份(译者注:即 session 认证)。

=> IS_AUTHENTICATED_FULLY

需要用户明确的登录身份(译者注:即 session 认证)。

通过采用 IS_AUTHENTICATED_FULLY 方式,你可以实现这样一个方案:用户可以在登录过程中通过点击“记住我”选项来保存登录状态,这样任意时候当用户回到你的网站,都可以自动通过认证,但是在某些页面仍然需要输入密码的登录操作。例如:仅依靠 cookie 认证允许通常的查看和添加物品到购物车内,但是在支付和查看购物历史时需要一个明确的登录动作。

关于 IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED、and IS_AUTHENTICATED_ANONYMOUSLY 更多的信息,参看关于

AuthenticatedVoter 的Java文档。

方式间的比较

每种方式都有它的有点和缺点。注解(Annotations)和 Config.groovy 映射方式使用起来不太灵活,因为他们只能在你的代码中配置一次,而且你只能靠重启应用系统来更新他们(至少需要在 prod 模式下)。如果是练习的话这个不足还不是主要问题,因为对于大多数应用来说安全认证的映射未必需要实时更新。

从另一个角度来说,保存 Requestmap 实体能够实现实时配置,这种方式将在系统启动时提供给你一个核心规则集合,你可以编辑、添加,如果需要的话也可以删除。然而,它从应用系统代码中分离了安全规则,这会使得使用起来稍微比将规则定义在 grails-app/conf/Config.groovy 文件中或直接使用注解标注于代码之中。

如果你使用的是 Requestmap 或者是配置于 grails-app/conf/Config.groovy 文件这两种方式,记得映射用 URL 路径必须是全小写。 例如:如果你有一个名为 FooBarController 的控制器(controller),对应的 URL 应该类似 /fooBar/list、/fooBar/create 等等,但是必须像 /foobar/list、/foobar/create 这样配置映射路径。如果你使用注解方式框架将会自动实现这个映射。

5.1 定义安全控制注解

你可用通过在控制器(controller)中使用 @Secured 注解来配置什么规则对于指定的动作(Actions)来说是必要的。要使用注解方式,通过显示定义 securityConfigType=SecurityConfigType.Annotation,或者干脆不定义,因为默认就是这么定义的:

import grails.plugins.springsecurity.SecurityConfigType
...

grails.plugins.springsecurity.securityConfigType = SecurityConfigType.Annotation

你可以在类级定义注解,意味着所有动作都需要这个特定的规则。或者定义在动作级别,或者类级别和动作级别同时定义。如果类级别和动作级别同时定义,那么动作级别定义的规则将有效,因为它们属于“定制的”。

例如,给出这样一个控制器(controller):

package com.mycompany.myapp
import grails.plugins.springsecurity.Secured

class SecureAnnotatedController {

@Secured(['ROLE_ADMIN'])

def index = {

render 'you have ROLE_ADMIN'

}

@Secured(['ROLE_ADMIN', 'ROLE_SUPERUSER'])

def adminEither = {

render 'you have ROLE_ADMIN or SUPERUSER'

}

def anybody = {

render 'anyone can see this'

}

}

你需要通过认证并持有 ROLE_ADMIN 角色才可以访问 /myapp/secureAnnotated (或 /myapp/secureAnnotated/index),需要认证并持有 ROLE_ADMIN 或 ROLE_SUPERUSER 才能访问 /myapp/secureAnnotated/adminEither。任何用户都可以访问 /myapp/secureAnnotated/anybody。

通常控制器中的大多数方法都需要类似的访问控制,因此你也可以在类级别定义注解:

package com.mycompany.myapp
import grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])

class SecureClassAnnotatedController {

def index = {

render 'index: you have ROLE_ADMIN'

}

def otherAction = {

render 'otherAction: you have ROLE_ADMIN'

}

@Secured(['ROLE_SUPERUSER'])

def super = {

render 'super: you have ROLE_SUPERUSER'

}

}

这里,你需要通过认证并持有 ROLE_ADMIN 角色才能访问 /myapp/secureClassAnnotated (或 /myapp/secureClassAnnotated/index) 或 /myapp/secureClassAnnotated/otherAction,然而,你需要持有 ROLE_SUPERUSER 角色才能访问 /myapp/secureClassAnnotated/super,动作级别的注解覆盖类级别的注解。

controllerAnnotations.staticRules

你也可以定义一个不在控制器中解析的静态(static)映射块,例如 ‘/**' 或者 JavaScript、CSS,或者图片路径等等。使用 controllerAnnotations.staticRules 属性定义,例如:

grails.plugins.springsecurity.controllerAnnotations.staticRules = [

'/js/admin/**': ['ROLE_ADMIN'],

'/someplugin/**': ['ROLE_ADMIN']

]

这个例子匹配所有含有形如 /somePlugin/... 形式的 SomePluginController 的动作,角色需求是 ROLE_ADMIN,这里注解不是选项,因为你不需要为了这样的变更而去修改插件代码。

注:当为已经在 UrlMappings.groovy 中定义了 URL 映射的控制器做映射时,你需要定义“未映射”的 URL。例如:如果你的一个 FooBarController 控制器已经映射到 /foo/bar/$action 路径上时,你在 controllerAnnotations.staticRules 中必须定义为 /foobar/**,这么做是必须的而且也是与其他两种方式所不同的,原因是因为 controllerAnnotations.staticRules 定义是与直接在相应的控制器中定义采用一样的方式来处理的。

5.2 简单的在 Config.groovy 中定义映射

要想使用 Config.groovy 来控制 URLs 的访问安全,首先要指定 securityConfigType=SecurityConfigType.InterceptUrlMap:

import grails.plugins.springsecurity.SecurityConfigType
...

grails.plugins.springsecurity.securityConfigType = SecurityConfigType.InterceptUrlMap

然后在 Config.groovy 中定义一个 Map:

grails.plugins.springsecurity.interceptUrlMap = [

'/secure/**': ['ROLE_ADMIN'],

'/finance/**': ['ROLE_FINANCE', 'IS_AUTHENTICATED_FULLY'],

'/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],

'/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],

'/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],

'/*': ['IS_AUTHENTICATED_ANONYMOUSLY'],

'/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],

'/logout/**': ['IS_AUTHENTICATED_ANONYMOUSLY']

]

当使用这个方式,请确保规则排序的正确性。第一个匹配的规则将被使用,因此假如你有一个 controller 有一套设置,但是 action 又指定了一个更具体的规则:

'/secure/**': ['ROLE_ADMIN', 'ROLE_SUPERUSER'],

'/secure/reallysecure/**': ['ROLE_SUPERUSER']

这里会有问题。框架不会阻止一个拥有 ROLE_ADMIN(译者注:原文此处是 ROLE_SUPERUSER,但是我感觉有问题,也许是打印错误?) 权限的用户访问 /secure/reallysecure/list,因为第一条规则已经允许他访问了,因此第二条将被忽略,正确的映射应该是这个样子:

'/secure/reallysecure/**': ['ROLE_SUPERUSER']

'/secure/**': ['ROLE_ADMIN', 'ROLE_SUPERUSER'],

5.3 在数据库中保存 Requestmap 实例

使用此方式,你将使用 Requestmap 实例在数据库中保存规则的映射项。Requestmap 拥有一个包含要保护的 URLs 的正则匹配的 url 属性和一个包含以逗号分隔的规则列表的 configAttribute 属性,例如 IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED 和 IS_AUTHENTICATED_ANONYMOUSLY。

要使用 Requestmap 规则项,需要指定 securityConfigType=SecurityConfigType.Requestmap :

import grails.plugins.springsecurity.SecurityConfigType
...

grails.plugins.springsecurity.securityConfigType = SecurityConfigType.Requestmap

你可以像创建其他领域类实例一样的创建 Requestmap 规则项:

new Requestmap(url: '/js/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save()

new Requestmap(url: '/css/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save()

new Requestmap(url: '/images/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save()

new Requestmap(url: '/login/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save()

new Requestmap(url: '/logout/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save()

new Requestmap(url: '/*', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save()

new Requestmap(url: '/profile/**', configAttribute: 'ROLE_USER').save()

new Requestmap(url: '/admin/**', configAttribute: 'ROLE_ADMIN').save()

new Requestmap(url: '/admin/user/**', configAttribute: 'ROLE_SUPERVISOR').save()

和上面 Config.groovy 映射方法不一样,你不需要关心 Requestmap 规则项的排序问题,因为插件已经通过计算保证了这些规则的正确匹配顺序。

Requestmap 的缓存

处于性能原因的考虑,Requestmap 规则项是被缓存的。但是缓存会影响运行期配置的灵活性。如果你创建、编辑或者删除了一条规则,缓存必须被刷新来保证与数据库中数据的一致性。你可以通过调用 springSecurityService.clearCachedRequestmaps() 来实现缓存的刷新。例如,如果你创建了一个 RequestmapController ,那么 save action 应该是这个样子(同样的, update 和 delete 也需要调用 clearCachedRequestmaps()
):

class RequestmapController {

def springSecurityService

...

def save = {

def requestmapInstance = new Requestmap(params)

if (!requestmapInstance.save(flush: true)) {

render view: 'create', model: [requestmapInstance: requestmapInstance]

return

}

springSecurityService.clearCachedRequestmaps()

flash.message = "${message(code: 'default.created.message', args: [message(code: 'requestmap.label', default: 'Requestmap'), requestmapInstance.id])}"

redirect action: show, id: requestmapInstance.id

}

}

5.4 使用表达式创建清晰、细刻度的规则

Spring Security 允许你使用
Spring Expression Language (SpEL) 创建相对于传统方式而言更加清晰的保护 URLs 的规则,而且也是更加细致的规则。在你使用传统方式指定一个列表和/或指定具体标志(Tokens),例如 IS_AUTHENTICATED_FULLY 的位置,基于

Spring Security's expression support 的支持下,你可以使用嵌入式脚本语言(embedded scripting language)方式来定义简单或者负责的访问规则。

你可以配合之前描述的任何方式来使用表达式保护应用的 URLs。例如,思考一下这个注解方式的 controller:

package com.yourcompany.yourapp
import grails.plugins.springsecurity.Secured

class SecureController {

@Secured(["hasRole('ROLE_ADMIN')"])

def someAction = {



}

@Secured(["authentication.name == 'ralph'"])

def someOtherAction = {



}

}

在这里例子中,someAction 需要 ROLE_ADMIN 权限,someOtherAction 需要以用户名为 ralph 的身份登录。

相应的 Requestmap 规则应该是这样的:

new Requestmap(url: "/secure/someAction", configAttribute: "hasRole('ROLE_ADMIN'").save()
new Requestmap(url: "/secure/someOtherAction", configAttribute: "authentication.name == 'ralph'").save()

相应的静态映射规则应该是这样的:

grails.plugins.springsecurity.interceptUrlMap = [

'/secure/someAction': ["hasRole('ROLE_ADMIN'"],

'/secure/someOtherAction': ["authentication.name == 'ralph'"]

]

Spring Security 文档含有一份标准规则列表(table listing the standard expressions),复制如下以供参考:

表达式

描述

hasRole(role)
如果角色拥有指定的权限(role)则返回true

hasAnyRole([role1,role2])如果角色拥有列表中任意一个权限则返回true

principal允许直接访问角色对象代表当前用户authentication允许直接访问 Security 上下文中的认证对象permitAll总是返回truedenyAll总是返回falseisAnonymous()如果角色是一个 anonymous 用户则返回true

isRememberMe()如果角色是一个 remember-me 用户则返回true

isAuthenticated()如果角色不是 anonymous 则返回true

isFullyAuthenticated()如果角色即不是 anonymous 也不是 remember-me 用户则返回true

另外,你可以使用 WEB 特有的表达式 hasIpAddress 。不管怎样,你将发现使用IP地址过滤(IP address filter)将 IP 限制从其他规则限制中分离出来是多么容易的事情。

为了帮助你实现从传统的配置到表达式的转变,下表比较了各种配置以及和他们一致的表达式:

传统配置

表达式

ROLE_ADMINhasRole('ROLE_USER')ROLE_USER,ROLE_ADMINhasAnyRole('ROLE_USER,ROLE_ADMIN')ROLE_ADMIN,IS_AUTHENTICATED_FULLYhasRole('ROLE_ADMIN') and isFullyAuthenticated()IS_AUTHENTICATED_ANONYMOUSLYpermitAllIS_AUTHENTICATED_REMEMBEREDisAnonymous() or isRememberMe()IS_AUTHENTICATED_FULLY
isFullyAuthenticated()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐