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
的启动方式,相信读者会有很大收获。