Spring Security 3.2分为11个模块
模块 | 描述 |
---|---|
ACL | 通过访问控制列表(access control list)为域对象提供安全性 |
切面 | 当使用Spring Security注解时,会使用基于AspectJ的切面,而不是标准的SpringAOP |
配置 | 包含通过Java和XML配置Spring Security功能支持 |
LDAP | 支持基于LDAP进行认证 |
标签库 | Spring Security的JSP标签库 |
Web | 提供了Spring Security基于Filter的Web安全性支持 |
核心 | 提供Spring Security基本库 |
加密 | 提供加密和密码编码的功能 |
OpenID | 支持使用OpenId进行集中认证 |
Remoting | 提供对Spring Remoting的支持 |
CAS客户端 | 提供与Jasig的中心认证服务(central authentication service)进行集成的功能 |
先注册DelegatingFilterProxy:
package spittr.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
//继承这个类来注册DelegatingFilterProxy,方法体为空。
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {}
AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer接口,因此Spring会发现它,并用它在容器中注册DelegatingFilterProxy。DelegatingFilterProxy会拦截发往应用中的请求,将它交给Filter。
启用Web安全性功能配置:
package spittr.config;
@Configuration
@EnableWebSecurity //启用 Web安全性
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
不重载任何方法,应用是锁定状态,没有人能进入系统。**所有的请求都需要认证,但是没有用户存储,请求都会认证失败。**我们可以通过重载configure()方法来配置Web安全性:
方法 | 描述 |
---|---|
configure(WebSecurity) | 配置Spring Security的Filter链 |
configure(HttpSecurity) | 配置如何通过拦截器保护请求 |
configure(AuthenticationManagerBuilder) | 配置user-detail服务 |
要想实现应有的作用,还需要:
通过重载configure(AuthenticationManagerBuilder auth)方法来实现。
这种存储多用于调试和开发测试。调用 auth.inMemoryAuthentication().
注入一个DataSource类型的dataSource,调用auth.jdbcAuthentication().dataSource(dataSource)方法。可以通过该方法下的.usersByUsernamQuery()来自定义查询用户。
其余的查询方法和密码加密详见配置用户存储。
如果需要认证的用户存储在非关系型数据库,或者我们想用自己的方法从数据库中取出用户再进行认证。这种情况下,可以自定义一个实现UserDetailsService接口的类,它以提供一个组件(能插入到SecurityConfig配置类中)。
//UserDetailsService接口代码
public interface UserDetailsService{
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
//UserDetails 是org.springframework.security.core.userdetails下的一个接口。
}
例如,我们要从SpitterRepository中查找具体的Spittle,来验证信息:
package spittr.security;
public class SpitterUserService implements UserDetailsService {
private final SpittleRepository spittleRepository;
public SpitterUserService(SpittleRepository spittleRepository) {//注入自定义的服务
this.spittleRepository = spittleRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
Spittle spittle = spittleRepository.findSpittleByUsername(String username); //调用服务查找该username的用户
if(spittle != null) { //如果该用户存在,就创建权限列表,然后返回
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER"));
return new org.springframework.security.core.userdetails.User(spittle.getUsername(), spittle.getPassword(), authorities);
//返回一个User对象,存储着这个用户的信息和权限
}else {
throw new UsernameNotFoundException("User '" + username + "' not found");
//不存在就抛出异常
}
return null;
}
}
这个查询username对应的用户信息的部件已经写好了,要使它发挥作用,需要将它设置到安全配置类(SecurityConfig)中去:
package spittr.config;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
SpittleRepository spittleRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(new SpitterUserService(spittleRepository));
}
}
userDetailsService()方法(类似于inMemoryAuthentication()和jdbcAuthentication())会配置一个用户存储。
另一种方案是使Spittle直接实现UserDetails接口,这样loadUserByUsername()就能直接返回Spitter对象,而不必通过User对象传递返回。
通过重载configure(HttpSecurity)方法实现对请求进行细粒度安全性控制。
例如,为不同的URL路径有选择的应用安全性:
//在SecurityConfig类中
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests() //配置请求级别的安全性细节
.antMatchers("/spitters/me").authenticated() //指明对"/spitters/me"路径的请求需要认证
.antMatchers(HttpMethod.POST, "/spittles").authenticated() //指明对"/spittles"路径的POST请求必须经过认证
.anyRequest().permitAll(); //其它请求都是允许的,不需要认证和权限
//这些规则会按照给定的顺序发挥作用,所以anyRequest().permitAll要写在最后!!!
}
antMatchers()方法可以改写成通配符的方式antMatchers("/spitters/**").authenticated();
或指定多个路径antMatchers("/spitters/**", "/spittle/me").authenticated();
。
authenticated()
要求在执行请求时,必须已经登录了应用。如果用户没有进行认证,Filter就会拦截该请求。
permitAll()
允许请求没有任何的安全限制。
除了authenticated()和permitAll()还有其它的方法:
方法 | 功能 |
---|---|
access(String) | 如果给定的SpEL表达式结果为true,就允许访问 |
anonymous() | 允许匿名用户访问 |
authenticated() | 允许认证过的用户访问 |
denyAll() | 拒绝所有访问 |
fullyAuthenticated | 用户是完整认证(不是Remember-me认证的)允许访问 |
hasAnyAuthority(String…) | 如果用户具备给定权限中的某一个的话,允许访问 |
hasAnyRole(String…) | 如果用户具备给定角色中的某一个的话,允许访问 |
hasAuthority(String) | 如果用户具备给定的权限,允许访问 |
hasRole(String) | 如果用户具备给定的角色,允许访问 |
hasIpAddress(String) | 如果请求来自给定Ip,允许访问 |
not() | 对其它访问方法的结果取反 |
permitAll() | 允许访问 |
remenberMe() | 如果用户是通过Remember-me功能认证的,允许访问 |
//在SecurityConfig类中
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers("/spitters/me").authenticated()
.antMatchers(HttpMethod.POST, "/spittles").authenticated()
.anyRequest().permitAll()
.and().requiresChannel() //使用通道
.antMatchers("/spitter/form").requiresSecure(); //为这个路径使用 Https通道
//http.requiresChannel().antMatchers("/").requiresInsecure(); //使用 Http通道
}
requiresSecure()使用Https,requiresInsecure()使用Http
在SecurityConfig配置类中配置该功能非常简单:
protected void configure(HttpSecurity http) throws Exception{
http...//其它设置
http.rememberMe() //这个功能是通过在cookie中存储一个token完成的
.tokenValiditySeconds(2419200) //指定这个token四周有效,默认是两周
.key("spitterKey");//这里将私钥的key设置为spitterKey,虽然我也不知道怎么用
//token包含用户名、密码、过期时间、私钥,写入cookie时已经进行了MD5哈希
}
要实现这个功能,还需要前端页面的登陆表单中包含一个name为"remember_me"的参数。
在Remember-me生效期间退出的功能,Filter已经默认实现了,Filter会拦截对“/logout”的请求。因此,为应用添加退出功能只需在页面中添加链接:
<a href="/logout"> Logout a>
<a th:href="@{/logout}"> Logout a>
点击后、Remember-me的token会被清除,浏览器会重定向到"/login?logout"。
如果想定位到其它页面,可以增加配置:
http.formLogin()
.loginPage("/login") //设置的登录页面
.and()
.logout().logoutSuccessUrl("/"); //设置登出成功后返回的页面