近期阴差阳错的搞上了SpringSecurity3。由于是自己做的小系统。中间遇到了非常多坑,基本每一个坑都踩过了,网上也查了不少资料,发现有不少错误的。更是让我绕了一圈又一圈,如今把一些主要的东西总结一下。

先从总体上总结一下为什么使用SS,一般的,在不使用ss的情况下,我们基本会在每一个业务方法运行前。插入一段用于验证权限的代码。从而推断当前用户是否有对应权限进行操作。这样做就会让业务方法和验证权限有了一个紧密的耦合。假设使用ss,我们就能够通过注解或者XML配置方式取代权限验证,使得业务和权限代码彻底分离,通过下图能够更形象的理解:

眼下。权限管理採用最多的技术都是基于角色訪问控制技术RBAC(Role Based Access Control)。一般来说,提供例如以下功能:1,角色管理界面,由用户定义角色,给角色赋权限。2。用户角色管理界面,由用户给系统用户赋予角色。什么是RBAC。说究竟事实上就是五张表,权限表-权限角色相应表-角色表-角色用户相应表-用户表,比較常见。可是ss3默认支持的并非这样的模式,而是通过XML配置角色及用户的方式实现的权限验证等操作,所以须要我们去实现SS中一些接口。让其支持RBAC。以下開始搭建一套支持RBAC技术的SS框架:

(1)数据库相关表格:

1.用户表Users

CREATE TABLE `users` (

`password` varchar(255) default NULL,

       `username` varchar(255) default NULL,

       `uid` int(11) NOT NULL auto_increment,

       PRIMARY KEY  (`uid`)

    )

2.角色表Roles

CREATE TABLE `roles` (

     `rolename` varchar(255) default NULL,

     `rid` int(11) NOT NULL auto_increment,

     PRIMARY KEY  (`rid`)

   )

3 用户_角色表users_roles

CREATE TABLE `users_roles` (

--用户表的外键

     `uid` int(11) default NULL,

--角色表的外键

     `rid` int(11) default NULL,

     `urid` int(11) ,

     PRIMARY KEY  (`urid`),

   )

4.资源表resources

CREATE TABLE `resources` (

-- 权限所相应的url地址

     `url` varchar(255) default NULL,

--权限所相应的编码,例201代表发表文章

     `resourcename` varchar(255) default NULL,

     `rsid` int(11) ,

     PRIMARY KEY  (`rsid`)

   )

5.角色_资源表roles_resources

CREATE TABLE `roles_resources` (

      `rsid` int(11) default NULL,

      `rid` int(11) default NULL,

      `rrid` int(11) NOT NULL ,

      PRIMARY KEY  (`rrid`),

      )

(2)在继续配置前,须要知道ss是怎样通过权限验证的。实际上ss通过拦截器,拦截发来的请求,对其进行验证的。而详细验证的方式则是通过我们实现相关接口的方法来进行的。

既然是拦截器,web.xml势必是优先配置的。

[html] view
plain
 copy

  1. <!DOCTYPE web-app PUBLIC
  2. "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  3. "http://java.sun.com/dtd/web-app_2_3.dtd" >
  4. <web-app>
  5. <display-name>Archetype Created Web Application</display-name>
  6. <!-- Spring Security配置 -->
  7. <filter>
  8. <filter-name>springSecurityFilterChain</filter-name>
  9. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  10. </filter>
  11. <filter-mapping>
  12. <filter-name>springSecurityFilterChain</filter-name>
  13. <url-pattern>/*</url-pattern>
  14. </filter-mapping>
  15. <!-- Spring MVC配置 -->
  16. <servlet>
  17. <servlet-name>spring</servlet-name>
  18. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  19. <init-param>
  20. <param-name>contextConfigLocation</param-name>
  21. <param-value>classpath:spring-mvc.xml</param-value>
  22. </init-param>
  23. <load-on-startup>1</load-on-startup>
  24. </servlet>
  25. <servlet-mapping>
  26. <servlet-name>spring</servlet-name>
  27. <url-pattern>*.do</url-pattern>
  28. </servlet-mapping>
  29. <!-- Spring配置 -->
  30. <listener>
  31. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  32. </listener>
  33. <listener>
  34. <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  35. </listener>
  36. <!-- 指定Spring Bean的配置文件所在文件夹。默认配置在WEB-INF文件夹下 -->
  37. <context-param>
  38. <param-name>contextConfigLocation</param-name>
  39. <param-value>classpath:applicationContext*.xml,classpath:spring-mybatis.xml</param-value>
  40. </context-param>
  41. <filter>
  42. <filter-name>encodingFilter</filter-name>
  43. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  44. <init-param>
  45. <param-name>encoding</param-name>
  46. <param-value>UTF-8</param-value>
  47. </init-param>
  48. </filter>
  49. <!-- Spring 刷新Introspector防止内存泄露 -->
  50. <listener>
  51. <listener-class>
  52. org.springframework.web.util.IntrospectorCleanupListener
  53. </listener-class>
  54. </listener>
  55. <!--  获取Spring Security session的生命周期-->
  56. <listener>
  57. <listener-class>
  58. org.springframework.security.web.session.HttpSessionEventPublisher
  59. </listener-class>
  60. </listener>
  61. <!-- session超时定义,单位为分钟 -->
  62. <session-config>
  63. <session-timeout>20</session-timeout>
  64. </session-config>
  65. </web-app>

接下来是spring security3的一些配置,详细的每个是什么意思。网上非常多资料。这里不赘述了。

总之。须要依据自己的需求,进行对应的改动。

[html] view
plain
 copy

  1. <?xml version="1.0" encoding="UTF-8"?

    >

  2. <beans:beans xmlns="http://www.springframework.org/schema/security"
  3. xmlns:beans="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xmlns:context="http://www.springframework.org/schema/context"
  8. xmlns:mvc="http://www.springframework.org/schema/mvc"
  9. xsi:schemaLocation="
  10. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  11. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
  12. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
  13. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
  14. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
  15. http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
  16. <http pattern="/css/**" security="none"></http>
  17. <http pattern="/images/**" security="none"></http>
  18. <http pattern="/img/**" security="none"></http>
  19. <http pattern="/scripts/**" security="none"></http>
  20. <http pattern="/font-awesome/**" security="none"></http>
  21. <http pattern="/system/resources/**" security="none"></http>
  22. <http pattern="/system/login.do" security="none"/>
  23. <http auto-config="true" use-expressions="true">
  24. <form-login login-page="/system/login.do"  default-target-url="/system/sysManage.do"/>
  25. <!--
  26. error-if-maximum-exceeded 后登陆的账号会挤掉第一次登陆的账号
  27. session-fixation-protection  防止伪造sessionid攻击,用户登录成功后会销毁用户当前的session。

  28. -->
  29. <!-- <session-management invalid-session-url="/user/timedout" session-fixation-protection="none">
  30. <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>
  31. </session-management> -->
  32. <custom-filter ref="myFilterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>
  33. </http>
  34. <!-- 认证管理器。实现用户认证的入口,主要实现UserDetailsService接口就可以 -->
  35. <authentication-manager alias="authenticationManager">
  36. <authentication-provider
  37. user-service-ref="myUserDetailsServiceImpl">
  38. <!-- <password-encoder hash="md5" /> -->  <!--盐值  [加入这个属性后,加密password明文为:"password明文{盐值}"]  -->
  39. </authentication-provider>
  40. </authentication-manager>
  41. <!-- 配置过滤器 -->
  42. <beans:bean id="myFilterSecurityInterceptor" class="com.product.sys.security.MyFilterSecurityInterceptor">
  43. <!-- 用户是否拥有所请求资源的权限 -->
  44. <beans:property name="accessDecisionManager" ref="myAccessDescisionManager" />
  45. <!-- 资源与权限相应关系 -->
  46. <beans:property name="fisMetadataSource" ref="mySecurityMetadataSource" />
  47. <!-- 用户拥有的权限 -->
  48. <beans:property name="authenticationManager" ref="authenticationManager" />
  49. </beans:bean>
  50. <beans:bean id="mySecurityMetadataSource" class="com.product.sys.security.MySecurityMetadataSource"><beans:constructor-arg name="userMapper" ref="userMapper"></beans:constructor-arg></beans:bean>
  51. <beans:bean id="myAccessDescisionManager" class="com.product.sys.security.MyAccessDescisionManager"></beans:bean>
  52. </beans:beans>

到上面的这个配置文件,则是重中之重了。和ss3打交道。主要都是这个文件。简单说一下,我们须要实现一个自己的filter。在配置中就是myFilterSecurityInterceptor,而这个filter中,还须要我们额外注入三个bean,各自是accessDecisionManager、fisMetadataSource以及authenticationManager,这三个属性中除了fisMetadataSource能够自己定义名称外,其它两个都在ss3的父类中定义好了。所以此处须要特别注意。在这里掉过坑了。另外这里说一下这三个分别的作用。accessDecisionManager中有decide(Authentication
authentication, Object object,Collection<ConfigAttribute> configAttributes)方法,该方法用于推断当前用户是否有权限进行操作,參数中authentication包括了当前用户所拥有的权限,configAttributes中包括了进行该步骤须要的权限,对其进行对照就能够推断该用户是否有权限进行操作。

[html] view
plain
 copy

  1. /**
  2. * @description  訪问决策器,决定某个用户具有的角色。是否有足够的权限去訪问某个资源 ;做终于的訪问控制决定
  3. */
  4. public class MyAccessDescisionManager implements AccessDecisionManager{
  5. @Override
  6. public void decide(Authentication authentication, Object object,
  7. Collection<ConfigAttribute> configAttributes)
  8. throws AccessDeniedException, InsufficientAuthenticationException {
  9. // TODO Auto-generated method stub
  10. System.out.println("MyAccessDescisionManager.decide()------------------验证用户是否具有一定的权限--------");
  11. if(configAttributes==null) return;
  12. Iterator<ConfigAttribute> it = configAttributes.iterator();
  13. while(it.hasNext()){
  14. String needResource = it.next().getAttribute();
  15. //authentication.getAuthorities()  用户全部的权限
  16. for(GrantedAuthority ga:authentication.getAuthorities()){
  17. if(needResource.equals(ga.getAuthority())){
  18. return;
  19. }
  20. }
  21. }
  22. throw new AccessDeniedException("--------MyAccessDescisionManager:decide-------权限认证失败!");
  23. }
  24. @Override
  25. public boolean supports(ConfigAttribute attribute) {
  26. // TODO Auto-generated method stub
  27. return true;
  28. }
  29. @Override
  30. public boolean supports(Class<?> clazz) {
  31. // TODO Auto-generated method stub
  32. return true;
  33. }
  34. }

到这里。能够非常自然的想到是权限和用户数据从哪里得到的,filterInvocationSecurityMetadataSource在被载入时候,会首先将权限的信息建立起来。这里我用一个map。key为url,value为该权限的名称。这一步是在构造方法中进行的,也就是server启动时候完毕的。而当用户訪问某一个地址时,ss会到该类中调用getAttributes(Object obj)方法。obj中包括了訪问的url地址,我们须要做的就是将该url相应的权限名称返回给ss,而ss会将返回的这个对象,事实上就是accessDecisionManager的decide方法中的configAttributes对象。

[java] view
plain
 copy

  1. /**
  2. * @description  资源源数据定义。将全部的资源和权限相应关系建立起来,即定义某一资源能够被哪些角色訪问
  3. * @author aokunsang
  4. * @date 2012-8-15
  5. */
  6. public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
  7. private UserMapper userMapper;
  8. public UserMapper getUserMapper() {
  9. return userMapper;
  10. }
  11. public void setUserMapper(UserMapper userMapper) {
  12. this.userMapper = userMapper;
  13. }
  14. /* 保存资源和权限的相应关系  key-资源url  value-权限 */
  15. private static Map<String,Collection<ConfigAttribute>> resourceMap = null;
  16. private AntPathMatcher urlMatcher = new AntPathMatcher();
  17. public MySecurityMetadataSource(UserMapper userMapper) {
  18. this.userMapper = userMapper;
  19. loadResourcesDefine();
  20. }
  21. @Override
  22. public Collection<ConfigAttribute> getAllConfigAttributes() {
  23. return null;
  24. }
  25. private void loadResourcesDefine(){
  26. resourceMap = new HashMap<String,Collection<ConfigAttribute>>();
  27. System.out.println("MySecurityMetadataSource.loadResourcesDefine()--------------開始载入资源列表数据--------");
  28. List<RolePO> roles = userMapper.findAllRoles();
  29. for(RolePO role : roles){
  30. List<ResourcePO> resources = role.getResources();
  31. for(ResourcePO resource : resources){
  32. Collection<ConfigAttribute> configAttributes = null;
  33. ConfigAttribute configAttribute = new SecurityConfig(resource.getResourceName());
  34. if(resourceMap.containsKey(resource.getUrl())){
  35. configAttributes = resourceMap.get(resource.getUrl());
  36. configAttributes.add(configAttribute);
  37. }else{
  38. configAttributes = new ArrayList<ConfigAttribute>() ;
  39. configAttributes.add(configAttribute);
  40. resourceMap.put(resource.getUrl(), configAttributes);
  41. }
  42. }
  43. }
  44. System.out.println("11");
  45. Set<String> set = resourceMap.keySet();
  46. Iterator<String> it = set.iterator();
  47. while(it.hasNext()){
  48. String s = it.next();
  49. System.out.println("key:"+s+"|value:"+resourceMap.get(s));
  50. }
  51. }
  52. /*
  53. * 依据请求的资源地址,获取它所拥有的权限
  54. */
  55. @Override
  56. public Collection<ConfigAttribute> getAttributes(Object obj)
  57. throws IllegalArgumentException {
  58. //获取请求的url地址
  59. String url = ((FilterInvocation)obj).getRequestUrl();
  60. System.out.println("MySecurityMetadataSource:getAttributes()---------------请求地址为:"+url);
  61. Iterator<String> it = resourceMap.keySet().iterator();
  62. while(it.hasNext()){
  63. String _url = it.next();
  64. if(_url.indexOf("?

    ")!=-1){

  65. _url = _url.substring(0, _url.indexOf("?"));
  66. }
  67. if(urlMatcher.match(_url,url)){
  68. System.out.println("MySecurityMetadataSource:getAttributes()---------------须要的权限是:"+resourceMap.get(_url));
  69. return resourceMap.get(_url);
  70. }
  71. }
  72. Collection<ConfigAttribute> nouse = new ArrayList<ConfigAttribute>();
  73. nouse.add(new SecurityConfig("无对应权限"));
  74. return nouse;
  75. }
  76. @Override
  77. public boolean supports(Class<?> arg0) {
  78. System.out.println("MySecurityMetadataSource.supports()---------------------");
  79. return true;
  80. }
  81. }

到这里。我们另一个疑问。就是decide方法中的authentication对象(authentication.getAuthorities()包括当前用户拥有的权限),用户的相应角色和权限信息是从哪里获得的?事实上这里是通过调用MyUserDetailsServiceImpl来获取的,该类须要实现UserDetailService接口。更详细一些实际上是通过loadUserByUsername进行获取用户权限信息的,这里注意返回的User不是我们自定义的PO,而是ss3框架中的User。

(这里说下为什么我自己的UserPO没有继承ss的User,就是由于User没有默认无參构造方法。导致mybatis无法创建对象,详细可能还是有办法的,比方重写mybatis的相关接口,比較麻烦。所以这里是先通过返回我们自己的UserPO后,再组装成ss须要的User对象进行的)这里在回到刚才AccessDescisionManager中的decide方法想一下。authentication.getAuthorities()事实上获得的就是以下的Collection<GrantedAuthority>类型的对象。

最后以下的这段代码。我没有直接从username中直接获得resource,而是通过先获得role。再通过role获取resource。我感觉这样方便一些,sql也简单,当然有更好的能够替换掉。

[java] view
plain
 copy

  1. @Component("myUserDetailsServiceImpl")
  2. public class MyUserDetailsServiceImpl implements UserDetailsService{
  3. @Resource
  4. private UserMapper userMapper;
  5. @Override
  6. public UserDetails loadUserByUsername(String username)
  7. throws UsernameNotFoundException {
  8. System.out.println("username is " + username);
  9. UserPO user = userMapper.getUserByUserName(username);
  10. if(user == null) {
  11. throw new UsernameNotFoundException(username);
  12. }
  13. Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(user);
  14. System.out.println(user.getUsername());
  15. return new User(
  16. user.getUsername(),
  17. user.getPassword(),
  18. true,
  19. true,
  20. true,
  21. true,
  22. grantedAuths);
  23. }
  24. //取得用户的权限
  25. private Set<GrantedAuthority> obtionGrantedAuthorities(UserPO user) {
  26. Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
  27. List<RolePO> roles = user.getRoles();
  28. for(RolePO role : roles) {
  29. RolePO innerRole = userMapper.getRoleByRoleName(role.getRoleName());
  30. List<ResourcePO> tempRes = innerRole.getResources();
  31. for(ResourcePO res : tempRes) {
  32. authSet.add(new GrantedAuthorityImpl(res.getResourceName()));
  33. }
  34. }
  35. return authSet;
  36. }
  37. }

到这里,全部的权限-角色-用户信息已经能够串起来了。

再来梳理一下流程,启动server时,通过FilterInvocationSecurityMetadataSource获得用户的全部角色及权限信息。当用户登陆时,通过MyUserDetailsServiceImpl中的loadUserByUsername获得该登陆用户全部的权限,发出请求时。通过FilterInvocationSecurityMetadataSource的getAttributes(Object
url)获得须要的权限名,最后在AccessDecisionManager中decide方法进行对照。假设用户拥有的权限名称和该url须要的权限名同样。那么放行,否则认证失败。清楚这些后,我们还须要一个filter,把上述流程串起来。就像提葡萄一样~

[java] view
plain
 copy

  1. /**
  2. * @description 一个自己定义的filter,
  3. *  必须包括authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
  4. 我们的全部控制将在这三个类中实现
  5. */
  6. public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
  7. private FilterInvocationSecurityMetadataSource fisMetadataSource;
  8. /* (non-Javadoc)
  9. * @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#getSecureObjectClass()
  10. */
  11. @Override
  12. public Class<?

    > getSecureObjectClass() {

  13. return FilterInvocation.class;
  14. }
  15. @Override
  16. public SecurityMetadataSource obtainSecurityMetadataSource() {
  17. return fisMetadataSource;
  18. }
  19. @Override
  20. public void destroy() {}
  21. @Override
  22. public void doFilter(ServletRequest request, ServletResponse response,
  23. FilterChain chain) throws IOException, ServletException {
  24. System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------開始拦截器了....");
  25. FilterInvocation fi = new FilterInvocation(request, response, chain);
  26. InterceptorStatusToken token = super.beforeInvocation(fi);
  27. try {
  28. fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }finally{
  32. super.afterInvocation(token,null);
  33. }
  34. System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....");
  35. }
  36. @Override
  37. public void init(FilterConfig config) throws ServletException {
  38. }
  39. public void setFisMetadataSource(
  40. FilterInvocationSecurityMetadataSource fisMetadataSource) {
  41. this.fisMetadataSource = fisMetadataSource;
  42. }
  43. public FilterInvocationSecurityMetadataSource getFisMetadataSource() {
  44. return fisMetadataSource;
  45. }
  46. }

假设所有照搬上边的代码,到这里就已经结束了。

可是昨天晚上遇到一个大坑,就是发现假设我在数据库中配置了该用户的相关权限url后,用户能够訪问。假设用户没有该url的权限。该用户依旧能够訪问url,这是让我无比惊讶,由于大部分都是參考网络的资料写的,后来看了一下ss的源代码,才发现可能是其它人写错了。这里简单说一下,由于单位电脑没有ss的源代码,主要问题出在MyFilterSecurityInterceptor中的doFilter方法:InterceptorStatusToken token = super.beforeInvocation(fi);  当ss在未匹配到url的权限时,即MySecurityMetadataSource中的getAttributes返回的对象为空时。该方法beforeInvocation直接return
null。而实际decide方法在下方并未执行。

[java] view
plain
 copy

  1. protected InterceptorStatusToken beforeInvocation(Object object) {
  2. if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
  3. .....
  4. }
  5. Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
  6. if (attributes == null || attributes.isEmpty()) {//此处推断MySecurityMetadataSource中的getAttributes返回的对象
  7. if (rejectPublicInvocations) {
  8. throw new IllegalArgumentException("Secure object invocation " + object +
  9. " was denied as public invocations are not allowed via this interceptor. "
  10. + "This indicates a configuration error because the "
  11. + "rejectPublicInvocations property is set to 'true'");
  12. }
  13. if (debug) {
  14. logger.debug("Public object - authentication not attempted");
  15. }
  16. publishEvent(new PublicInvocationEvent(object));
  17. return null; // no further work post-invocation
  18. }
  19. if (debug) {
  20. logger.debug("Secure object: " + object + "; Attributes: " + attributes);
  21. }
  22. if (SecurityContextHolder.getContext().getAuthentication() == null) {
  23. credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
  24. "An Authentication object was not found in the SecurityContext"), object, attributes);
  25. }
  26. Authentication authenticated = authenticateIfRequired();//实际执行decide方法的地方
  27. // Attempt authorization
  28. try {
  29. this.accessDecisionManager.decide(authenticated, object, attributes);
  30. }
  31. catch (AccessDeniedException accessDeniedException) {
  32. publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));
  33. throw accessDeniedException;
  34. }
  35. if (debug) {
  36. logger.debug("Authorization successful");
  37. }
  38. if (publishAuthorizationSuccess) {
  39. publishEvent(new AuthorizedEvent(object, attributes, authenticated));
  40. }
  41. // Attempt to run as a different user
  42. Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
  43. if (runAs == null) {
  44. if (debug) {
  45. logger.debug("RunAsManager did not change Authentication object");
  46. }
  47. // no further work post-invocation
  48. return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
  49. } else {
  50. if (debug) {
  51. logger.debug("Switching to RunAs Authentication: " + runAs);
  52. }
  53. SecurityContext origCtx = SecurityContextHolder.getContext();
  54. SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
  55. SecurityContextHolder.getContext().setAuthentication(runAs);
  56. // need to revert to token.Authenticated post-invocation
  57. return new InterceptorStatusToken(origCtx, true, attributes, object);
  58. }
  59. }

在我看的所有BLOG中,当匹配不到时,所有返回了Null,而当我追到 super.beforeInvocation(fi)源代码中时,发现当getAttributes返回null后。ss就会跳过AccessDecisionManager的decide方法,导致未进行推断!

从而ss会让用户请求顺利的通过。之后,查了一下ss官方英文文档,例如以下描写叙述:

Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException
Accesses the ConfigAttributes that apply to a given secure object.
Parameters:
object - the object being secured
Returns:
the attributes that apply to the passed in secured object. Should return an empty collection if there are no applicable attributes.
Throws:

is-external=true" title="class or interface in java.lang" style="color:rgb(76,107,135); text-decoration:none">IllegalArgumentException -
if the passed object is not of a type supported by the SecurityMetadataSource implementation

红色标出了,应当返回一个空的对象集合假设没有对应权限的时候。而其它blog文返回的是null,导致兴许跳过了decide方法!

所以我在MySecurityMetadataSource中的getAttributes中写的是:

[java] view
plain
 copy

  1. Collection<ConfigAttribute> nouse = new ArrayList<ConfigAttribute>();
  2. nouse.add(new SecurityConfig("无对应权限"));
  3. return nouse;

这样当没有权限时。才干够正常拦截。如今博文抄来抄去,正确的还好,但凡有错误。

真是坑死人。

SpringSecurity3.2.5自己定义角色及权限的教程的更多相关文章

  1. SpringSecurity 自定义用户 角色 资源权限控制

    SpringSecurity 自定义用户 角色 资源权限控制 package com.joyen.learning.security; import java.sql.ResultSet; impor ...

  2. SQL Server 数据库的安全管理(登录、角色、权限)

    ---数据库的安全管理 --登录:SQL Server数据库服务器登录的身份验证模式:1)Windows身份验证.2)Windows和SQL Server混合验证 --角色:分类:1)服务器角色.服务 ...

  3. ASP.NET MVC 基于角色的权限控制系统的示例教程

    上一次在 .NET MVC 用户权限管理示例教程中讲解了ASP.NET MVC 通过AuthorizeAttribute类的OnAuthorization方法讲解了粗粒度控制权限的方法,接下来讲解基于 ...

  4. Oracle用户及角色的权限管理[Oracle基础]

    1.查看全部用户:   select * from dba_users;   select * from all_users;   select * from user_users; 2.查看用户或角 ...

  5. RBAC(Role-Based Access Control,基于角色的权限访问控制)—权限管理设计

    RBAC模型的核心是在用户和权限之间引入了角色的概念,将用户和权限进行解耦,采用用户确定角色,角色分配权限,进而间接达到给用户分配角色的目的 这样采用的方式优点在于 (1)降低管理成本--由于一个角色 ...

  6. IdentityServer4实战 - 基于角色的权限控制及Claim详解

    一.前言 大家好,许久没有更新博客了,最近从重庆来到了成都,换了个工作环境,前面都比较忙没有什么时间,这次趁着清明假期有时间,又可以分享一些知识给大家.在QQ群里有许多人都问过IdentityServ ...

  7. C 实现基于角色的权限系统

    本文demo下载地址:http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=1068 实例使用C# 实现基于角色的权限 ...

  8. shiro 角色与权限的解读

    1.为什么 shiro 有了<角色>后,还要设置<角色权限>呢?(问题) 思考:设置好角色了,那么就代表什么操作都可以执行了吗? 理解:如果上边回答是的话,那么只是<角色 ...

  9. PostgreSQL学习手册(角色和权限)

    原文地址:http://www.cnblogs.com/stephen-liu74/archive/2012/05/18/2302639.html PostgreSQL是通过角色来管理数据库访问权限的 ...

随机推荐

  1. SSO单点登录学习总结(1)——单点登录(SSO)原理解析

    SSO的概念: 单点登录SSO(Single Sign-On)是身份管理中的一部分.SSO的一种较为通俗的定义是:SSO是指访问同一服务器不同应用中的受保护资源的同一用户,只需要登录一次,即通过一个应 ...

  2. 用实力让情怀落地!阅兵前线指挥车同款电视TCL&#160;H8800受捧

        近期.一则重磅消息刷爆了平面媒体.微博.朋友圈等各个传播渠道:TCL曲面电视H8800正式入驻大阅兵前线指挥车以及国旗护卫队荣誉室.宣告代表眼下中国彩电业最高技术水准的曲面电视,正式走上大阅兵 ...

  3. win7打不开chm格式文件

           近期在开发的过程中,发现重装的系统Wind7 打不开java帮助文档.搜索了半天才找到. 在这里分享一下. 一.假设不能打开,可这样恢复文件关联: 1.開始执行,输入:regsvr32 ...

  4. 7.zookeeper集群搭建(windows环境下)

    转自:https://www.cnblogs.com/xuxiuxiu/p/5868481.html 本次zk测试部署版本为3.4.6版本,下载地址http://mirrors.cnnic.cn/ap ...

  5. 在同一个局域网下实时访问vue项目,移动端也可以。

    之前,UI看做好的页面效果,一直都是把项目打包好后放上服务器跑. 现在,UI能实时看到我的项目的页面效果情况了. 那么问题来了!!! 要怎样做到呢??? 接下来照做就行了!!! 请您先打开项目下的pa ...

  6. Flask项目之手机端租房网站的实战开发(十)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...

  7. COGS——C2274. [HEOI 2016] tree

    http://www.cogs.pro/cogs/problem/problem.php?pid=2274 ★☆   输入文件:heoi2016_tree.in   输出文件:heoi2016_tre ...

  8. POJ 2236 Wireless Network ||POJ 1703 Find them, Catch them 并查集

    POJ 2236 Wireless Network http://poj.org/problem?id=2236 题目大意: 给你N台损坏的电脑坐标,这些电脑只能与不超过距离d的电脑通信,但如果x和y ...

  9. (转)windows 下 Java 及 Python 环境变量设置

    转自:http://www.cnblogs.com/zhj5chengfeng/archive/2013/01/01/2841253.html http://www.cnblogs.com/qiyes ...

  10. android开发-获取wifi列表

    近期博主在学frangment框架,因此想着想着就想通过listfragment完毕对wifi列表的获取. 好! 如今就不说废话了. 一.wifi的基础知识 在Android的官方文档中定义了例如以下 ...