转载

寻根究底 Spring Security - 概述与快速启动

Spring Security 为基于 Java-EE 的企业级应用提供了综合的安全管理功能。当前有很多方案来解决服务器级别的安全访问机制,但是当部署环境一改变,就有需要花费大量的时间来解决应用的安全问题。 Spring Security 很好地提供了 WAR&EAR 级别的应用安全问题。

应用的安全主要包含两个核心概念 authenticationauthorization ,即认证和授权。通过认证识别身份,通过授权获取能够访问的资源的权限。

在应用级别 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 请求
  • 授权防范的调用
  • 授权访问个人的领域对象

模块划分

Core

spring-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 servicesURL-based 访问控制时需要使用。主要的包为 org.springframework.security.web

Config

包含 spring security namespace 的解析代码和 Java Configuration Code 的相关接口。当使用XML和Java 注解的方式配置应用时需要用到。主要的包为 org.springframework.security.config

LDAP

LDAP authentication and provisioning code。核心的包为 org.springframework.security.ldap

ACL

专业的领域对象ACL 实现。用来为领域对象的访问提供安全机制。核心包为 org.springframework.security.acls

CAS

CAS 客户端的集成工具。当需要集成几个基于CAS的单点登录系统时需要使用。核心包为 org.springframework.security.cas

OpenID

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

原文  https://zhangzhaoyu.github.io/2016/12/23/spring-security-2016-12-23-spring-security-01/
正文到此结束
Loading...