【书籍篇】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 配置流程注册表

<!-- 加载流程定义并让流程执行器能够使用它们 -->
<!-- 流程注册表会在"/WEB-INF/flows"目录下查找流程定义,任何文件名以"*-flow.xml"结尾的xml文件都视为流程 -->
<flow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows">
    <flow:flow-location-pattern value="*-flow.xml" />
</flow:flow-registry>
<!-- 直接指明文件 springpizza作为流程id -->
<flow:flow-registry id="flowRegistry">
    <flow:flow-location value="/WEB-INF/flows/springpizza.xml" />
</flow:flow-registry>

<!-- 直接指明文件和id -->
<flow:flow-registry id="flowRegistry">
    <flow:flow-location id="pizza" value="/WEB-INF/flows/springpizza.xml" />
</flow:flow-registry>

8.1.3 处理流程请求

<!-- 在Spring应用上下文中配置一个FlowHandlerMapping来帮助前端控制器将流程请求发送给Spring Web Flow  -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
	<propertry name="flowRegistry" ref="flowRegistry" />
</bean>

<!-- 响应请求的是FlowHandlerAdapter,等同于Spring MVC的控制器对请求进行处理 -->
<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 行为状态
<!-- 应用程序执行任务时一般会出发Spring所管理bean的一些方法,并根据方法的执行结果转移到另一个状态 -->
<action-state id="saveOrder">
	<evaluate expression="pizzaFlowActions.saveOrder(order)" />
    <transition to="thankYou" />
</action-state>
8.2.1.2 决策状态
<!-- 评估一个boolean类型的表达式,然后再两个状态转移中选择一个 -->
<decision-state id="checkDeiveryArea">
	<if test="pizzaFlowAtions.checkDeliveryArea(customer.zipCode)"
        then="addCustomer"
        else="deliveryWarning" />
</decision-state>
8.2.1.3 结束状态
<!-- 结束之后发生什么取决于三个因素。。。详细可从书中p231获知 -->
<end-state id="customerReady" />
8.2.1.4 子流程状态
<!-- 如果子流程结束状态id为orderCreated,那么流程会转移到payment -->
<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" />
<!-- 触发了phoneEntered就会进入customerReady状态 -->
<transition on="phoneEntered" to="customerReady" />
<!-- 触发了异常就会进入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" />
<!-- 计算一个表达式并且放到toppingsList,该变量是一个视图作用域变量 -->
<evaluate result="viewScope.toppingsList" expression="T(com.springinaction.pizza.ddomain.Topping).asList()" />
<!-- 将变量设置为表达式计算的结果,类似evaluate标签 -->
<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简介

  1. 为基于Spring 应用程序提供声明式安全保护的安全性框架
  2. 提供了完整的安全性解决方案,能够在Web请求级别和方法调用级别处理身份认证和授权
  3. 因为是基于Spring框架,所有充分利用了依赖注入和面向切面的技术
  4. 使用Servlet规范中的Filter保护Web请求并限制URL级别访问
  5. 使用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请求

  1. Spring Security借助一系列Servlet Filter来提供各种安全性功能,借助Spring的小技巧,只需要配置一个Filter即可。
  2. DelegatingFilterProxy是一个特殊的Servlet Filter,本身工作并不多,只是将工作委托给一个javax.servlet.Filter实现类
  3. 该实现类作为一个bean注册在Spring应用的上下文中
// AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer并在Web容器中注册DelegatingFilterProxy
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
    
}
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

9.1.3 启用安全性功能最简单配置

  1. 不管是java还是xml配置DelegatingFilterProxy,它都会拦截应用中的请求,并将请求委托给ID为SpringSecurityFilterChain
  2. 其本身是另一个特殊的Filter,被称为FilterChainFilter。可以链接任意一个或多个其他的Filter。
  3. Spring Security依赖一系列Servlet Filter提供不同的安全特性,启用Web安全性的时候就自动创建这些Filter
@Configuration
@EnableWebSecurity	// 启用Web安全性
// @EnableWebMvcSecurity 启用Spring MVC安全性
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 可以通过重载三个Configure()方法来配置Web安全性
}
方法描述
configure(WebSecurity)通过重载,配置Spring Security的Filter链
configure(HttpSecurity)通过重载,配置如何通过拦截器保护请求
configure(AuthenticationManagerBuilder)通过重载,配置user-detail服务

9.2 选择查询用户详细信息的服务

9.2.1 基于内存的用户存储

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    // roles() 会给参数补充一个“ROLE_”前缀 或可以用.authorities("ROLE_USER")
    // 其他方法详细可从书中p259获知
    @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"));
    }
}

// 自定义转码只需要实现PasswordEncode接口
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")		// 用户从名为people的组织单元下查找,没有调用为从根开始
            .userSearchFilter("(uid={0})")		
            .groupSearchBase("ou=groups")		// 组从名为groups的组织单元下查找,没有调用为从根开始
            .groupSearchFilter("member={0}")
            .passwordCompare()					// 配置密码对比
            .passwordEncoder(new Md5PasswordEncoder())	// MD5加密
            .passwordAttribute("passcode")		// 对比passcode属性
            .contextSource()
            .url("ladp://habuma.com:389/dc=habuma,dc=com")	// 引用远程LDAP服务器
            .root("dc=habuma,de=com")			// 配置嵌入式LDAP服务器
            .ldif("classpath:users.ldif");		// LDAP服务器启动时会寻找该文件加载数据
    }
}

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 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);
	}
}

// 通过userDetailsService()方法将其设置到安全配置中
@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 {
    
    // antMathcers()支持通配符和多个请求路径
    // regexMatchers()接受正则表达式
    // hasRole() 会给参数补充一个“ROLE_”前缀 或可以用.hasAuthority("ROLE_USER")
    // 其他方法详细可从书中p268~269获知
    // 其他Spring表达式可从p270获知
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequest()
            .antMathcers("/spitters/me").authenticated()			// 请求"/spitters/me"需要进行认证
            .antMathcers(HttpMethod.POST, "/spitters").authenticated()	// 请求"/spitters"路径 post请求需要进行认证
            .antMathcers(HttpMethod.POST, "/spitters").hasRole("USER") // 不仅需要认证还需要USER权限
            .antMathcers("/spitters/me").access("hashRole('ROLE_USER')") // 使用Spring表达式实现
            	.requiresChannel()								 // 强制使用HTTPS
            	.antMathcers("/spitters/form").requiresSecure()		// 需要通过HTTPS传送
            	.antMathcers("/").requiresInsecure()			   //不需要通过HTTPS传送
            .anyRequst().permitAll()// 其他请求无需认证
    }
}

9.4 防止跨站请求伪造


<!-- 使用Thymeleaf作为模板的话,只要form标签的action属性添加了Thymeleaf的命名空间前缀,就会自动生成"_csrf" -->
<form method="POST" th:action="@{/spitters}">
    ...
</form>

<!-- jsp -->
<input type="hidden" name="${_csrf.param}" value="${_csrf.token}" />
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    // 禁用csrf,慎用
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
    }
}

9.5 认证用户

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    // 禁用csrf,慎用
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .formLogin()		// 启用默认的登录页
            .httpBasic()		// 启用HTTP Basic认证
            .rememberMe()		// 启用记住我功能
            .tokenValiditySeconds(2419200)	// 记住四周,默认两周
            .key("spitterKey")	// 私钥
            .logout()			// 退出
            .logoutSuccessUrl("/")	// 退出后返回的路径
            
    }
}

9.6 保护视图

略。。。详细可从书中p278~283获知

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值