Spring Security
为基于 Java-EE
的企业级应用提供了综合的安全管理功能。当前有很多方案来解决服务器级别的安全访问机制,但是当部署环境一改变,就有需要花费大量的时间来解决应用的安全问题。 Spring Security
很好地提供了 WAR&EAR
级别的应用安全问题。
应用的安全主要包含两个核心概念 authentication
和 authorization
,即认证和授权。通过认证识别身份,通过授权获取能够访问的资源的权限。
在应用级别 Spring Security
支持各种级别的授权模型,常见的类型包括:
除上面描述之外,还支持其他众多方案,具体参见 Spring Security Document 。
Spring Security
主要解决以下三个方面的安全问题:
spring-security-core.jar
包含核心的认证和访问控制类和接口,远程支持,以及供应用使用的基本的 spring-security
的接口。支持独立的应用程序,远程的客户端,方法层的安全和基于JDBC的用户存储。包含以下核心目录:
提供了与 spring remoting
的集成。核心包为 org.springframework.security.remoting
。
包含过滤器(filters)和Web 安全相关的基础代码。当采用 spring security web authentication services
和 URL-based
访问控制时需要使用。主要的包为 org.springframework.security.web
。
包含 spring security namespace
的解析代码和 Java Configuration Code
的相关接口。当使用XML和Java 注解的方式配置应用时需要用到。主要的包为 org.springframework.security.config
。
LDAP authentication and provisioning code。核心的包为 org.springframework.security.ldap
。
专业的领域对象ACL 实现。用来为领域对象的访问提供安全机制。核心包为 org.springframework.security.acls
。
CAS 客户端的集成工具。当需要集成几个基于CAS的单点登录系统时需要使用。核心包为 org.springframework.security.cas
。
spring-security.openid.jar
提供对OpenID 的支持。核心包为 org.springframework.security.openid
。需要 OpenID4Java
。
Java-EE
规范为了实现不通过 web.xml
启动Java-EE 项目定义了基于SPI(Service Provider Interface)机制的 javax.servlet.ServletContainerInitializer
接口。通过 SPI 机制
Java-EE
容器启动后,会在 classpath
中寻找上述接口的实现类,并回调该接口提供的方法。
Spring Web
就是采用上述机制来实现Web 相关内容的初始化工作的。 org.springframework.web.SpringServletContainerInitializer
既为 Spring Web
中实现 javax.servlet.Servletcontainerinitializer
接口的类,具体代码如下:
@HandlesTypes(WebApplicationInitializer.class)
public classSpringServletContainerInitializerimplementsServletContainerInitializer{
@Override
publicvoidonStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
从上述代码上,我们可以看到Spring 在启动后调用了 org.springframework.web.WebApplicationInitializer
的实现类的 onStartup()
方法。
启动spring Security 需要注册 springSecurityFilterChain
,同时 Spring Security
提供了 WebSecurityConfigurerAdapter
来对Spring Security 进行配置。
在非基于注解的Spring 应用中不能采用Sevlet 容器的SPI机制来进行启动,因此需要将 WebSecurityConfig.class
传递给 AbstractSecurityWebApplicationInitializer
的构造函数,来启动 Spring Security
。
@EnableWebSecurity
@EnableGlobalMethodSecurity
@EnableGlobalAuthentication
public classWebSecurityConfigextendsWebSecurityConfigurerAdapter{
// do our self http config, we user WebSecurityConfigurerAdapter
@Autowired
publicvoidconfigureGlobal(AuthenticationManagerBuilder auth)throwsException{
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
@Override
protectedvoidconfigure(HttpSecurity http)throwsException{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
}
public classSecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
publicSecurityWebApplicationInitializer(){
super(WebSecurityConfig.class);
}
}
通过以上配置可以得到以下功能:
基于Spring MVC 的应用由SPI 机制启动,因此需要由 Spring MVC
来加载 Spring Security
的配置。因此,注册 springsecurityfilterchain
仅仅需要实现 AbstractSecurityWebApplicationInitializer
即可。
public classSecurityWebApplicationInitializerextendsAbstractSecurityWebApplicationInitializer{
// register springSecurityFilterChain
// with out exist spring mvc, we use AbstractSecurityWebApplicationInitializer
}
Spring MVC 的启动Init 类:
public classWebInitializerextendsAbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class, WebSecurityConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
Spring MVC 基于 JSP
模板的配置:
@Configuration
@EnableWebMvc
@ComponentScan("org.zzy.spring.aop.web")
public classWebConfigextendsWebMvcConfigurerAdapter{
@Bean
publicViewResolverviewResolver(){
InternalResourceViewResolver resourceViewResolver =
new InternalResourceViewResolver();
resourceViewResolver.setPrefix("/WEB-INF/views/");
resourceViewResolver.setSuffix(".jsp");
resourceViewResolver.setViewClass(JstlView.class);
resourceViewResolver.setExposeContextBeansAsAttributes(true);
return resourceViewResolver;
}
@Bean
publicMessageSourcemessageSource(){
/*ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages/messages");*/
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages/messages");
messageSource.setCacheSeconds(10);
return messageSource;
}
@Override
publicvoidconfigureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
Spring Security
为应用级别的安全访问提供了许多开箱即用的功能。同时,用户也能基于自己的业务需求根据 Spring Security
暴露的接口进行个性化的开发。相关内容会在后续的n文章中进行介绍。本文对 Spring MVC
的启动机制进行了详细地址介绍,在此基础上也介绍了 Spring Security
的启动方式,相信读者会有很大收获。