Spring Security简介
Spring Security 是为基于Spring的应用程序提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性解决方案,它能够在Web请求级别和方法调用级别处理身份认证和授权。因为基于Spring框架,所以Spring Security充分利用了依赖注入DI和面向切面AOP的技术。
理解Spring Security的模块
应用程序的类路径下至少要包含Core和Configuration这两个模块。Spring Security经常被用于保护Web应用。同时我们可能也会用到Spring Security的JSP标签库,所以我们也要讲Tag Library添加进来。
过滤Web请求
Spring Security借助一系列Servlet Filter来提供各种安全性功能。你可能会想,这是否意味着我们需要在web.xml或者WebApplicationInitializer中配置多个Filter呢?实际上,我们只需要配置一个Filter就行。
DelegatingFilterProxy是一个特殊的Servlet Filter,它本身所做的工作并不多。只是将工作委托给一个javax.servlet.Filter实现类。
我们这里用java的方式来配置DelegatingFilterProxy,我们所需要做的就是创建一个扩展的新类:(再次更新pom.xml)
package spittr.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
}
AbstractSecurityWebApplicationInitializer也是实现了WebApplicationInitializer,因此Spring会发现它,并用它在Web容器中注册DelegatingFilterProxy。
这个代理会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain bean。
springSecurityFilterChain本身是另一个特殊的Filter,它也被称为FilterChainProxy。它可以链接一个或多个Filter。
编写简单的安全性配置
package spittr.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
@EnableWebSecurity
注解将会启用Web安全功能,但他本身并没有什么用处,Spring Security必须配置在一个实现了WebSecurityConfigurerAdapter里面才会起作用。
@EnableWebSecurity
可以启动任意Web应用的安全性功能,不过如果你的应用碰巧是使用SpringMVC开发的,那么就应该考虑使用@EnableWebMvcSecurity替代它。(貌似已经弃用!)
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
尽管不是严格的,但是我们可能希望指定Web安全的细节,这样重载WebSecurityConfigurerAdapter 中的一个或多个方法实现:
我们现在默认的configure()方法是这样的:
protected void configure(HttpSecurity http)throws Exception{
http
.authorizeRequests()
.anyRequest().anthenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
这个默认配置制定了该如何保护HTTP请求,以及客户端认证用户的方案,通过调用authorizeRequests()和anyRequest().anthenticated()就会要求所有进入应用的HTTP请求都要进行认证。它也配置支持基于表单的登录formLogin()以及HTTP Basic方式的认证httpBasic()
为了让Spring Security 满足我们应用的需求,还需要再添加一点配置。具体来讲,我们需要:
- 配置用户存储
- 指定哪些请求需要认证,哪些不需要,以及所需要的权限
- 提供一个自定义的登录页面,替代原来简单的默认登录页
选择查询用户详细信息的服务
这个标题有点难以理解,我们这样说:有一家饭店,我们会提前预定。但当我们去的时候,告诉服务员我们的名字,但遗憾的是,没有我们的预定记录!
服务员说,没有预定名单,事实上根本不存在这个预定名单。
这就是我们应用程序的现状。我们没办法进入应用,因为安全名单不存在。因为缺少用户存储。
好消息是,我们借助Spring Security的java配置,很容易配置一个或多个数据存储方案。
使用基于内存的用户存储
重载configure方法,并使用两个用户来配置内存用户存储。
@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");
}
}
AuthenticationManagerBuilder使用构造者风格的结构构建认证配置,通过简单调用.inMemoryAuthentication()
来启用内存用户存储。
我们添加了两个用户“user”和“admin”,密码均为“password”。“user”具有USER角色,而“admin”具有USER和ADMIN两个角色。and将多个用户的配置连接起来。
基于数据库表进行认证
用户数据常常会存储在数据库中,并通过JDBC进行访问,为了配置Spring Security使用以JDBC为支撑的用户存储,我们可以使用jdbcAuthentication()
方法:
@Autowired
DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth
.jdbcAuthentication()
.dataSource(dataSource);
}
尽管这些配置能够运行,但是它对我们的数据库模式有要求,它默认运行下面的代码片段:
如果你数据库里有这些表,并且表结构相同,那我没有意见。
但有些时候,你数据库里定义和填充的不满足这些表,那么,我们可以按照如下的方式配置自己的查询:
@Autowired
DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username,password,true from Spitter where username =?"
)
.authoritiesByUsernameQuery(
"select username,'USER' from Spitter where username=?"
);
}
很多时候,我们希望密码保密。所以需要把明文转为密文,我们需要借助passwordEncoder()方法指定一个密码转码器(encoder):
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username,password,true from Spitter where username =?"
)
.authoritiesByUsernameQuery(
"select username,'USER' from Spitter where username=?"
)
.passwordEncoder(new StandardPasswordEncoder("53cr3t"));
上面的代码使用了StandardPasswordEncoder,当然加密方式可以自定义实现,只需要实现PasswordEncoder接口即可。
基于LDAP进行认证
暂时用不到,暂略。
拦截请求
暂略
认证用户
暂略
保护视图
暂略