基於java config的springSecurity(四)--啓用全局方法安全

 

參考資料:http://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle
前面實現了認證,通常都知足不了應用的需求.使用@EnableGlobalMethodSecurity來實現受權,實現用戶對某個操做是否有權限的控制.
 html

1.使用@EnableGlobalMethodSecurity註解,能夠直接標註以前的SecurityConfig上面.也能夠獨立出來一個配置MethodSecurityConfig,繼承GlobalMethodSecurityConfiguration能夠實現更加複雜的操做(好比重寫createExpressionHandler方法實現自定義的MethodSecurityExpressionHander).java

 

package org.exam.config;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
/**
 * Created by xin on 15/1/15.
 */
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

}

 

而後把這個MethodSecurityConfig加入到RootConfigClasses.更改org.exam.config.DispatcherServletInitializer#getRootConfigClassesweb

 

@Override
protected Class<?>[] getRootConfigClasses() {
	return new Class<?>[] {AppConfig.class,SecurityConfig.class,MethodSecurityConfig.class};
}

這個註解是須要一個AuthenticationManager,因爲前面配置認證(Authentication),spring security會有一個AuthenticationManager(ProviderManager實現).從org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#authenticationManagerBean的Javadoc來看,重寫這個方法來暴露來自於configure(AuthenticationManagerBuilder)的AuthenticationManager成一個Bean,代碼以下.spring

@Bean(name name="myAuthenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
	return super.authenticationManagerBean();
}

把這段代碼加到SecurityConfig.
2.在方法(類或接口上)添加註解來限制方法的訪問.咱們使用prePostEnabled(從參考文檔介紹,要比securedEnabled和jsr250Enabled強大).例如:數據庫

package org.exam.service;
import org.exam.domain.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
/**
 * Created by xin on 15/1/14.
 */
public interface UserService {
	@PreAuthorize("hasAuthority('user_query')")
	Page<User> findAll(Pageable pageable);
	User save(User user);
	User findOne(Long id);
	void delete(Long id);
}

3.直接修改相應數據庫記錄,讓一個用戶有user_query權限,一個用戶沒有user_query權限來測試.在單元測試添加了初始化的數據.tomcat

@Test
public void testInitUsers(){
	Authority authority1=new Authority();
	authority1.setName("查看用戶");
	authority1.setAuthority("user_query");
	authorityRepository.save(authority1);
	Authority authority2=new Authority();
	authority2.setName("保存用戶");
	authority2.setAuthority("user_save");
	authorityRepository.save(authority2);
	Authority authority3=new Authority();
	authority3.setName("刪除用戶");
	authority3.setAuthority("user_delete");
	authorityRepository.save(authority3);
	Role role1=new Role();
	role1.setName("管理員");
	role1.setAuthorities(new HashSet<Authority>(Arrays.asList(authority2, authority3)));
	roleRepository.save(role1);
	User user1=new User();
	user1.setUsername("admin");
	user1.setPassword("$2a$04$fCqcakHV2O.4AJgp3CIAGO9l5ZBq61Gt6YNzjcyC8M.js0ucpyun.");//admin
	user1.setAuthorities(new HashSet<Authority>(Arrays.asList(authority1)));
	user1.setRoles(new HashSet<Role>(Arrays.asList(role1)));
	userRepository.save(user1);
}

還有,參考文檔有安全表達試的介紹,好比下面表明的意思:傳入的聯繫人爲當前的認證用戶,才能夠執行doSomething方法
@PreAuthorize("#c.name == authentication.name")安全

public void doSomething(@P("c")Contact contact);mvc

 

小結:
從個人測試結果來看,一旦用戶被拒絕訪問,就會返回一個403,使用jetty9.2.2.v20140723發現返回的response的Content-Type是text/html; charset=ISO-8859-1.因此出現了亂碼.但使用tomcat 8.0.14下部署是不會亂碼的.app

跟蹤源碼到org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper#sendError(int, java.lang.String),發現這個Response仍是UTF-8,再跟下去就到javax.servlet.http.HttpServletResponseWrapper#sendError(int, java.lang.String),今後處基本可判斷亂碼的引發與spring security無關.繼續到org.eclipse.jetty.server.Response#sendError(int, java.lang.String),有一句setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString());固然,這個403要處理,可在SecurityConfig#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)配置錯誤頁,好比
and().exceptionHandling().accessDeniedPage("/exception/403");注意mvc要能正常解析這個url,而且權限也要忽略這個url.而後在頁面經過${SPRING_SECURITY_403_EXCEPTION}能夠獲取這個異常(爲何是SPRING_SECURITY_403_EXCEPTION,可參考org.springframework.security.web.access.AccessDeniedHandlerImpl#handle)dom

若是把權限放在web層(好比@PreAuthorize放在spring mvc的Controller方法上),那就將MethodSecurityConfig加入到ServletConfigClasses.若是但願在web和service層都要作權限控制,就把全部的Config放到RootConfigClasses,ServletConfigClasses留空.解析一下爲何要這麼作:

實現這個啓用全局方法安全是經過spring aop實現的.一旦使用了@EnableGlobalMethodSecurity註解,就會註冊一個名爲org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator的BeanPostProcessor.它的做用就是將那些使用了@PreAuthorize之類的類生成代理.但前提下,經過這個方式生成代理的目標Bean,它至少要和這個BeanPostProcessor在同一個BeanFactory(上下文).進一步來看看org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization:

 

@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {
		Object result = existingBean;
		for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
			result = beanProcessor.postProcessAfterInitialization(result, beanName);
			if (result == null) {
				return result;
			}
		}
		return result;
	}
public List<BeanPostProcessor> getBeanPostProcessors() {
		return this.beanPostProcessors;
	}

這個beanPostProcessors是在實例化spring容器時,將對應的beanPostProcessor添加到對應的spring容器.假設用戶自定義的Bean和這個beanPostProcessor不在同一個上下文,那麼天然它不會受這個BeanPostProcessor的影響.剩下的就不難解析了.

 

 

還有要注意使用@EnableWebMvcSecurity或@EnableWebSecurity來定義springSecurityFilterChain Bean的Configuration類應只能放在RootApplicationContext下面,至於緣由可看http://blog.csdn.net/xiejx618/article/details/50603758文末

 

 

 

源碼:http://download.csdn.net/detail/xiejx618/8366505

啓用全局方法安全是經過spring aop實現的,具體看:<spring aop(十)--spring security啓用全局方法使用aop的分析>

相關文章
相關標籤/搜索