springsecurity权限控制系列教程:1 入门

springsecurity的执行过程,无非就是拦截器的执行流程,网上有很多资料可供学习。新手学习后实际去做权限控制时,感觉对springsecurity的理解还是雾里看花,如何快速学透权限控制?看完我这篇博客就行了。拦截器里面的实现需要一些组件来实现,在这些组件中有三个最重要接口or方法

1 UserDetailsService 处理用户和用户可以访问的url(可以在数据库中配置用户,角色)

                     密码验证在 authenticate方法里面 http://blog.csdn.net/d7011800/article/details/8692667

2 FilterInvocationSecurityMetadataSource 

             (访问的url和访问当前url所需要的角色.例如:用户访问http://www.love.jsp,调用FilterInvocationSecurityMetadataSource

             的方法来获取被拦截url所需的全部权限Role角色,

             这个接口中要注意两个接口:RequestMatcher访问的url,ConfigAttribute访问的url需要的对应的Role角色)

3 AccessDecisionManager投票器 

             三种策略,分别对应那个策略类: 
             UnanimousBased.java 只要有一个Voter不能完全通过权限要求,就禁止访问。 
             AffirmativeBased.java只要有一个Voter可以通过权限要求,就可以访问。 
             ConsensusBased.java只要通过的Voter比禁止的Voter数目多就可以访问了。 

主要流程:用户访问url,FilterInvocationSecurityMetadataSource的方法获取被拦截url所需的全部权限Role角色,把这些权限Role角色去和数据库中配置的所有权限Role角色比对(查询数据库中配置的所有权限Role角色主要由接口UserDetailsService完成),如果"访问此url需要的权限Role角色"是"用户数据库中配置的权限Role角色"的子集,就允许访问。这个比对过程由AccessDecisionManager去完成。

附:

1 我上面使用的是用角色作为中转站,通过比较角色确定是否有权限。其实也可以用其他的模型作为中转站。

如把权限作为中转表:

http://blog.csdn.net/jaune161/article/details/18353397

http://blog.csdn.net/jaune161/article/details/18354599 

http://blog.csdn.net/jaune161/article/details/18446481(什么时候查询用户所有的角色或权限呢?可以是此链接jaune161的filterSecurityInterceptor在SpringSecurity默认的过滤器之前执行。也可以是在用户校验完用户名密码后也可以添加自己定义的AuthenticationFilter(对应的bean是UsernamePasswordAuthenticationFilter或UsernamePasswordAuthenticationFilter的继承类)到FilterChain的FORM_LOGIN_FILTER位置见http://elim.iteye.com/blog/2161648目前再用这种。

elim.iteye.com/blog/2161648链接里也提到了两种方式的关联:1.5.1 FilterSecurityInterceptor---FilterSecurityInterceptor是用于保护Http资源的,它需要一个AccessDecisionManager和一个AuthenticationManager的引用。它会从SecurityContextHolder获取Authentication,然后通过SecurityMetadataSource(对应jaune161里FilterInvocationSecurityMetadataSource,是SecurityMetadataSource的实现类)可以得知当前请求是否在请求受保护的资源。

elim里在UsernamePasswordAuthenticationFilter的return this.getAuthenticationManager().authenticate(authRequest);代码里getAuthenticationManager()就是获得AuthenticationManager)

2腾讯rep 浅谈springSecurity:http://zhaoxijun.iteye.com/blog/2261757

3比较老的springsecurity开发指南,但是内容很好:http://www.mossle.com/docs/auth/html/index.html

好了,有了我上文的铺垫,再看完那两篇文章,你再学习springsecurity就很容易就学了。

-----看完后是不是更迷惑了---囧!!!

小结:整个流程是什么样的呢?介绍三种方式:

方式1 假设用户user--->角色role1---->权限abcUrl。在UserDetailsService 中去数据库中查询用户时,根据用户的角色查询对应的权限,并把权限放在user对象里。用户访问urlBcd时,在投票器中校验,这里要自己写个自定义投票器,投票器里逻辑这样写,根据request获得用户请求的url:urlBcd,取出user对象里用户所有权限,看是否包含urlBcd。发现用户只有一个权限abcUrl,没有urlBcd,拒绝访问。

方式2:假设用户user--->角色role1---->权限abcUrl。在UserDetailsService 中去数据库中查询用户时,根据用户的角色查询对应的权限,并把权限放在user对象里。在用户访问的controller的方法加上注解:PreAuthorize

@PreAuthorize("hasAuthority('query-demo')")
    public String getDemo(){
        return "good";
    }

用户不包含query-demo权限,拒绝访问。博客 代码

方式3: 见下图流程

注意这个表和前面两个有点不同,用户---角色---权限role_admin---资源url 见链接 。在UserDetailsService 中去数据库中查询用户时,根据用户的角色查询对应的权限,并把权限放在user对象里。

链接   : 在投票器中鉴权处理。Authentication中是用户及用户权限信息,attributes是访问资源需要的权限,然后循环判断用户是否有访问资源需要的权限,如果有就返回ACCESS_GRANTED,通俗的说就是有权限。

链接  :查询用户权限role_xx,根据请求的资源url,根据数据库中配置资源url和权限role_xx的关系,去获取访问当前资源url需要哪些权限role_xx

通过查询数据库获得资源与权限的对应列表 RequestMap,

具体的逻辑见getURLResourceMapping---->

loadResuorce() 里key为resourcePath,vlue为权限, 一个资源对应多个权限,则把所有权限用逗号拼接组成vlaue,如map("resourcePath","aaa,bbb,c")--->

最后在bindRequestMap()里组合为Map<RequestMatcher, Collection<ConfigAttribute>> 这样的数据结构赋值给 requestMap。

getAllConfigAttributes:获取所有权限集合
getAttributes:根据request请求获取访问资源所需权限

上面两个链接其实就是使用自定义的securityMetadataSource里的代码,在FilterInvocationSecurityMetadataSource实现类里重写。

方式3 流程图


springsecurity默认的提供了三种决策器,AffirmativeBased 一票通过,只要有一个投票器通过就允许访问ConsensusBased 有一半以上投票器通过才允许访问资源

UnanimousBased 。决策器里再调用投票器去投票判断是否有权限,有无权限访问的最终觉得权是由投票器来决定的,最常见的投票器为RoleVoter。例如作者用的是投票器里鉴权。

禅师,决策器和投票器什么关系?非得调用投票器才能鉴权吗?当然不是,其实决策器就是判断用户有无权限,不去调用投票器去判断也可以,自己写逻辑判断也行。打个比方我们项目里controller层会调用ip工具类去获取访问者的ip 地址,非得调用ip工具类才可以获得ip地址吗,没必要,我不用ip工具类直接在controller中写个if判断获取ip地址也行。禅师,我懂了,你的意思是说ip工具类抽出来有利于复用,修改,或者把ip工具类抽象成接口,然后开发地球ip,火星ip多个实现类,方便调用,虽然有这么多好处,但是我可以不用,我就是爱自己写,毕竟葛大爷说过,有小孩这事我还是喜欢自力更生。。。禅师:大郎,注意看好莲莲。

链接 作者写的自定义决策器里就不使用投票器,直接返回有无权限。

xml中:access-decision-manager-ref="accessDecisionManager" 

<bean id="accessDecisionManager" class="xxxMyAccessDecisionManager">

然后类MyAccessDecisionManager extends AbstractAccessDecisionManager 重写decide方法

还有点要注意,前文提到 链接  : 去获取访问当前资源url需要哪些权限role_xx。容器启动时会操作一次,如果后来管理员重新授权,访问当前url对应的需要的权限变了怎么办。即系统会在初始化时一次将所有资源加载到内存中,即使在数据库中修改了资源信息,系统也不会再次去从数据库中读取资源信息。这就造成了每次修改完数据库后,都需要重启系统才能时资源配置生效。我们只要想办法在管理员修改数据后,更新内存中数据map即可。

解决方案是,有网友提出1 如果数据库中的资源出现的变化,需要刷新内存中已加载的资源信息时。

ApplicationContext ctx =  WebApplicationContextUtils.
                          getWebApplicationContext(request.getSession().getServletContext());
xxSecurityMetadataSource cs=(CustomInvocationSecurityMetadataSource)ctx.
                             getBean("xxxSecurityMetadataSource",com.xx.xxMetadataSource.class);
cs.loadResourceDefine();
loadResourceDefine()重新查数据库,资源url需要哪些权限role_xx。

2  放资源的map 用static。

这个解决的办法也行,就是多个管理员并发修改时会有点问题。我现在想的是用定时器做。

源码:对比xml和springboot配置方式

Spring Security


Spring Security是如何完成身份认证的?

1 用户名和密码被过滤器获取到,封装成Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。

AuthenticationManager 身份管理器负责验证这个Authentication

3 认证成功后,AuthenticationManager身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除)Authentication实例。

SecurityContextHolder安全上下文容器将第3步填充了信息的Authentication,通过SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菠萝科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值