海水正藍

          面朝大海,春暖花開
          posts - 145, comments - 29, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          Spring Security3.1 最新配置實例

          Posted on 2013-08-13 17:49 小胡子 閱讀(328) 評論(0)  編輯  收藏 所屬分類: Spring Security

          這幾天學習了一下Spring Security3.1,從官網下載了Spring Security3.1版本進行練習,經過多次嘗試才摸清了其中的一些原理。本人不才,希望能幫助大家。還有,這次我第二次寫博客啊,文體不是很行。希望 能讓觀看者不產生疲憊的感覺,我已經心滿意足了。

          一、數據庫結構

               先來看一下數據庫結構,采用的是基于角色-資源-用戶的權限管理設計。(MySql數據庫)

              為了節省篇章,只對比較重要的字段進行注釋。

              1.用戶表Users

              CREATE TABLE `users` (

                 -- 賬號是否有限 1. 是 0.否
                 `enable` int(11) default NULL,
                 `password` varchar(255) default NULL,
                 `account` varchar(255) default NULL,
                 `id` int(11) NOT NULL auto_increment,
                 PRIMARY KEY  (`id`)
              )

           

             2.角色表Roles

             CREATE TABLE `roles` (
               `enable` int(11) default NULL,
               `name` varchar(255) default NULL,
               `id` int(11) NOT NULL auto_increment,
               PRIMARY KEY  (`id`)
             )

           

             3 用戶_角色表users_roles

             CREATE TABLE `users_roles` (

               --用戶表的外鍵
               `uid` int(11) default NULL,

               --角色表的外鍵
               `rid` int(11) default NULL,
               `urId` int(11) NOT NULL auto_increment,
               PRIMARY KEY  (`urId`),
               KEY `rid` (`rid`),
               KEY `uid` (`uid`),
              CONSTRAINT `users_roles_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`),
              CONSTRAINT `users_roles_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `users` (`id`)
             )

           

             4.資源表resources

             CREATE TABLE `resources` (
               `memo` varchar(255) default NULL,

               -- 權限所對應的url地址
               `url` varchar(255) default NULL,

               --優先權
               `priority` int(11) default NULL,

               --類型
               `type` int(11) default NULL,

               --權限所對應的編碼,例201代表發表文章
               `name` varchar(255) default NULL,
               `id` int(11) NOT NULL auto_increment,
               PRIMARY KEY  (`id`)
             )

           

             5.角色_資源表roles_resources

              CREATE TABLE `roles_resources` (
                `rsid` int(11) default NULL,
                `rid` int(11) default NULL,
                `rrId` int(11) NOT NULL auto_increment,
                PRIMARY KEY  (`rrId`),
                KEY `rid` (`rid`),
                KEY `roles_resources_ibfk_2` (`rsid`),
                CONSTRAINT `roles_resources_ibfk_2` FOREIGN KEY (`rsid`) REFERENCES `resources` (`id`),
                CONSTRAINT `roles_resources_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`)
                )

           

            二、系統配置

             所需要的jar包,請自行到官網下 載,我用的是Spring Security3.1.0.RC1版的。把dist下的除了源碼件包導入就行了。還有那些零零碎的   數據庫驅動啊,log4j.jar等等,我相信在用Spring Security之前,大家已經會的了。

            1) web.xml


          <!-- Spring -->
            
          <context-param>
              
          <param-name>contextConfigLocation</param-name>
              
          <param-value>classpath:applicationContext.xml,classpath:applicationContext-security.xml</param-value>
            
          </context-param>
            
              
            
          <listener>
              
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            
          </listener>
            
          <!-- 權限 -->
            
          <filter>
                  
          <filter-name>springSecurityFilterChain</filter-name>
                  
          <filter-class>
                      org.springframework.web.filter.DelegatingFilterProxy
                  
          </filter-class>
             
          </filter>
              
          <filter-mapping>
                  
          <filter-name>springSecurityFilterChain</filter-name>
                  
          <url-pattern>/*</url-pattern>
              </filter-mapping>
          這里主要是配置了讓容器啟動的時候加載application-security.xml和Spring Security的權限過濾器代理,讓其過濾所有的客服請求。

           2)application-security.xml


          <?xml version="1.0" encoding="UTF-8"?>
          <beans:beans xmlns="http://www.springframework.org/schema/security"
              xmlns:beans
          ="http://www.springframework.org/schema/beans"
              xmlns:xsi
          ="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation
          ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                                  http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
                                  
              
          <global-method-security pre-post-annotations="enabled" /> 
              
          <!-- 該路徑下的資源不用過濾 -->           
              
          <http pattern="/js/**" security="none"/>
              
          <http use-expressions="true" auto-config="true">
                  
                  
          <form-login />
                  
          <logout/>
                  
          <!-- 實現免登陸驗證 -->
                  
          <remember-me />
                  
          <session-management invalid-session-url="/timeout.jsp">
                      
          <concurrency-control max-sessions="10" error-if-maximum-exceeded="true" />
                  
          </session-management>
                  
          <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
              
          </http>
              
          <!-- 配置過濾器 -->
              
          <beans:bean id="myFilter" class="com.huaxin.security.MySecurityFilter">
                  
          <!-- 用戶擁有的權限 -->
                  
          <beans:property name="authenticationManager" ref="myAuthenticationManager" />
                  
          <!-- 用戶是否擁有所請求資源的權限 -->
                  
          <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />
                  
          <!-- 資源與權限對應關系 -->
                  
          <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />
              
          </beans:bean>
              
          <!-- 實現了UserDetailsService的Bean -->
              
          <authentication-manager alias="myAuthenticationManager">
                  
          <authentication-provider user-service-ref="myUserDetailServiceImpl" />
              
          </authentication-manager>
              
          <beans:bean id="myAccessDecisionManager" class="com.huaxin.security.MyAccessDecisionManager"></beans:bean>
              
          <beans:bean id="mySecurityMetadataSource" class="com.huaxin.security.MySecurityMetadataSource">
                  
          <beans:constructor-arg name="resourcesDao" ref="resourcesDao"></beans:constructor-arg>
              
          </beans:bean>
              
          <beans:bean id="myUserDetailServiceImpl" class="com.huaxin.security.MyUserDetailServiceImpl">
                  
          <beans:property name="usersDao" ref="usersDao"></beans:property>
              
          </beans:bean>
          </beans:beans>

          我們在第二個http標簽下配置一個我們自定義的繼承了org.springframework.security.access.intercept.AbstractSecurityInterceptor的Filter,并注入其

          必須的3個組件authenticationManager、accessDecisionManager和securityMetadataSource。其作用上面已經注釋了。

           

          <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/> 這里的FILTER_SECURITY_INTERCEPTOR是Spring Security默認的Filter,

          我們自定義的Filter必須在它之前,過濾客服請求。接下來看下我們最主要的myFilter吧。

           

          3)myFilter

            (1) MySecurityFilter.java 過濾用戶請求


          public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter {
              
          //與applicationContext-security.xml里的myFilter的屬性securityMetadataSource對應,
              
          //其他的兩個組件,已經在AbstractSecurityInterceptor定義
              private FilterInvocationSecurityMetadataSource securityMetadataSource;

              @Override
              
          public SecurityMetadataSource obtainSecurityMetadataSource() {
                  
          return this.securityMetadataSource;
              }

              
          public void doFilter(ServletRequest request, ServletResponse response,
                      FilterChain chain) 
          throws IOException, ServletException {
                  FilterInvocation fi 
          = new FilterInvocation(request, response, chain);
                  invoke(fi);
              }
              
              
          private void invoke(FilterInvocation fi) throws IOException, ServletException {
                  
          // object為FilterInvocation對象
                            
          //super.beforeInvocation(fi);源碼
                  
          //1.獲取請求資源的權限
                  
          //執行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
                  
          //2.是否擁有權限
                  
          //this.accessDecisionManager.decide(authenticated, object, attributes);
                  InterceptorStatusToken token = super.beforeInvocation(fi);
                  
          try {
                      fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                  } 
          finally {
                      
          super.afterInvocation(token, null);
                  }
              }

              
          public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
                  
          return securityMetadataSource;
              }

              
          public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
                  
          this.securityMetadataSource = securityMetadataSource;
              }
              
              
          public void init(FilterConfig arg0) throws ServletException {
                  
          // TODO Auto-generated method stub
              }
              
              
          public void destroy() {
                  
          // TODO Auto-generated method stub
                  
              }

              @Override
              
          public Class<? extends Object> getSecureObjectClass() {
                  
          //下面的MyAccessDecisionManager的supports方面必須放回true,否則會提醒類型錯誤
                  return FilterInvocation.class;
              }
          }
          核心的InterceptorStatusToken token = super.beforeInvocation(fi);會調用我們定義的accessDecisionManager:decide(Object object)和securityMetadataSource

            :getAttributes(Object object)方法。

           

           (2)MySecurityMetadataSource.java


          //1 加載資源與權限的對應關系
          public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
              
          //由spring調用
              public MySecurityMetadataSource(ResourcesDao resourcesDao) {
                  
          this.resourcesDao = resourcesDao;
                  loadResourceDefine();
              }

              
          private ResourcesDao resourcesDao;
              
          private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

              
          public ResourcesDao getResourcesDao() {
                  
          return resourcesDao;
              }

              
          public void setResourcesDao(ResourcesDao resourcesDao) {
                  
          this.resourcesDao = resourcesDao;
              }

              
          public Collection<ConfigAttribute> getAllConfigAttributes() {
                  
          // TODO Auto-generated method stub
                  return null;
              }

              
          public boolean supports(Class<?> clazz) {
                  
          // TODO Auto-generated method stub
                  return true;
              }
              
          //加載所有資源與權限的關系
              private void loadResourceDefine() {
                  
          if(resourceMap == null) {
                      resourceMap 
          = new HashMap<String, Collection<ConfigAttribute>>();
                      List
          <Resources> resources = this.resourcesDao.findAll();
                      
          for (Resources resource : resources) {
                          Collection
          <ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
                                          
          //以權限名封裝為Spring的security Object
                          ConfigAttribute configAttribute = new SecurityConfig(resource.getName());
                          configAttributes.add(configAttribute);
                          resourceMap.put(resource.getUrl(), configAttributes);
                      }
                  }
                  
                  Set
          <Entry<String, Collection<ConfigAttribute>>> resourceSet = resourceMap.entrySet();
                  Iterator
          <Entry<String, Collection<ConfigAttribute>>> iterator = resourceSet.iterator();
                  
              }
              
          //返回所請求資源所需要的權限
              public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
                  
                  String requestUrl 
          = ((FilterInvocation) object).getRequestUrl();
                  System.out.println(
          "requestUrl is " + requestUrl);
                  
          if(resourceMap == null) {
                      loadResourceDefine();
                  }
                  
          return resourceMap.get(requestUrl);
              }

          }
          這里的resourcesDao,熟悉Dao設計模式和Spring 注入的朋友應該看得明白。

           

          (3)MyUserDetailServiceImpl.java


          public class MyUserDetailServiceImpl implements UserDetailsService {
              
              
          private UsersDao usersDao;
              
          public UsersDao getUsersDao() {
                  
          return usersDao;
              }

              
          public void setUsersDao(UsersDao usersDao) {
                  
          this.usersDao = usersDao;
              }
              
              
          public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                  System.out.println(
          "username is " + username);
                  Users users 
          = this.usersDao.findByName(username);
                  
          if(users == null) {
                      
          throw new UsernameNotFoundException(username);
                  }
                  Collection
          <GrantedAuthority> grantedAuths = obtionGrantedAuthorities(users);
                  
                  
          boolean enables = true;
                  
          boolean accountNonExpired = true;
                  
          boolean credentialsNonExpired = true;
                  
          boolean accountNonLocked = true;
                  
                  User userdetail 
          = new User(users.getAccount(), users.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths);
                  
          return userdetail;
              }
              
              
          //取得用戶的權限
              private Set<GrantedAuthority> obtionGrantedAuthorities(Users user) {
                  Set
          <GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
                  Set
          <Roles> roles = user.getRoles();
                  
                  
          for(Roles role : roles) {
                      Set
          <Resources> tempRes = role.getResources();
                      
          for(Resources res : tempRes) {
                          authSet.add(
          new GrantedAuthorityImpl(res.getName()));
          s            }
                  }
                  
          return authSet;
              }
          }

          (4) MyAccessDecisionManager.java

          public class MyAccessDecisionManager implements AccessDecisionManager {
              
              
          public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
                  
          if(configAttributes == null) {
                      
          return;
                  }
                  
          //所請求的資源擁有的權限(一個資源對多個權限)
                  Iterator<ConfigAttribute> iterator = configAttributes.iterator();
                  
          while(iterator.hasNext()) {
                      ConfigAttribute configAttribute 
          = iterator.next();
                      
          //訪問所請求資源所需要的權限
                      String needPermission = configAttribute.getAttribute();
                      System.out.println(
          "needPermission is " + needPermission);
                      
          //用戶所擁有的權限authentication
                      for(GrantedAuthority ga : authentication.getAuthorities()) {
                          
          if(needPermission.equals(ga.getAuthority())) {
                              
          return;
                          }
                      }
                  }
                  
          //沒有權限
                  throw new AccessDeniedException(" 沒有權限訪問! ");
              }

              
          public boolean supports(ConfigAttribute attribute) {
                  
          // TODO Auto-generated method stub
                  return true;
              }

              
          public boolean supports(Class<?> clazz) {
                  
          // TODO Auto-generated method stub
                  return true;
              }
              
          }

          三、流程

           1)容器啟動(MySecurityMetadataSource:loadResourceDefine加載系統資源與權限列表)
           2)用戶發出請求
           3)過濾器攔截(MySecurityFilter:doFilter)
           4)取得請求資源所需權限(MySecurityMetadataSource:getAttributes)
           5)匹配用戶擁有權限和請求權限(MyAccessDecisionManager:decide),如果用戶沒有相應的權限,

               執行第6步,否則執行第7步。
           6)登錄
           7)驗證并授權(MyUserDetailServiceImpl:loadUserByUsername)
           8)重復4,5

           

          四、結束語

          好了,終于寫完了,回頭看了一下,感覺不是怎么行。等我弄明白Spring Security它的原理之后,再回頭修改下注釋吧。大家覺得不妥的地方,可以留言,我會回復大家的。

          原文出自:
          http://blog.csdn.net/k10509806/article/details/6369131










          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 厦门市| 桂平市| 富源县| 那坡县| 饶平县| 安顺市| 鲜城| 荣成市| 监利县| 措勤县| 灵川县| 无极县| 贵港市| 寻甸| 柳林县| 珠海市| 潞城市| 阿巴嘎旗| 龙井市| 阿克苏市| 曲周县| 疏附县| 京山县| 若羌县| 水富县| 神农架林区| 梅河口市| 新巴尔虎右旗| 封开县| 云林县| 巧家县| 平原县| 五家渠市| 峡江县| 海林市| 故城县| 体育| 精河县| 长子县| 唐山市| 九龙城区|