Spring实战第4版 第2部分 Web中的Spring(二)
读前须知: 本篇章内容取自《Git 学习指南》 P225~P283,如需更多详细内容请购买正版书籍
上一章节: 【书籍篇】Spring实战第4版 第2部分 Web中的Spring(一)
下一章节:
八. 使用Spring Web Flow
8.1 在Spring中配置Web Flow(xml配置)
8.1.1 装配流程执行器
<flow:flow-executor id="flowExecutor" />
8.1.2 配置流程注册表
<flow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows">
<flow:flow-location-pattern value="*-flow.xml" />
</flow:flow-registry>
<flow:flow-registry id="flowRegistry">
<flow:flow-location value="/WEB-INF/flows/springpizza.xml" />
</flow:flow-registry>
<flow:flow-registry id="flowRegistry">
<flow:flow-location id="pizza" value="/WEB-INF/flows/springpizza.xml" />
</flow:flow-registry>
8.1.3 处理流程请求
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<propertry name="flowRegistry" ref="flowRegistry" />
</bean>
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
<propertry name="flowExecutor" ref="flowExecutor" />
</bean>
8.2 流程组件
8.2.1 状态
类型 | 作用 |
---|
行为(Action) | 流程逻辑发生的地方 |
决策(Decision) | 将流程基于流程数据确定分成两个方向 |
结束(End) | 流程最后一站,一旦进入流程终止 |
子流程(Subflow) | 会在当前正在运行大队流程上下文中启动一个新的流程 |
视图(View) | 暂停流程并邀请用户参与流程 |
8.2.1.1 行为状态
<action-state id="saveOrder">
<evaluate expression="pizzaFlowActions.saveOrder(order)" />
<transition to="thankYou" />
</action-state>
8.2.1.2 决策状态
<decision-state id="checkDeiveryArea">
<if test="pizzaFlowAtions.checkDeliveryArea(customer.zipCode)"
then="addCustomer"
else="deliveryWarning" />
</decision-state>
8.2.1.3 结束状态
<end-state id="customerReady" />
8.2.1.4 子流程状态
<subflow-state id="order" subflow="pizza/order">
<input name="order" value="order" />
<transition on="orderCreated" to="payment" />
</subflow-state>
8.2.1.5 视图状态
<view-state id = "welcome" />
<view-state id = "welcome" view="greeting"/>
<view-state id = "welcome" modedl="flowScope.paymentDetails"/>
8.2.2 转移
<transition to="customerReady" />
<transition on="phoneEntered" to="customerReady" />
<transition on-exception="com.springinaction.pizza.service.CustomerNotFoundException" to="customerReady" />
<global-transitions>
<transition on="cancel" to="endstate" />
</global-transitions>
8.2.3 流程数据
8.2.3.1 声明变量
<var name="customer" class="com.springinaction.pizza.domain.Customer" />
<evaluate result="viewScope.toppingsList" expression="T(com.springinaction.pizza.ddomain.Topping).asList()" />
<set name="flowScope.pizza" value="new com.springinaction.pizza.domain.Pizza()" />
8.2.3.2 作用域
范围 | 作用域和可见性 |
---|
Conversation | 最高层级的的流程开始时创建,最高层级的流程结束时销毁。被最高层级的流程和其所有的子流程所共享 |
Flow | 当流程开始时创建,在流程结束时销毁。只有在创建它的流程中是可见的 |
Request | 当一个请求进入流程时创建,在流程返回时销毁 |
Flash | 当流程开始时创建,在流程结束时销毁。在视图状态渲染后,它会被清除 |
View | 当进入视图状态时创建,当这个状态退出时销毁。只在视图状态内是可见的 |
8.3 流程实例
略。。。详细可从书中p234~248获知
九. 保护Web应用
9.1 Spring Security简介
- 为基于Spring 应用程序提供声明式安全保护的安全性框架
- 提供了完整的安全性解决方案,能够在Web请求级别和方法调用级别处理身份认证和授权
- 因为是基于Spring框架,所有充分利用了依赖注入和面向切面的技术
- 使用Servlet规范中的Filter保护Web请求并限制URL级别访问
- 使用Spring AOP保护方法调用(借助于对象代理和使用通知)能确保只有具备适当权限的用户才能安全访问。
9.1.1 Spring Security模块
模块 | 描述 |
---|
ACL(access control list) | 支持通过访问控制列表(ACL)为域对象提供安全性 |
切面(Aspects) | 很小的模块,使用Spring Security注解时,会使用基于AspectJ的切面,而不是使用标准Spring Aop |
CAS客户端(CAS Client) | 提供于Jasig的中心认证服务(Central Authentication Service)进行集成的功能 |
配置(Configuration) | 包含通过XML和Java配置Spring Security的功能支持 |
核心(Core) | 提供Spring Security基本库 |
加密(Cryptograpy) | 提供了加密和密码编码的功能 |
LDAP | 支持基于LDAP进行认证 |
OpenID | 支持使用OpenID进行集中式认证 |
Remoting | 提供了对Spring Remoting的支持 |
标签库(Tag Libaray) | Spring Security的JSP标签库 |
Web | 提供了Spring Security基于Filter的Web安全性支持 |
9.1.2 过滤Web请求
- Spring Security借助一系列Servlet Filter来提供各种安全性功能,借助Spring的小技巧,只需要配置一个Filter即可。
- DelegatingFilterProxy是一个特殊的Servlet Filter,本身工作并不多,只是将工作委托给一个javax.servlet.Filter实现类
- 该实现类作为一个bean注册在Spring应用的上下文中
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
}
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
9.1.3 启用安全性功能最简单配置
- 不管是java还是xml配置DelegatingFilterProxy,它都会拦截应用中的请求,并将请求委托给ID为SpringSecurityFilterChain
- 其本身是另一个特殊的Filter,被称为FilterChainFilter。可以链接任意一个或多个其他的Filter。
- Spring Security依赖一系列Servlet Filter提供不同的安全特性,启用Web安全性的时候就自动创建这些Filter
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
方法 | 描述 |
---|
configure(WebSecurity) | 通过重载,配置Spring Security的Filter链 |
configure(HttpSecurity) | 通过重载,配置如何通过拦截器保护请求 |
configure(AuthenticationManagerBuilder) | 通过重载,配置user-detail服务 |
9.2 选择查询用户详细信息的服务
9.2.1 基于内存的用户存储
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
}
9.2.2 基于数据库表进行认证
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Authwired
DataSource dataSource
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password from User Where username = ?")
.passwordEncoder(new StandardPasswordEncoder("password"));
}
}
public interface PasswordEncode {
String encode(CharSequence rawPassword);
boolean mathces(CharSequence rawPassword, CharSequence encodePassword);
}
9.2.3 基于LDAP进行认证
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.userSearchBase("ou=people")
.userSearchFilter("(uid={0})")
.groupSearchBase("ou=groups")
.groupSearchFilter("member={0}")
.passwordCompare()
.passwordEncoder(new Md5PasswordEncoder())
.passwordAttribute("passcode")
.contextSource()
.url("ladp://habuma.com:389/dc=habuma,dc=com")
.root("dc=habuma,de=com")
.ldif("classpath:users.ldif");
}
}
9.2.4 配置自定义的用户服务
public interface UerDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
public class UserService implements UerDetailsService {
@Autowired
private SpitterDao spitterDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Spitter spitter = userDao.findByUsername(username);
if (spitter == null) {
return throw new UsernameNotFoundException("message");
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER"));
return new User(spitter.getUsername(), spitter.getPassword(), authorities);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserService());
}
}
9.3 拦截请求
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequest()
.antMathcers("/spitters/me").authenticated()
.antMathcers(HttpMethod.POST, "/spitters").authenticated()
.antMathcers(HttpMethod.POST, "/spitters").hasRole("USER")
.antMathcers("/spitters/me").access("hashRole('ROLE_USER')")
.requiresChannel()
.antMathcers("/spitters/form").requiresSecure()
.antMathcers("/").requiresInsecure()
.anyRequst().permitAll()
}
}
9.4 防止跨站请求伪造
<form method="POST" th:action="@{/spitters}">
...
</form>
<input type="hidden" name="${_csrf.param}" value="${_csrf.token}" />
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
9.5 认证用户
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.httpBasic()
.rememberMe()
.tokenValiditySeconds(2419200)
.key("spitterKey")
.logout()
.logoutSuccessUrl("/")
}
}
9.6 保护视图
略。。。详细可从书中p278~283获知