在 Spring 安全中使用拦截 URL
Posted
技术标签:
【中文标题】在 Spring 安全中使用拦截 URL【英文标题】:Using intercept-url in Spring security 【发布时间】:2013-05-06 18:58:49 【问题描述】:在 Spring 安全性的 intercept-url
元素中制作模式的首选方法是什么?
我正在创建一个 Web 服务 (RESTful),我目前要求所有用户都已登录并拥有ROLE_USER
角色。然后通过服务层上的@PreAuthorize
注释来实施进一步的约束。但是,添加多个具有不同配置的 intercept-url
元素是否常见?
【问题讨论】:
【参考方案1】:SpringSecurity 的配置取决于您为应用程序选择的身份验证: 例如,对于表单认证,你想配置登录 url 和注销成功 url 没有权限:
<http realm="Contacts Realm">
<intercept-url pattern="/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER"/>
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
<logout logout-success-url="/index.jsp"/>
</http>
在基本身份验证的情况下,您没有登录和注销 URL,配置更简单:
<http realm="Contacts Realm">
<intercept-url pattern="/**" access="ROLE_USER"/>
<http-basic/>
</http>
如果您将选择基本身份验证,请使用第二个示例。
【讨论】:
而且......这并没有比我已经在问题中写的更多。那你在服务层做剩下的授权吗? (它是一个 REST 网络服务,使用 BASIC)【参考方案2】:Spring Security 通常适用于基于角色的访问。就是这样。您可以创建自己的 ProcessingFilter 来做一些额外的事情。但是,我发现这是一项无益的大量工作。
另一种方法是使用请求拦截器。在几个使用 Spring MVC 的 RESTful 服务项目中,我利用请求拦截器来完成基本角色访问之外的一些身份验证。这行得通,而且在我看来,这使得事件链更容易跟踪,但如果您每分钟处理数千笔交易,它可能会使请求陷入困境。
【讨论】:
【参考方案3】:可以通过这种方式拦截 URL。假设您的 REST 服务 URL 以 /rest/
开头。
<http auto-config="true" use-expressions="true" access-denied-page="/accessDeniedPageURL">
<intercept-url pattern="/rest/**" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/view/products" access="isFullyAuthenticated()"/>
<form-login login-page="/landing" default-target-url="/view/products" authentication-failure-handler-ref="authenticationFailureHandler"/>
<logout invalidate-session="true" logout-success-url="/landing" delete-cookies="JSESSIONID"/>
<session-management invalid-session-url="/landing" session-authentication-error-url="/landing?msg=alreadyLogin">
<concurrency-control max-sessions="1" expired-url="/landing?msg=sessionExpiredDuplicateLogin" error-if-maximum-exceeded="false"/>
</session-management>
</http>
【讨论】:
【参考方案4】:大多数使用 Spring Security 的 Web 应用程序只有几个 intercept-url
s,因为它们只有非常基本的安全要求。您需要对登录和登录错误屏幕以及公共站点的某些方面进行未经身份验证的访问,因此可以是一些 URL 模式。然后通常有一个管理部分,然后其他所有内容都是ROLE_USER
。
如果您需要更多角色,通常会将它们与*** URL 路径组件相关联。虽然这不是必需的,但它可以更轻松地确保资源得到适当的保护。
<http realm="Contacts Realm" use-expressions="false">
<intercept-url pattern="/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/admin/*" access="ROLE_ADMIN"/>
<intercept-url pattern="/secret/*" access="ROLE_SECRET"/>
<intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN,ROLE_SECRET"/>
<http-basic/>
</http>
您必须根据您的用例决定是否允许人们拥有多个角色。在应用程序中管理有点困难,因此大多数具有简单安全性的人都会对其进行设置,以便用户只有一个角色,然后他们允许多个角色访问受保护的内容。当然,另一种方法是每个 URL 模式一个角色,并赋予人们多个角色。
无论如何,您的问题(或至少我认为您要问的问题)的答案是,通常要做的事情是每个角色使用一个***路径组件来保护具有相同安全限制的所有资源。当然,您也在该路径前缀下对功能进行分组,因此有些人讨厌这种结构,而有些人只是更喜欢在代码中使用注释。我喜欢我的安全性,我可以在一个地方看到它,并且可以通过查看 URL 轻松判断安全性期望是什么。
【讨论】:
这是错误的:access="ROLE_ADMIN",应该是"hasRole('ROLE_ADMIN')" @otterslide Spring 引入了 expression-based access control 作为选项,但不是必需的。要使用它,需要将http
标记的use-expressions
属性设置为true
,您会看到在此示例中没有这样做。基于表达式的访问控制允许比此处说明的简单的基于角色的限制更复杂的限制,但也不是更好。由于配置错误是安全故障的主要来源,因此我更喜欢尽可能简单的基于角色的配置。
这是有道理的,但我使用的是 Spring 4,并且“use-expressions”默认为“true”。见springframework.org/schema/security/spring-security.xsd,它说它默认为true。我确实看到以前的 Spring 3 默认为 false。值得一提的是,答案中可能需要 use-expressions="false" 。我确实尝试过,没有 use-expressions="true",但它不起作用,只有表达式起作用。谢谢。【参考方案5】:
我应该认为“首选方式”是[必然]主观的。我的security.xml
中有<intercept-url>
元素,但放弃了它们,转而使用控制器上的@PreAuthorize
注释@RequestMapping
。这纯粹是个人喜好(在我的情况下),因为我更喜欢将内容保存在 Java 中而不是 XML 命名空间中,尽可能多地这样做。
您可以使用一个、另一个或两者,并且可以使注释增强 XML ——例如,您可以使用以下内容:
security.xml:
<intercept-url pattern="/something" access="hasRole('ROLE_USER')"/>
YourController.java:
@RequestMapping(value="/something/else")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String getSomethingElseContents() return "somethingelse";
正如我所说,这似乎只是一个偏好问题。使用 XML 命名空间的好处是基本访问规则都在一个地方,易于阅读和遵循。 @PreAuthorize
(或其他注释变体)施加的规则特别性感,因为您可以在其中调用自己的 SpEL 表达式,并根据对传递的参数或类可访问的字段的访问权限决定您的权限(即 @PreAuthorize("@my.project.package.hasMagicPower(#power.INVISIBILITY)")
) ,或这些的组合。您可以将原本不可用的逻辑和动态权限应用于命名空间选项。
当然,您可以使用“和”、“或”和“!”来应用基本逻辑。命名空间中 SpEL 表达式中的连接词(您可以通过 XML 中的 @
指示符访问外部类 [boolean] 方法),但 XML 中指定的所有内容都必须是静态的。
tl;dr:个人偏好就是偏好,但如果您希望或需要在权限处理中动态灵活,您必须使用注释。如果您既不想也不需要动态灵活性,那么您可以选择(并且一个合理的论点会建议命名空间选项相对于 SoC 更好)。我更喜欢让 Java 来处理它的灵活性,因为一旦我设置了我的 XML,我想不管它并专注于 Java。此外,对于 SoC 观点,还有一个颇具说服力的反驳,即适当常规化的 Java 应用程序将其控制器放在易于查找的包中,并具有明显的控制名称。
还是 tl;dr:嗯。六个一个,六个另一个。我说 po-tay-to。
【讨论】:
【参考方案6】:是的,定义多个拦截 URL 模式然后在顶部添加注释以进一步限制特定资源是很常见的。
您还可以为每个限制使用一个目录,这样您就不需要注释了。前任。 /user/** 需要 ROLE_USER,/admin/** 需要 ROLE_ADMIN 等。
在使用任何一种方法时请记住,如果您犯了错误,它会如何影响您的安全。
如果使用拦截url,则必须将行按正确的顺序排列,例如:
<intercept-url pattern="/**" access="permitAll"/>
<intercept-url pattern="/user/**" access="hasRole('ROLE_USER')"/>
将无法正常工作,因为您在第一行屏蔽了所有访问权限,因此必须颠倒这些行。如果初学者正在处理您的项目并反转线条,这可能是一个大问题。
如果你使用注解,可能会误删注解行,给你的应用留下一个安全漏洞。
@PreAuthorize("hasRole('ROLE_USER')")
如果您在两个项目之间重用一个控制器类,并且在一个项目中您希望允许 ROLE_USER 访问控制器内的所有方法,而另一个使用公共代码的项目需要 ROLE_ADMIN,那么实现这一点的最简单方法是使用拦截网址。
所以请记住,@PreAuthorize 将被硬编码并在项目之间共享。您可以扩展类并为每个项目添加不同的@PreAuthorize,但这会增加额外的复杂性。
我喜欢使用intercept-url 来保护整个区域,例如/user/**,因为误删注释的风险较小。
如果我需要添加细粒度访问,我会在控制器中的方法中添加一个注解,该注解已经被intercept-url 保护,但这种情况很少发生。
【讨论】:
以上是关于在 Spring 安全中使用拦截 URL的主要内容,如果未能解决你的问题,请参考以下文章