16. 安全流
安全性是任何应用程序的重要概念。 终端用户不应仅通过猜测URL就能访问站点的任何部分。 站点中敏感的区域必须确保只处理经过授权的请求。 Spring Security 是一个经过验证的安全平台,可以在多个层面与您的应用程序集成。 本节重点介绍如何保护流执行的安全。
16.1. 我如何保护一个流程?
保护流程是一个三步过程:
-
配置 Spring Security 的身份验证和授权规则。
-
使用 secured 元素注解流定义,以定义安全规则。
-
将
SecurityFlowExecutionListener添加到处理安全规则的过程中。
必须完成上述每个步骤,否则流安全规则将不会应用。
16.2. secured元素
secured 元素表示其包含的元素应在完全进入之前应用授权检查。 在受保护的流程执行的每个阶段,这种情况可能不会发生超过一次。
可以保护流的三个阶段:流、状态和转换。 在每种情况下,secured 元素的语法是相同的。 secured 元素位于它所保护的元素内部。 例如,要保护某个状态,secured 元素直接出现在该状态的内部,如下所示:
<view-state id="secured-view">
<secured attributes="ROLE_USER" />
...
</view-state>
16.3. SecurityFlowExecutionListener
在流中定义安全规则本身并不能保护流。 您还必须在 webflow 配置中定义一个 SecurityFlowExecutionListener 并将其应用于流执行器,如下所示:
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-listeners>
<webflow:listener ref="securityFlowExecutionListener" />
</webflow:flow-execution-listeners>
</webflow:flow-executor>
<bean id="securityFlowExecutionListener"
class="org.springframework.webflow.security.SecurityFlowExecutionListener" />
如果访问被拒绝到应用程序的某一部分,则会抛出 AccessDeniedException。 此异常随后会被 Spring Security 捕获,并用于提示用户进行身份验证。 重要的是,应允许此异常不受阻碍地向上传播执行堆栈。 否则,最终用户可能不会被提示进行身份验证。
16.3.1. 自定义授权管理器
如果您的应用程序使用了非基于角色的权限,则需要配置一个自定义的 AuthorizaitonManager。 您可以通过安全监听器上的 authorizationManagerInitializer 属性覆盖默认使用的 AuthorityAuthorizationManager。例如:
@Bean
SecurityFlowExecutionListener securityFlowExecutionListener() {
SecurityFlowExecutionListener listener = new SecurityFlowExecutionListener();
listener.setAuthorizationManagerInitializer(securityRule -> {
// ...
});
return listener;
}
16.4. 配置 Spring Security
Spring Security 拥有强大的配置选项。 由于每个应用程序和环境都有其自身的安全需求,Spring Security 参考文档 是了解可用选项的最佳去处。
Both the booking-faces and booking-mvc 示例应用程序都配置为使用 Spring Security。 需要在 Spring 和 web.xml 层级进行配置。
16.4.1. Spring 配置
Spring 配置定义了 http 细节(例如受保护的 URL 和登录/注销机制)以及 authentication-provider。 对于示例应用程序,配置了一个本地身份验证提供程序。 以下示例为 Web 流配置了 Spring Security:
<security:http auto-config="true">
<security:form-login login-page="/spring/login"
login-processing-url="/spring/loginProcess"
default-target-url="/spring/main"
authentication-failure-url="/spring/login?login_error=1" />
<security:logout logout-url="/spring/logout" logout-success-url="/spring/logout-success" />
</security:http>
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:user-service>
<security:user name="keith" password="417c7382b16c395bc25b5da1398cf076"
authorities="ROLE_USER,ROLE_SUPERVISOR" />
<security:user name="erwin" password="12430911a8af075c6f41c6976af22b09"
authorities="ROLE_USER,ROLE_SUPERVISOR" />
<security:user name="jeremy" password="57c6cbff0d421449be820763f03139eb"
authorities="ROLE_USER" />
<security:user name="scott" password="942f2339bf50796de535a384f0d1af3e"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
16.4.2. web.xml配置
在web.xml文件中,定义了一个filter来拦截所有请求。 该过滤器监听登录和注销请求,并相应地处理它们。 它还会捕获AccesDeniedException实例并将用户重定向到登录页面。 以下示例定义了这样的过滤器:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>