基于java config的springSecurity(四)--启用全局方法安全

来源:互联网 发布:海康视频矩阵128进24出 编辑:程序博客网 时间:2024/05/21 10:27

1.使用@EnableGlobalMethodSecurity注解,可以直接标注之前的SecurityConfig上面.也可以独立出来一个配置MethodSecurityConfig,继承GlobalMethodSecurityConfiguration可以实现更加复杂的操作(比如重写createExpressionHandler方法实现自定义的MethodSecurityExpressionHander).

[java] view plain copy
  1. package org.exam.config;  
  2. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;  
  3. import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;  
  4. /** 
  5.  * Created by xin on 15/1/15. 
  6.  */  
  7. @EnableGlobalMethodSecurity(prePostEnabled=true)  
  8. public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {  
  9.   
  10. }  

然后把这个MethodSecurityConfig加入到RootConfigClasses.更改org.exam.config.DispatcherServletInitializer#getRootConfigClasses

[java] view plain copy
  1. @Override  
  2. protected Class<?>[] getRootConfigClasses() {  
  3.     return new Class<?>[] {AppConfig.class,SecurityConfig.class,MethodSecurityConfig.class};  
  4. }  
这个注解是需要一个AuthenticationManager,由于前面配置认证(Authentication),spring security会有一个AuthenticationManager(ProviderManager实现).从org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#authenticationManagerBean的Javadoc来看,重写这个方法来暴露来自于configure(AuthenticationManagerBuilder)的AuthenticationManager成一个Bean,代码如下.
[java] view plain copy
  1. @Bean(name name="myAuthenticationManager")  
  2. @Override  
  3. public AuthenticationManager authenticationManagerBean() throws Exception {  
  4.     return super.authenticationManagerBean();  
  5. }  
把这段代码加到SecurityConfig.
2.在方法(类或接口上)添加注解来限制方法的访问.我们使用prePostEnabled(从参考文档介绍,要比securedEnabled和jsr250Enabled强大).例如:
[java] view plain copy
  1. package org.exam.service;  
  2. import org.exam.domain.User;  
  3. import org.springframework.data.domain.Page;  
  4. import org.springframework.data.domain.Pageable;  
  5. import org.springframework.security.access.prepost.PreAuthorize;  
  6. /** 
  7.  * Created by xin on 15/1/14. 
  8.  */  
  9. public interface UserService {  
  10.     @PreAuthorize("hasAuthority('user_query')")  
  11.     Page<User> findAll(Pageable pageable);  
  12.     User save(User user);  
  13.     User findOne(Long id);  
  14.     void delete(Long id);  
  15. }  
3.直接修改相应数据库记录,让一个用户有user_query权限,一个用户没有user_query权限来测试.在单元测试添加了初始化的数据.
[java] view plain copy
  1. @Test  
  2. public void testInitUsers(){  
  3.     Authority authority1=new Authority();  
  4.     authority1.setName("查看用户");  
  5.     authority1.setAuthority("user_query");  
  6.     authorityRepository.save(authority1);  
  7.     Authority authority2=new Authority();  
  8.     authority2.setName("保存用户");  
  9.     authority2.setAuthority("user_save");  
  10.     authorityRepository.save(authority2);  
  11.     Authority authority3=new Authority();  
  12.     authority3.setName("删除用户");  
  13.     authority3.setAuthority("user_delete");  
  14.     authorityRepository.save(authority3);  
  15.     Role role1=new Role();  
  16.     role1.setName("管理员");  
  17.     role1.setAuthorities(new HashSet<Authority>(Arrays.asList(authority2, authority3)));  
  18.     roleRepository.save(role1);  
  19.     User user1=new User();  
  20.     user1.setUsername("admin");  
  21.     user1.setPassword("$2a$04$fCqcakHV2O.4AJgp3CIAGO9l5ZBq61Gt6YNzjcyC8M.js0ucpyun.");//admin  
  22.     user1.setAuthorities(new HashSet<Authority>(Arrays.asList(authority1)));  
  23.     user1.setRoles(new HashSet<Role>(Arrays.asList(role1)));  
  24.     userRepository.save(user1);  
  25. }  
还有,参考文档有安全表达试的介绍,比如下面代表的意思:传入的联系人为当前的认证用户,才可以执行doSomething方法
@PreAuthorize("#c.name == authentication.name")

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


小结:
从我的测试结果来看,一旦用户被拒绝访问,就会返回一个403,使用jetty9.2.2.v20140723发现返回的response的Content-Type是text/html; charset=ISO-8859-1.所以出现了乱码.但使用tomcat 8.0.14下部署是不会乱码的.

跟踪源码到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)

如果把权限放在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:

[java] view plain copy
  1. @Override  
  2.     public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)  
  3.             throws BeansException {  
  4.         Object result = existingBean;  
  5.         for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {  
  6.             result = beanProcessor.postProcessAfterInitialization(result, beanName);  
  7.             if (result == null) {  
  8.                 return result;  
  9.             }  
  10.         }  
  11.         return result;  
  12.     }  
[java] view plain copy
  1. public List<BeanPostProcessor> getBeanPostProcessors() {  
  2.         return this.beanPostProcessors;  
  3.     }  
这个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的分析>

0
阅读全文
0 0