当前位置: 首页 > news >正文

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

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

 

参考资料:http://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle
前面实现了认证,一般都满足不了应用的需求.使用@EnableGlobalMethodSecurity来实现授权,实现用户对某个操作是否有权限的控制.
 

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

 

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#getRootConfigClasses

 

@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,代码如下.

@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权限来测试.在单元测试添加了初始化的数据.

@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);

 

小结:
从我的测试结果来看,一旦用户被拒绝访问,就会返回一个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:

 

@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的分析>

转载于:https://my.oschina.net/airship/blog/1556172

相关文章:

  • 黑客预警:搞瘫北美互联网?规模更大的僵尸网络现身
  • 一个关于ConfigurationManager.GetSecion方法的小问题
  • 基础大概回顾
  • 重新学习Mysql数据库3:Mysql存储引擎与数据存储原理
  • P1679 神奇的四次方数
  • nginx服务企业应用
  • Hadoop起源
  • HDU2255 奔小康赚大钱 【KM算法】
  • RxJava简介与入门(一)
  • 手把手教你用1行命令实现人脸识别
  • 【编程之美】字符串移位包含的问题(续)
  • 前端同学大福利,最全的面试题目整理
  • 随机ID添加
  • 新概念英语(1-115)Knock! Knock!
  • Python 数据结构
  • 【技术性】Search知识
  • 77. Combinations
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • E-HPC支持多队列管理和自动伸缩
  • Fabric架构演变之路
  • Java Agent 学习笔记
  • Java 最常见的 200+ 面试题:面试必备
  • JSONP原理
  • js作用域和this的理解
  • Vue全家桶实现一个Web App
  • 从0到1:PostCSS 插件开发最佳实践
  • 记录:CentOS7.2配置LNMP环境记录
  • 普通函数和构造函数的区别
  • 深入浏览器事件循环的本质
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 小程序 setData 学问多
  • 一道闭包题引发的思考
  • ​iOS实时查看App运行日志
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • #传输# #传输数据判断#
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (十)c52学习之旅-定时器实验
  • (转)http-server应用
  • (转载)Google Chrome调试JS
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .cn根服务器被攻击之后
  • .gitignore文件设置了忽略但不生效
  • .NET Core WebAPI中封装Swagger配置
  • .NET Core 通过 Ef Core 操作 Mysql
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET构架之我见
  • /bin/bash^M: bad interpreter: No such file ordirectory
  • [ C++ ] STL_stack(栈)queue(队列)使用及其重要接口模拟实现
  • [ element-ui:table ] 设置table中某些行数据禁止被选中,通过selectable 定义方法解决
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例