《Spring实战》学习笔记-------Security框架


Spring Security是基于Spring AOP和Serlet规范中的Filter实现的安全框架。它使用Filter保护Web请求并限制URL级别的访问;使用AOP保护方法调用,确保只有具备适当权限的用户才能访问保护的方法。

1. Spring Security的模块

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)进行集成的功能

2. 初步配置安全性,过滤Web请求(Filter)*****

先注册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服务

要想实现应有的作用,还需要:

  • 配置用户存储
  • 指定需要认证的请求

3. 配置用户存储

通过重载configure(AuthenticationManagerBuilder auth)方法来实现。

3.1 基于内存的用户存储

这种存储多用于调试和开发测试。调用 auth.inMemoryAuthentication().

3.2 基于数据库的用户存储

注入一个DataSource类型的dataSource,调用auth.jdbcAuthentication().dataSource(dataSource)方法。可以通过该方法下的.usersByUsernamQuery()来自定义查询用户。
其余的查询方法和密码加密详见配置用户存储

3.3 配置自定义的用户服务**

如果需要认证的用户存储在非关系型数据库,或者我们想用自己的方法从数据库中取出用户再进行认证。这种情况下,可以自定义一个实现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对象传递返回。


4. 拦截请求****

通过重载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功能认证的,允许访问

5. 强制使用Https

//在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


6. 启用Remember-me功能

在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> <!-- 这个是Thymeleaf模板的形式-->

点击后、Remember-me的token会被清除,浏览器会重定向到"/login?logout"。
如果想定位到其它页面,可以增加配置:

http.formLogin() 
		.loginPage("/login") //设置的登录页面
	.and()
		.logout().logoutSuccessUrl("/"); //设置登出成功后返回的页面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值