第八章 使用Spring Web Flow
1. Web框架
2. Spring Web Flow是Spring MVC的扩展,构建于Spring MVC基础之上。它支持基于流程的开发。它将流程的定义与实现流程行为的类和视图分离开来。
3. 在Spring中配置Web Flow
DispatcherServlet
只能在XML中进行配置
(1) 流程执行器(flow executor)
<flow:flow-executor id=””/>
流程执行器负责创建和执行流程,但不负责加载流程定义
(2) 流程注册表(flow registry)
<flow:flow-registry id=”” base-path=”/WEB-INF/flows”>
<flow:flow-location-pattern value=””/>
</flow:flow-registry>
<flow:flow-registry id=””>
<flow:flow-location id=”” path=””/>
</flow:flow-registry>
流程ID
所有流程都是通过其ID来进行引用的
(3) 处理流程请求
① DispatcerServlet一般将请求分发给控制器。
② 在Spring应用上下文中,配置FlowHandlerMapping
③ FlowHandlerMapping仅仅是将流程请求定向到Spring Web Flow上,响应请求的是FlowHandlerAdapter
4. 流程的组件
在Spring Web Flow中,流程有三个主要元素定义:状态、转移和流程数据。
(1) 状态
行为(Action)、决策(Decision)、结束(End)、子流程(Subflow)、视图(View)
① 视图状态
1) <view-state id=””/>
id属性的含义:在流程内标识这个状态;指定了流程到达这个状态时要展现的逻辑视图名
<view-state id=”” view=””/>
② 行为状态
1) <action-state id=””>
<evaluate expressioin=””/>
<transition to=””/>
</action-state>
③ 决策状态
1) 决策状态能够在流程执行时产生两个分支
2) <decision-state id=””>
<if test=””
then=””
else=””/>
</decision-state>
④ 子流程状态
1) <subflow-state id=”” subflow=””>
<input name=”” value=””/>
<transition on=”” to=””/>
</subflow-state>
⑤ 结束状态
1) <end-state id=””/>
(2) 转移:连接了流程中的状态
① <transition on=”” to=””/>
<transition on-exception=”” to=””/>
② 全局转移
<global-transitions>
<transition on=”” to=””/>
</global-transitions>
(3) 流程数据
① 声明变量
1) <var name=”” class=””/>
2) <evaluate result=”” expression=”” />
3) <set name=”” value=”” />
② 定义流程数据的作用域
1) Conversation
2) Flow
3) Request
4) Flash
5) View
5. 组合起来:披萨流程
(1) 定义基本流程
(2) 收集客户信息
① 询问电话号码
② 查找顾客
③ 注册新顾客
④ 检查配送区域
⑤ 存储顾客数据
⑥ 结束流程
(3) 构建订单
(4) 支付
6. 保护Web流程
(1) <view-state id=””>
<secured attributes=”” match=”” />
</view-state>
第九章 保护Web应用
Spring Security:基于Spring AOP和Servlet规范的Filter实现的安全框架
1. Spring Security简介
(1) Spring Security从两个角度解决安全性问题。
① 使用Servlet规范中的Filter保护Web请求并限制URL级别的访问。
② 使用SpringAOP保护方法调用。
(2) Spring Security 3.2分为11个模块:
① ACL
② Aspects
③ CAS Client
④ Configuration
⑤ Core
⑥ Cryptography
⑦ LDAP
⑧ OpenID
⑨ Remoting
⑩ Tag Library
⑪ Web
(3) DelegatingFilterProxy:将工作委托给一个javax.servlet.Filter实现类,作为一个<bean>注册在Spring应用的上下文中。
(4) 编写简单的安全性配置
① @EnableWebSecurity:启用Web安全功能
② WebSecurityConfigurer
③ WebSecurityConfigurerAdapter
④ 如果使用Spring MVC开发,应使用@EnableWebMvcSecurity,启用Spring MVC安全性
⑤ 指定Web安全的细节
WebSecurityConfigurerAdapter
三个configure()方法配置Web安全性:configure(WebSecurity)/configure(HttpSecurity)/configure(AuthenticationManagerBuilder)
2. 选择查询用户详细信息的服务
(1) 内存、关系型数据库、LDAP、自定义的用户存储实现
① 内存
1) inMemoryAuthentication()
2) UserDetailsManagerConfigurer.UserDetailsBuilder
3) 调试和开发
② 数据库表
1) jdbcAuthentication()
2) 重写默认的用户查询功能
a. usersByUsernameQuery
b. authoritiesByUsernameQuery
c. groupAuthoritiesByUsername
3) 使用转码后的密码
a. passwordEncoder()
b. Spring Security的加密模块包含了三个PasswordEncoder接口的实现:
a) BCryptPasswordEncoder
b) NoOpPasswordEncoder
c) StandardPasswordEncoder
4) 数据库中的密码是永远不会解码的
③ LDAP
1) ldapAuthentication()
2) 默认情况下,对于用户和组的基础查询都是空的。搜索会在LDAP层级结构的根开始。
3) 配置密码比对 passwordCompare()
a. 基于LDAP进行认证的默认策略是进行绑定操作,直接通过LDAP服务器认证用户。
b. 比对操作
比对在LADP服务器内完成
c. 默认与用户的LDAP条目中的userPassword属性进行比对。passwordAttribute()
4) 引用远程的LDAP服务器
a. 默认情况下,Spring Security的LDAP认证假设LDAP服务器监听33389端口。contextSource()
④ 配置嵌入式的LDAP服务器
1) root()
2) ldif()
⑤ 配置自定义的用户服务
1) Mongo或Neo4j
2) userDetailsService()
3. 拦截请求
(1) 对每个请求进行细粒度安全控制的关键在于重载configure(HttpSecurity)方法
(2) authorizeRequests()
(3) antMatchers()使用的路径可能会包括Ant风格的通配符;regexMatchers()能够接受正则表达式来定义请求路径
(4) authenticated()要求在执行该请求时,必须已经登陆了应用
(5) permitAll()允许请求没有任何的安全限制
(6) 将最为具体的请求路径放在前面
(7) 使用SpEL进行安全保护
① access()
.antMatchers(“/spitter/me”).access(“hasRole(‘ROLE_SPITTER’) and hasIpAddress(‘’)”)
(8) 强制通道的安全性
① requiresChannel():为各种URL模式声明所要求的通道
② requiresInsecure():声明为始终通过HTTP传送
③ requiresSecure():自动将请求重定向到HTTPS
(9) 防止跨站请求伪造 CSRF
① 从Spring 3.2开始,默认启用CSRF防护
② Spring Security通过一个同步token的方式来实现CSRF防护的功能
③ Thymeleaf
<form method=”POST” th:action=”@{/spittles}”>
</form>
④ 如果使用Spring的表单绑定标签的话,<sf:form>标签会自动添加隐藏的CSRF token标签
⑤ JSP
<input type=”hidden”
name=”${_csrf.parameterName}”
value=”${_csrf.token}”/>
4. 认证用户
(1) formLogin():启用默认的登录页
(2) 添加自定义的登录页
Thymeleaf模板会将隐藏的“_csrf”域自动添加到表单。
(3) 启用HTTP Basic认证
① HTTP Basic认证会直接通过HTTP请求本身,对要访问应用程序的用户进行认证。向用户弹出一个简单的静态对话框。
② @Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage()
.and()
.httpBasic()
.realmName()
.and()
...
}
(4) 启用Remember-me功能
http.rememberMe().tokenValiditySeconds().key()
默认情况下,通过在cookie中存储一个token完成。这个token最多两周内有效。
<label for=”remember_me” class=”inline”>Remember me</label>
(5) 退出
① 默认情况下,退出功能是通过Servlet容器中的Filter实现的
② <a th:href=”@{/logout}”>Logout</a>
③ LogoutFilter
④ 退出完成后,用户浏览器重定向到/login?logout
⑤ http.logout().logoutSuccessUrl(“/”)
5. 保护视图
(1) 使用Spring Security的JSP标签库
<%@ taglib prefix=”security” uri=”http://www.springframework.org/security/tags” %>
<security:accesscontrollist>
<security:authentication>
<security:authorize>
① 访问认证信息的细节
1) <security:authentication property=”principal.username” var=”” scope=”request” />
变量默认定义在页面作用域内
② 条件性的渲染内容
1) <security:authorize />
<security:authorize access=”hasRole(“ROLE_SPITTER”)”>
<form>
</form>
</security:authorize>
2) 无法禁止手动输入URL
3) .antMatchers(“/admin”).access(“isAuthenticated() and principal.username=”humba”)
4) url属性对一个给定的URL模式会间接引用其安全性约束
<security:authorize url=”/admin”>
<spring:url value=”/admin” var=”admin_url” />
<br><a href=”${admin_url}”>Admin</a>
<security:authorize>
(2) 使用Thymeleaf的Spring Security方言
① templateEngine.addDialect(new SpringSecurityDialect())
② sec:authorize-url
<span sec:authorize-url=”/admin”>
<br><a href=”@{/admin}”>Admin</a>
</span>
③ <html xmlns=””
xmlns:th
xmlns:sec=””>
<div sec:authorize=”isAuthenticated()”>
Hello <span sec:authentication=”name”>someone</span>
</div>
<html>