Spring Security 为基于 Java-EE 的企业级应用提供了综合的安全管理功能。当前有很多方案来解决服务器级别的安全访问机制,但是当部署环境一改变,就有需要花费大量的时间来解决应用的安全问题。 Spring Security 很好地提供了 WAR&EAR 级别的应用安全问题。
应用的安全主要包含两个核心概念 authentication 和 authorization ,即认证和授权。通过认证识别身份,通过授权获取能够访问的资源的权限。
在应用级别 Spring Security 支持各种级别的授权模型,常见的类型包括:
HTTP BASIC authentication headers HTTP Digest authentication headers HTTP X.509 client certificate exchange Form-based authentication Jasig Central Authentication Service (otherwise known as CAS, which is a popular open source single sign-on system) LDAP (a very common approach to cross-platform authentication needs, especially in large environments) OpenID authentication除上面描述之外,还支持其他众多方案,具体参见 Spring Security Document 。
Spring Security 主要解决以下三个方面的安全问题:
授权Web 请求 授权防范的调用 授权访问个人的领域对象 模块划分 Corespring-security-core.jar 包含核心的认证和访问控制类和接口,远程支持,以及供应用使用的基本的 spring-security 的接口。支持独立的应用程序,远程的客户端,方法层的安全和基于JDBC的用户存储。包含以下核心目录:
org.springframework.security.core org.springframework.security.access org.springframework.security.authentication org.springframework.security.provisioning Remoting提供了与 spring remoting 的集成。核心包为 org.springframework.security.remoting 。
Web包含过滤器(filters)和Web 安全相关的基础代码。当采用 spring security web authentication services 和 URL-based 访问控制时需要使用。主要的包为 org.springframework.security.web 。
Config包含 spring security namespace 的解析代码和 Java Configuration Code 的相关接口。当使用XML和Java 注解的方式配置应用时需要用到。主要的包为 org.springframework.security.config 。
LDAPLDAP authentication and provisioning code。核心的包为 org.springframework.security.ldap 。
ACL专业的领域对象ACL 实现。用来为领域对象的访问提供安全机制。核心包为 org.springframework.security.acls 。
CASCAS 客户端的集成工具。当需要集成几个基于CAS的单点登录系统时需要使用。核心包为 org.springframework.security.cas 。
OpenIDspring-security.openid.jar 提供对OpenID 的支持。核心包为 org.springframework.security.openid 。需要 OpenID4Java 。
启动Spring Security Spring Web 基于注解的启动机制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启动spring Security 需要注册 springSecurityFilterChain ,同时 Spring Security 提供了 WebSecurityConfigurerAdapter 来对Spring Security 进行配置。
非Spring MVC 应用在非基于注解的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); } }通过以上配置可以得到以下功能:
所有URL 的访问都需要认证 基于用户名密码的的认证 允许用户logout X-XSS CSRF Fixation Session Fixation 集成了Servlet API: HttpServletRequest#getRemoteUser() HttpServletRequest.html#getUserPrincipal() HttpServletRequest.html#isUserInRole(java.lang.String) HttpServletRequest.html#login(java.lang.String, java.lang.String) HttpServletRequest.html#logout() …… Spring MVC应用的启动基于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 的启动方式,相信读者会有很大收获。