保护Web应用
9.1 Spring Security简介
Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架。它能够在Web请求级别和方法调用级别处理身份认证和授权。充分利用了依赖注入和面向切面技术。
9.1.1 理解Spring Security模块
Header One | Header Two |
---|---|
ACL | 支持通过访问控制列表(ACL)为域对象提供安全性 |
切面 | 使用基于AspectJ的切面,而不是使用标准的SpringAOP |
CAS客户端 | 提供与Jasig的中心认证服务(CAS)进行集成的功能 |
配置(Configuration) | 包含通过XML和java配置Spring Security的功能支持 |
核心(Core) | 提供Spring Security基本库 |
加密 | 提供了加密和密码编码的功能 |
LDAP | 支持基于LDAP进行认证 |
OpenID | 支持使用OpenID进行集中式认证 |
Remoting | 提供了对Spring Remoting的支持 |
标签库 | Spring Security的JSP标签库 |
Web | 提供了Spring Security基于Filter的Web安全性支持 |
9.1.2 过滤Web请求
Spring Security借助一系列Servlet Filter来提供各种安全性功能。
DeleagatingFilterProxy是一个特殊的Servlet Filter,它本身所做的工作不多。只是将工作委托给一个javax.servlet.Filter实现类。
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DeleagatingFilterProxy
</filter-class>
</filter>
package spitter.config;
import *;
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer{}
如果只是注册DelegatingFilterProxy的话,不需要重载任何方法。
AbstractSecurityWebApplicationInitializer实现了WebApplicaitonInitializer,Spring 会发现它,并用它在Web容器中注册DelegatingFilterProxy。
以上两种方法来配置DelegatingFilterProxy,都会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain bean.
springSecurityFilterChain本身是另一个特殊的Filter,它也被称为FilterChainProxy。它可以链接任意一个或多个其他的Filter
9.1.3 编写简单的安全性配置
为SpringMVC启用Web安全性功能的最简单配置
package spitter.config;
import *;
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
}
Spring Security 必须配置在一个实现了WebSecurityConfigurer的bean中,或者更简单的扩展WebSecurityConfigurerAdapter。
为了指定Web安全的细节,需要通过重载WebSecurityConfigurerAdapter中的方法来实现。
重载WebSecurityConfigurerAdapter中的configure()方法
Header One | Header Two |
---|---|
configure(WebSecurity) | 通过重载,配置Spring Security的Filter链 |
configure(HttpSecurity) | 通过重载,配置如何通过拦截器保护请求 |
configure(AuthenticationManagerBuilder) | 通过重载,配置user-detail服务 |
上述SecurityConfig类的示例中,没有重写任何一个方法,默认的configure(HttpSecurity) 等同于如下效果:
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequest()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
解释一下,这个默认配置指定了该如何保护HTTP请求,以及客户端认证用户的方案。通过调用.authorizeRequest()和.anyRequest().authenticated(),要求所有进入应用的HTTP请求都要进行认证。配置Spring Security 支持基于表单的登录以及HTTP Basic方式的认证。
9.2 选择查询用户详细信息的服务
Spring Security 非常灵活,能够基于各种数据存储来认证用户。它内置了多种常见的用户存储场景,如内存、关系型数据库以及LDAP.我们也可以编写并插入自定义的用户存储实现。最简单的:在内存中维护用户存储。
9.2.1 使用基于内存的用户存储
package spitter.config;
import *;
@Configuration
@EnableWebMvcSecurity
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");
}
}
inMemoryAuthentication()启用内存用户存储,
withUser()创建新用户
roles()赋予角色
roles("USER")
等同于authorities("ROLE_USER")
9.2.2 基于数据库表进行认证
9.2.3 基于LDAP进行认证
9.3 拦截请求
我们可以将任意数量的antMatchers()、regexMatchers()、anyRequesst()连接起来以满足Web应用安全规则的需要。但是,这些规则会按照给定的顺序发挥作用。所以,很重要的一点就是:
将最为具体的请求路径放在前面,而最不具体的路径放在后面。如果不这样做,具体的路径配置会被不具体的覆盖掉。
example:
java
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequest()
.antMatchers("/spitters/me").authenticated()
.antMatchers(HttpMethod.POST,"/spittles").authenticated()
.anyRequest().permitAll();
}
用来定义如何保护路径的配置方法:
方法 | 能够做什么 |
---|---|
access(String) | 如果给定的SpEL表达式计算结果为true,就允许访问 |
anonymous() | 允许匿名用户访问 |
anthenticated() | 允许认证过的用户访问 |
denyAll() | 无条件拒绝所有访问 |
fullyAuthenticated() | 如果用户是完整认证的,就允许访问 |
hasAnyAuthority(String…) | 如果用户具备给定权限中某一个的话,就允许访问 |
hasAnyRole(String…) | 如果用户具备给定角色中某一个的话,就允许访问 |
hasAuthority(String) | 如果用户具备给定权限的话,就允许访问 |
hasIpAddress(String) | 如果请求来自指定IP地址,就允许访问 |
hasRole(String) | 如果用户具备给定角色的话,就允许访问 |
not() | 对其他访问方法的结果求反 |
permitAll() | 无条件允许访问 |
rememberMe(String) | 如果用户是通过Remember-me功能认证的,就允许访问 |
9.3.1 使用Spring表达式进行安全保护
9.3.2 强制通道的安全性
强制使用HTTPS
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequest()
.antMatchers("/spitters/me").authenticated()
.antMatchers(HttpMethod.POST,"/spittles").authenticated()
.anyRequest().permitAll()
.and()
.requiresChannel()
.antMatchers("/spitter/form").requiresSecure();//需要HTTPS
}
9.3.3 防止跨站请求伪造
Spring Security通过一个同步token的方式来实现CSRF防护。它会拦截状态变化的请求并检查CSRF token。如果请求中不包含CSRF token的话,或者token不能与服务器的token相匹配,请求就会失败,抛出CsrfException异常。
这意味着在你的应用中,所有的表单必须在一个”_csrf”域中提交token,而且这个token必须要与服务器端计算的并存储的token相一致。