spring security (一) 建立spring mvc工程
Spring Security简介
Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性解决方案,它能够在Web请求级别和方法调用级别处理身份认证和授权。因为基于Spring框架,所以Spring Security充分利用了依赖注入(dependency injection,DI)和面向切面的技术。
最初,Spring Security被称为Acegi Security。Acegi是一个强大的安全框架,但是它存在一个严重的问题:那就是需要大量的XML配置。我不会向你介绍这种复杂配置的细节。总之一句话,典型的Acegi配置有几百行XML是很常见的。
理解Spring Security的模块
不管你想使用Spring Security保护哪种类型的应用程序,第一件需要做的事就是将Spring Security模块添加到应用程序的类路径下。Spring Security 3.2分为11个模块
应用程序的类路径下至少要包含Core和Configuration这两个模块。Spring Security经常被用于保护Web应用,所以我们还需要添加Web模块。同时我们还会用到Spring Security的JSP标签库,所以我们需要将这个模块也添加进来。
过滤Web请求
Spring Security借助一系列Servlet Filter来提供各种安全性功能。你可能会想,这是否意味着我们需要在web.xml或WebApplicationInitializer中配置多个Filter呢?实际上,借助于Spring的小技巧,我们只需配置一个Filter就可以了。 DelegatingFilterProxy是一个特殊的Servlet Filter,它本身所做的工作并不多。只是将工作委托给一个javax.servlet.Filter实现类,这个实现类作为一个<bean>注册在Spring应用的上下文中
如果你希望借助WebApplicationInitializer以Java的方式来配置Delegating-FilterProxy的话,那么我们所需要做的就是创建一个扩展的新类,在这里使用javaconfig创建类:SecurityWebInitializer.java
配置如下:
/**
* 使用war注册springSecurityFilter
*/
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
}
只需要继承AbstractSecurityWebApplicationInitializer,AbstractSecurityWebApplicationInitializer实现了WebApplication-Initializer,因此Spring会发现它,并用它在Web容器中注册DelegatingFilterProxy。尽管我们可以重载它的appendFilters()或insertFilters()方法来注册自己选择的Filter,但是要注册DelegatingFilterProxy的话,我们并不需要重载任何方法。
不管我们通过web.xml还是通过AbstractSecurityWebApplicationInitializer的子类来配置DelegatingFilterProxy,它都会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain bean。springSecurityFilterChain本身是另一个特殊的Filter,它也被称为FilterChainProxy。它可以链接任意一个或多个其他的Filter。Spring Security依赖一系列Servlet Filter来提供不同的安全特性。但是,你几乎不需要知道这些细节,因为你不需要显式声明springSecurityFilterChain以及它所链接在一起的其他Filter。当我们启用Web安全性的时候,会自动创建这些Filter。
编写简单的安全性配置
在Spring Security的早期版本中(在其还被称为Acegi Security之时),为了在Web应用中启用简单的安全功能,我们需要编写上百行的XML配置。Spring Security 2.0提供了安全性相关的XML配置命名空间,让情况有了一些好转。
Spring 3.2引入了新的Java配置方案,完全不再需要通过XML来配置安全性功能了。如下的程序清单展现了Spring Security最简单的Java配置。
这里使用spring 的java配置:
@Configuration
@EnableWebSecurity //启用web安全性
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
顾名思义,@EnableWebSecurity注解将会启用Web安全功能。但它本身并没有什么用处,Spring Security必须配置在一个实现了WebSecurityConfigurer的bean中,或者(简单起见)扩展WebSecurityConfigurerAdapter。在Spring应用上下文中,任何实现了WebSecurityConfigurer的bean都可以用来配置SpringSecurity,但是最为简单的方式还是像程序清单那样扩展WebSecurityConfigurer Adapter类。
@EnableWebSecurity可以启用任意Web应用的安全性功能,不过,如果你的应用碰巧是使用Spring MVC开发的,那么就应该考虑使用@EnableWebMvcSecurity替代它。
看起来似乎并没有做太多的事情,但程序清单中的配置类会给应用产生很大的影响。其中任何一种配置都会将应用严格锁定,导致没有人能够进入该系统了!
尽管不是严格要求的,但我们可能希望指定Web安全的细节,这要通过重载WebSecurityConfigurerAdapter中的一个或多个方法来实现。我们可以通过重载WebSecurityConfigurerAdapter的三个configure()方法来配置Web安全性,这个过程中会使用传递进来的参数设置行为。表描述了这三个方法。
让我们重新看一下程序清单,可以看到它没有重写上述三个configure()方法中的任何一个,这就说明了为什么应用现在是被锁定的。尽管对于我们的需求来讲默认的Filter链是不错的,但是默认的configure(HttpSecurity)实际上等同于如下所示:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().and().httpBasic();
}
这个简单的默认配置指定了该如何保护HTTP请求,以及客户端认证用户的方案。通过调用authorizeRequests()和anyRequest().authenticated()就会要求所有进入应用的HTTP请求都要进行认证。它也配置Spring Security支持基于表单的登
录以及HTTP Basic方式的认证。
同时,因为我们没有重载configure(AuthenticationManagerBuilder)方法,所以没有用户存储支撑认证过程。没有用户存储,实际上就等于没有用户。所以,在这里所有的请求都需要认证,但是没有人能够登录成功。
为了让Spring Security满足我们应用的需求,还需要再添加一点配置。具体来讲,我们需要:
1.配置用户存储;
2.指定哪些请求需要认证,哪些请求不需要认证,以及所需要的权限
3.提供一个自定义的登录页面,替代原来简单的默认登录页。
除了Spring Security的这些功能,我们可能还希望基于安全限制,有选择性地在Web视图上显示特定的内容。但首先,我们看一下如何在认证的过程中配置访问用户数据的服务。
使用基于内存的用户存储
因为我们的安全配置类扩展了
WebSecurityConfigurerAdapter,因此配置用户存储的最简单方式就是重载configure()方法,并以AuthenticationManagerBuilder作为传入参数。AuthenticationManagerBuilder有多个方法可以用来配置Spring Security对认证的支持。通过inMemoryAuthentication()方法,我们可以启用、配置并任意填充基于内存的用户存储。
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception{
auth.inMemoryAuthentication().withUser("user")//启用内存用户存储.password("password").roles("USER").and()
.withUser("admin").password("password") .roles("USER","ADMIN");
}
我们可以看到,configure()方法中的AuthenticationManagerBuilder使用构造者风格的接口来构建认证配置。通过简单地调用inMemoryAuthentication()就能启用内存用户存储。但是我们还需要有一些用户,否则的话,这和没有用户并没有什么区别。
因此,我们需要调用withUser()方法为内存用户存储添加新的用户,这个方法的参数是username。withUser()方法返回的是UserDetailsManagerConfigurer.UserDetailsBuilder,这个对象提供了多个进一步配置用户的方法,包括设置用户密码的password()方法以及为给定用户授予一个或多个角色权限的roles()方法。
除了password()、roles()和and()方法以外,还有其他的几个方法可以用来配置内存用户存储中的用户信息。表描述了UserDetailsManagerConfigurer.UserDetailsBuilder对象所有可用的方法。
需要注意的是,roles()方法是authorities()方法的简写形式。roles()方法所给定的值都会添加一个“ROLE_”前缀,并将其作为权限授予给用户。实际上,如下的用户配置与程序清单是等价的:
下面使用idea把上面所讲的内容实现起来:
本文使用spring 版本是4.3.3,spring security版本是4.2.3,ide是idea2016,数据库是sqlserver2008。
在idea中使用mave建立建立web工程。建好后的工程目录如下:
这个就能使用tomcat运行了,运行结果只有一个hello world的页面,如下图:
配置DispatcherServlet
DispatcherServlet是Spring MVC的核心。在这里请求会第一次接触到框架,它要负责将请求路由到其他的组件之中。按照传统的方式,像DispatcherServlet这样的Servlet会配置在web.xml文件中,这个文件会放到应用的WAR包里面。当然,这是配置DispatcherServlet的方法之一。但是,借助于Servlet 3规范和Spring 3.1的功能增强,这种方式已经不是唯一的方案了。
我们会使用Java将DispatcherServlet配置在Servlet容器中,而不会再使用web.xml文件。如下的程序清单展示了所需的Java类。
在config 目录下创建WebInitializer.java类继承AbstractAnnotationConfigDispatcherServletInitializer类
/** * Created by IBM on 2018/5/14. */ public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } // @Override protected String[] getServletMappings() { return new String[] { "/" }; } }
WebConfig.java类 启用webmvc,配置jsp视图解析器来指定视图所在的位置,配置静态资源的处理
[email protected] @EnableWebMvc//启用SpringMVC @ComponentScan("com.web.controller") public class WebConfig extends WebMvcConfigurerAdapter { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver();//配置jsp视图解析器 resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); // resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); return resolver; } //配置静态资源的处理 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
RootConfig.java类指定扫描的包和数据源类
@Configuration @Import(DataConfig.class) @ComponentScan(basePackages={"com"}, excludeFilters={ // @Filter(type= FilterType.CUSTOM, value=WebPackage.class) @Filter(type= FilterType.ANNOTATION, value=EnableWebMvc.class) }) public class RootConfig { /* public static class WebPackage extends RegexPatternTypeFilter { public WebPackage() { super(Pattern.compile("com\\.web")); } }*/ }
DataConfig.java类配置数据源,这里配置了两个但是使用的是datasource(),只要在sqlserver2008中建立一个数据库:db_SpringSecurity就不会报错。
@Configuration public class DataConfig { @Bean public DataSource dataSource1(){ BasicDataSource ds=new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306"); ds.setUsername("root"); ds.setPassword("root"); return ds; } @Bean public DataSource dataSource(){ BasicDataSource ds=new BasicDataSource(); ds.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); ds.setUrl("jdbc:sqlserver://localhost:1433;DatabaseName=db_SpringSecurity;SelectMethod=Cursor"); ds.setUsername("sa"); ds.setPassword("sa123"); return ds; } @Bean public JdbcOperations jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } }
建立控制器HomeController.java和视图home.java
@Configuration @Controller//声明为一个控制器 @RequestMapping({"/home","/"}) @Component public class HomeController { @RequestMapping(method = GET)//处理对“/”的GET请求。请求这个视图:http://localhost:8080/home public String home(){ return "home";//视图名为home } }
这样spring mvc的配置就完成了。
运行结果和工程目录如下:
下一篇配置spring security