30. JSP 标签库

Spring Security 有自己的 taglib,它为访问安全信息和在 JSP 中应用安全约束提供了基本的支持。

30.1 声明 Taglib

要使用任何标记,必须在 JSP 中声明安全标记库:

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

30.2 授权标签

该标签用于确定是否应评估其内容。在 Spring Security 3.0 中,可以两种方式使用[21]。第一种方法使用在标记的access属性中指定的web-security expression。表达式评估将委派给在应用程序上下文中定义的SecurityExpressionHandler<FilterInvocation>(您应在<http>命名空间配置中启用 Web 表达式,以确保该服务可用)。因此,例如,您可能有

<sec:authorize access="hasRole('supervisor')">

This content will only be visible to users who have the "supervisor" authority in their list of <tt>GrantedAuthority</tt>s.

</sec:authorize>

当与 Spring Security 的 PermissionEvaluator 结合使用时,该标签还可用于检查权限。例如:

<sec:authorize access="hasPermission(#domain,'read') or hasPermission(#domain,'write')">

This content will only be visible to users who have read or write permission to the Object found as a request attribute named "domain".

</sec:authorize>

通常的要求是,如果实际上允许用户单击某个链接,则仅显示该链接。我们如何预先确定是否允许某事?该标记还可以在替代模式下运行,该模式允许您将特定的 URL 定义为属性。如果允许用户调用该 URL,则将评估标记主体,否则将跳过该标记主体。所以你可能会喜欢

<sec:authorize url="/admin">

This content will only be visible to users who are authorized to send requests to the "/admin" URL.

</sec:authorize>

要使用此标记,您的应用程序上下文中还必须有一个WebInvocationPrivilegeEvaluator的实例。如果使用名称空间,将自动注册一个名称空间。这是DefaultWebInvocationPrivilegeEvaluator的实例,该实例为提供的 URL 创建一个虚拟 Web 请求并调用安全拦截器以查看该请求成功还是失败。这使您可以委派给使用<http>命名空间配置中的intercept-url声明定义的访问控制设置,并且省去了在 JSP 中重复信息(例如所需角色)的麻烦。此方法也可以与提供 HTTP 方法的method属性结合使用,以进行更具体的匹配。

通过将var属性设置为变量名称,可以将评估标记(无论是授予还是拒绝访问)的布尔结果存储在页面上下文范围变量中,而无需在复制代码的其他点重复和重新评估条件。页。

30.2.1 禁用测试的标签授权

在页面上为未经授权的用户隐藏链接不会阻止他们访问 URL。例如,他们可以直接将其键入浏览器中。在测试过程中,您可能希望显示隐藏区域,以检查链接在后端是否 true 固定。如果将系统属性spring.security.disableUISecurity设置为true,则authorize标记仍将运行,但不会隐藏其内容。默认情况下,它还将用<span class="securityHiddenUI">…</span>标签包围内容。这使您可以显示具有特定 CSS 样式(例如不同的背景颜色)的“隐藏”内容。例如,尝试在启用此属性的情况下运行“教程”示例应用程序。

如果要更改默认span标记中的周围文本(或使用空字符串将其完全删除),也可以设置属性spring.security.securedUIPrefixspring.security.securedUISuffix

30.3 身份验证标签

该标签允许访问存储在安全上下文中的当前Authentication对象。它直接在 JSP 中呈现对象的属性。因此,例如,如果Authenticationprincipal属性是 Spring Security 的UserDetails对象的实例,则使用<sec:authentication property="principal.username" />将呈现当前用户的名称。

当然,对于这种事情,不必使用 JSP 标记,并且某些人希望在视图中保持尽可能少的逻辑。您可以访问 MVC 控制器中的Authentication对象(通过调用SecurityContextHolder.getContext().getAuthentication()),并将数据直接添加到模型中以通过视图进行渲染。

30.4 accesscontrollist 标记

该标签仅在与 Spring Security 的 ACL 模块一起使用时才有效。它检查以逗号分隔的指定域对象的所需权限列表。如果当前用户拥有所有这些权限,则将评估标签正文。如果他们不这样做,它将被跳过。一个例子可能是

Warning

通常,应将此标签视为已弃用。而是使用第 30.2 节“授权标签”

<sec:accesscontrollist hasPermission="1,2" domainObject="${someObject}">

This will be shown if the user has all of the permissions represented by the values "1" or "2" on the given object.

</sec:accesscontrollist>

权限被传递到应用程序上下文中定义的PermissionFactory,将它们转换为 ACL Permission实例,因此它们可以是工厂支持的任何格式-它们不必是整数,它们可以是READWRITE之类的字符串。如果未找到PermissionFactory,将使用DefaultPermissionFactory的实例。应用程序上下文中的AclService将用于为所提供的对象加载Acl实例。将使用所需的权限调用Acl,以检查是否所有这些权限都被授予。

该标签还支持var属性,与authorize标签相同。

30.5 csrfInput 标签

如果启用了 CSRF 保护,则此标记将插入一个隐藏的表单字段,其中包含 CSRF 保护令牌的正确名称和值。如果未启用 CSRF 保护,则此标签不输出任何内容。

通常,Spring Security 会为您使用的任何<form:form>标签自动插入一个 CSRF 表单字段,但是如果由于某种原因您不能使用<form:form>,则csrfInput可以方便地替换。

您应该将此标记放置在 HTML <form></form>块中,通常将其放置在其他 Importing 字段中。请勿将此标签放在 Spring <form:form></form:form>块中。 Spring Security 自动处理 Spring 表单。

<form method="post" action="/do/something">
		<sec:csrfInput />
		Name:
<input type="text" name="name" /> ... </form>

30.6 csrfMetaTags 标记

如果启用了 CSRF 保护,则此标记将插入包含 CSRF 保护令牌形式字段,Headers 名称和 CSRF 保护令牌值的元标记。这些元标记对于在应用程序中的 JavaScript 中采用 CSRF 保护很有用。

您应将csrfMetaTags放置在 HTML <head></head>块中,通常将其放置在其他 meta 标签中。使用此标记后,您可以使用 JavaScript 轻松访问表单字段名称,标题名称和令牌值。在此示例中,使用 JQuery 简化了任务。

<!DOCTYPE html>
<html>
	<head>
		<title>CSRF Protected JavaScript Page</title>
		<meta name="description" content="This is the description for this page" />
		<sec:csrfMetaTags />
		<script type="text/javascript" language="javascript">

			var csrfParameter = $("meta[name='_csrf_parameter']").attr("content");
			var csrfHeader = $("meta[name='_csrf_header']").attr("content");
			var csrfToken = $("meta[name='_csrf']").attr("content");

			// using XMLHttpRequest directly to send an x-www-form-urlencoded request
			var ajax = new XMLHttpRequest();
			ajax.open("POST", "http://www.example.org/do/something", true);
			ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded data");
			ajax.send(csrfParameter + "=" + csrfToken + "&name=John&...");

			// using XMLHttpRequest directly to send a non-x-www-form-urlencoded request
			var ajax = new XMLHttpRequest();
			ajax.open("POST", "http://www.example.org/do/something", true);
			ajax.setRequestHeader(csrfHeader, csrfToken);
			ajax.send("...");

			// using JQuery to send an x-www-form-urlencoded request
			var data = {};
			data[csrfParameter] = csrfToken;
			data["name"] = "John";
			...
			$.ajax({
				url: "http://www.example.org/do/something",
				type: "POST",
				data: data,
				...
			});

			// using JQuery to send a non-x-www-form-urlencoded request
			var headers = {};
			headers[csrfHeader] = csrfToken;
			$.ajax({
				url: "http://www.example.org/do/something",
				type: "POST",
				headers: headers,
				...
			});

		<script>
	</head>
	<body>
		...
	</body>
</html>

如果未启用 CSRF 保护,则csrfMetaTags不输出任何内容。


[21] Spring Security 2.0 的旧选项也受支持,但不建议使用。