参考资料: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的分析>