Spring Security简介

Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性解决方案,它能够在Web请求级别和方法调用级别处理身份认证和授权。因为是基于Spring框架,所以Spring Security充分利用了依赖注入和面向切面的技术。

过滤Web请求

Spring Security借助一系列Servlet Filter来提供各种安全性功能。你可能会想,这是否意味着我们需要在web.xml或WebApplicationInitializer中配置多个Filter呢?实际上,借助与Spring的小技巧,我们只需要配置一个Filter就可以了。
DelegatingFilterProxy是一个特殊的Servlet Filter,它本身所做的工作并不多。只是将工作委托给一个javax.servlet.Filter实现类,这个实现类作为一个Bean注册在Spring应用的上下文中。

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer{

}

AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer,因此Spring会发现它,并且它在Web容器中注册DelegatingFilterProxy。不管我们通过web.xml还是通过AbstractSecurityWebApplicationInitializer的子类来配置DelegatingFilterProxy,它都会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain的Bean。springSecurityFilterChain本身是另一个特殊的Filter,但是在这里我们不去讨论它。

编写简单的安全性配置

SecurityConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
....
}

@EnableWebMvcSecurity开启Web安全性功能(SpringMVC开发)
Spring Security必须配置在一个实现了WebSecurityConfigurer的bean中,或者(简单起见)扩展WebSecurityConfigurerAdapter。在Spring应用上下文中,任何实现了WebSecurityConfigurer的bean都可以用来配置Spring Security,但是最简单的方式还是扩展WebSecurityConfigurerAdapter类。
将SecurityConfig配置文件加入Spring应用上下文。

@Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class,SecurityConfig.class };      //指定根配置类,例如:application.xml
    }

效果如图:

spring security 放过接口 spring security application_Web


看起来似乎没有做什么事情,但是如上的简单配置会给应用产生很大的影响。其中任何一种配置都会将应用严格锁定,导致没有人能够进入系统。

那我们怎么指定Web安全的细节呢?

我们可以通过重载WebSecurityConfigurerAdapter的三个configure()方法来配置Web安全性。

重载WebSecurityConfigurerAdapter的configure()方法

  • configure(WebSecurity) 通过重载,配置Spring Security的Filter链
  • configure(HttpSecurity) 通过重载,配置如何通过拦截器保护请求
  • configure(AuthenticationManagerBuilder) 通过重载,配置user-detail服务

可以到看SecurityConfig.java没有重写上述三个configure()方法 的任何一个,这就说明了为什么应用现在是被锁定的。尽管默认的Filter链是不错的,但是默认的 configure(HttpSecurity)实际上等同于如下所示:

@Override   
    protected void configure(HttpSecurity http)throws Exception
    {
     http.authorizeRequests().anyRequest().authenticated()
     .and().formLogin().and().httpBasic();     //formLogin()表单登陆的方式
    }                                          //httpBasic()HTTP Basic的方式

authorizeRequests().anyRequest().authenticated()会要求所有进入应用的HTTP请求都要进行认证。它也配置Spring Security支持基于表单的登陆以及HTTP Basic方式的认证。
同时,因为我们没有重载configure(AuthenticationManagerBuilder)方法,所有没有用户存储支持认证过程。没有用户存储,实际上就等于没有用户。所以,在这里所有的请求都需要认证,但是没有人能够登录成功。
为了让Spring Security满足我们的要求,还需要再添加一点配置。我们需要:

  • 配置用户存储;
  • 指定哪些请求需要认证,哪些请求不需要认证,以及所需要的权限;
  • 提供一个自定义的登录页面,替代原来简单的默认登录页面;

除了这些功能,我们可能还希望基于安全限制,有选择性地在Web视图上显示特定的内容。

使用基于内存的用户存储

配置用户存储最简单的方式就是重载configure()方法,并以AuthenticationManagerBuilder作为传入参数。AuthenticationManagerBuilder有多个方法可以用来配置Spring Security对认证的支持。通过inMemoryAuthentication()方法,我们可以启用,配置并任意填充基于内存的用户存储。

@Override   
    protected void configure(AuthenticationManagerBuilder auth)throws Exception
    {
      auth.inMemoryAuthentication().withUser("user").password("123").authorities("ROLE_USER").and()
      .withUser("admin").password("123").roles("USER","ADMIN").and()
      .withUser("123").password("123").roles("ADMIN");;
    }

withUser()方法为内存用户存储添加新的用户,withUser()方法返回的是UserDetailsManagerConfigurer.UserDetailsBuilder,这个对象提供了多个进一步配置用户的方法,包括设置密码的password()以及设置一个或多个权限的roles()方法。而and()方法可以将多个用户的配置连接起来。

需要注意的是roles()是authorities()的缩写,roles()方法所给的值都会添加一个ROLE_前缀。

UserDetailsManagerConfigurer.UserDetailsBuilder对象所有可用的方法

spring security 放过接口 spring security application_Web_02

基于数据表进行认证

用户数据通常存在关系型数据库中,并通过jdbc访问。为了配置Spring Security使用以JDBC为支撑的用户存储,我们可以使用jdbcAuthentication()方法。

@Autowired
    DataSource dataSource;
@Override   
    protected void configure(AuthenticationManagerBuilder auth)throws Exception
    {
      auth.jdbcAuthentication().dataSource(dataSource) ;
    }

我们必须配置一个DataSource ,在这里,我们配置了基于JDBC驱动的数据源,并且DataSource 是通过自动装配得到的。
RootConfig.java加入如下配置:

@Bean
        public DataSource dataSource() {
           DriverManagerDataSource dataSource = new DriverManagerDataSource();//每个连接请求都会返回一个新建的连接,没有进行池化管理
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");  //需要Spring-tx.jar 与Spring-jdbc.jar
            dataSource.setUrl("jdbc:mysql://localhost:3306/book");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            return dataSource;
        }

尽管默认的最少配置能让一切运行起来,但是它对我们的数据库模式有一些要求。它预期存在某些存储用户数据的表。如果你能够在数据库定义和填充满足所需的表,那么基本上就不需要你再做什么额外的事情了。但我们想自己定义我们的数据库表并且在查询上获取更多的控制权的话可以这样做

@Override   
    protected void configure(AuthenticationManagerBuilder auth)throws Exception
    {
      auth.jdbcAuthentication().dataSource(dataSource)
      .usersByUsernameQuery("select username ,password,true from users where username = ?")
      .authoritiesByUsernameQuery("select username,'user' from users where username = ?")
      ;
    }

我们重写了认证和基本权限的查询语句,我们也可以通过groupAuthoritiesByUsername(query)方法将群组查询写为自定义查询语句。认证查询会选取用户名、密码以及启用状态信息。权限查询会选取零行或多行包含该用户名及其权限信息的数据。群组权限查询会选取零行或多行数据,每行数据都会包含群组ID、群组名称以及权限。

基于LDAP进行认证

目前阶段没有使用过LDAP,所以这里不讨论,后续使用了再补上。

配置自定义的用户服务

目前也没有用到,后续用到再补上。