spring security 3.0 實現認證與授權

先看一下spring security 官方對以下幾個類或接口的解釋,因爲這個幾個類在程序中會使用到;
ConfigAttribute:Stores a security system related configuration attribute.
SecurityConfig:ConfigAttribute的實現類。
GrantedAuthority:Represents an authority granted to an Authentication object.
GrantedAuthorityImpl:GrantedAuthority的實現類。
UserDetails:Provides core user information.
Authentication:Represents the token for an authentication request or for an authenticated principal once the request has been processed by the AuthenticationManager.authenticate(Authentication) method.
UserDetailsService:Core interface which loads user-specific data.
FilterInvocationSecurityMetadataSource:Marker interface for SecurityMetadataSource implementations that are designed to perform lookups keyed on FilterInvocations.
AccessDecisionManager:Makes a final access control (authorization) decision.

 

定義四張表:用戶表、角色表、資源表、組織機構表(可選)

 

首先需要在web.xml文件中添加以下配置:

Web.xml代碼  收藏代碼
  1. <filter>  
  2.     <filter-name>springSecurityFilterChain</filter-name>  
  3.     <filter-class>  
  4.         org.springframework.web.filter.DelegatingFilterProxy  
  5.     </filter-class>  
  6. </filter>  
  7. <filter-mapping>  
  8.     <filter-name>springSecurityFilterChain</filter-name>  
  9.     <url-pattern>/*</url-pattern>  
  10. </filter-mapping>  

 

接着配置spring security的配置文件:

Xml代碼  收藏代碼
  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.     xsi:schemaLocation ="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.            http://www.springframework.org/schema/security  
  8.            http://www.springframework.org/schema/security/spring-security-3.0.xsd">   
  9.   
  10.     <!-- access-denied-page:定義沒有權限時跳轉的頁面 ;use-expressions:true表示使用表達式定義忽略攔截資源列表-->   
  11.     < http   auto-config = "true"   access-denied-page = "/accessDenied.do"   use-expressions = "true" >   
  12.         <!-- 忽略攔截資源定義 -->   
  13.         < intercept-url   pattern = "/showLogin.do*"   filters = "none"   />   
  14.         < intercept-url   pattern = "/scripts/**"   filters = "none"   />   
  15.         < intercept-url   pattern = "/images/**"   filters = "none"   />   
  16.         < intercept-url   pattern = "/styles/**"   filters = "none"   />   
  17.         < intercept-url   pattern = "/dwr/**"   filters = "none"   />   
  18.         <!-- isAuthenticated()表示只有通過驗證的用戶才能訪問 -->   
  19.         < intercept-url   pattern = "/**"   access = "isAuthenticated()"   />   
  20.         < intercept-url   pattern = "/main.do*"   access = "isAuthenticated()"   />   
  21.           
  22.         <!-- max-sessions=1:禁止2次登陸;ession-fixation-protection="none":防止僞造sessionid攻擊,用戶登錄成功後會銷燬用戶當前的session,-->   
  23.         <!-- 創建新的session並把用戶信息複製到新session中 ;invalid-session-url:定義session超時跳轉頁面 -->   
  24.          < session-management   invalid-session-url = "/timeout.jsp"   session-fixation-protection = "none" >   
  25.             < concurrency-control   max-sessions = "1"   />     
  26.         </ session-management >   
  27.           
  28.         <!-- 表單驗證 -->   
  29.         < form-login   login-page = "/showLogin.do"   authentication-failure-url = "/showLogin.do?error=true"   always-use-default-target = "true"   default-target-url = "/login.do?error=false"   />   
  30.         <!-- 退出系統跳轉頁面 -->    
  31.         < logout   invalidate-session = "true"   logout-success-url = "/showLogin.do"   />   
  32.         <!-- 免登陸驗證 -->   
  33.         < remember-me   />   
  34.         <!-- 自定義攔截器(這裏和spring security2的配置有點不同) -->   
  35.         < custom-filter   before = "FILTER_SECURITY_INTERCEPTOR"   ref = "securityFilter"   />   
  36.     </ http >   
  37.       
  38.     < beans:bean   id = "securityFilter"   class = "xxx.xxx.xxx.commons.permissionengine.web.security.FilterSecurityInterceptor" >   
  39.         < beans:property   name = "authenticationManager"   ref = "authenticationManager"   />   
  40.         < beans:property   name = "accessDecisionManager"   ref = "securityAccessDecisionManager"   />   
  41.         < beans:property   name = "securityMetadataSource"   ref = "securityMetadataSource"   />   
  42.     </ beans:bean >   
  43.       
  44.     < authentication-manager   alias = "authenticationManager" >   
  45.         < authentication-provider   user-service-ref = "securityUserDetailService" >   
  46.             <!-- 密碼採用MD5算法加密 -->   
  47.             < password-encoder   hash = "md5"   />   
  48.         </ authentication-provider >   
  49.     </ authentication-manager >   
  50.       
  51.     <!-- 用戶擁有的權限 -->   
  52.     < beans:bean   id = "securityUserDetailService"   class = "xxx.xxx.xxx.commons.permissionengine.web.security.SecurityUserDetailService"   />   
  53.     <!-- 用戶是否擁有所請求資源的權限 -->   
  54.     < beans:bean   id = "securityAccessDecisionManager"   class = "xxx.xxx.xxx.commons.permissionengine.web.security.SecurityAccessDecisionManager"   />   
  55.     <!-- 資源與權限對應關係 -->   
  56.     < beans:bean   id = "securityMetadataSource"   class = "xxx.xxx.xxx.commons.permissionengine.web.security.SecurityMetadataSource"   />   
  57. </ beans:beans >   

 

接着需要寫一個類實現UserDetails接口,這個並不是我們系統的用戶,它只是一個VO,
用於保存從spring security上下文環境中獲取到登錄用戶,因爲從spring security上下文環境中獲取登錄用戶的返回值就是UserDetails的實現類:

Java代碼  收藏代碼
  1. package  xxx.xxx.xxx.commons.permissionengine.entity;  
  2.   
  3. import  java.util.Collection;  
  4.   
  5. import  org.springframework.security.core.GrantedAuthority;  
  6. import  org.springframework.security.core.userdetails.UserDetails;  
  7.   
  8. public   class  UserDetailInfo  implements  UserDetails {  
  9.   
  10.     private   static   final   long  serialVersionUID = 6137804370301413132L;  
  11.       
  12.     private  String userName;  
  13.       
  14.     private  String password;  
  15.       
  16.     private  Collection<GrantedAuthority> authorities;  
  17.       
  18.     public  UserDetailInfo() {}  
  19.   
  20.     @Override   
  21.     public  Collection<GrantedAuthority> getAuthorities() {  
  22.         return  authorities;  
  23.     }  
  24.   
  25.     @Override   
  26.     public  String getPassword() {  
  27.         return  password;  
  28.     }  
  29.   
  30.     @Override   
  31.     public  String getUsername() {  
  32.         return  userName;  
  33.     }  
  34.   
  35.     @Override   
  36.     public   boolean  isAccountNonExpired() {  
  37.         return   true ;  
  38.     }  
  39.   
  40.     @Override   
  41.     public   boolean  isAccountNonLocked() {  
  42.         return   true ;  
  43.     }  
  44.   
  45.     @Override   
  46.     public   boolean  isCredentialsNonExpired() {  
  47.         return   true ;  
  48.     }  
  49.   
  50.     @Override   
  51.     public   boolean  isEnabled() {  
  52.         return   true ;  
  53.     }  
  54.   
  55.     public  String getUserName() {  
  56.         return  userName;  
  57.     }  
  58.   
  59.     public   void  setUserName(String userName) {  
  60.         this .userName = userName;  
  61.     }  
  62.   
  63.     public   void  setPassword(String password) {  
  64.         this .password = password;  
  65.     }  
  66.   
  67.     public   void  setAuthorities(Collection<GrantedAuthority> authorities) {  
  68.         this .authorities = authorities;  
  69.     }  
  70. }  

 

接着自定義一個繼承了AbstractSecurityInterceptor的filter:

Java代碼  收藏代碼
  1. package  xxx.xxx.xxx.commons.permissionengine.web.security;  
  2.   
  3. import  java.io.IOException;  
  4.   
  5. import  javax.servlet.Filter;  
  6. import  javax.servlet.FilterChain;  
  7. import  javax.servlet.FilterConfig;  
  8. import  javax.servlet.ServletException;  
  9. import  javax.servlet.ServletRequest;  
  10. import  javax.servlet.ServletResponse;  
  11.   
  12. import  org.springframework.security.access.SecurityMetadataSource;  
  13. import  org.springframework.security.access.intercept.AbstractSecurityInterceptor;  
  14. import  org.springframework.security.access.intercept.InterceptorStatusToken;  
  15. import  org.springframework.security.web.FilterInvocation;  
  16. import  org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
  17.   
  18. public   class  FilterSecurityInterceptor  extends  AbstractSecurityInterceptor  implements  Filter {  
  19.       
  20.     private  FilterInvocationSecurityMetadataSource securityMetadataSource;  
  21.   
  22.     @Override   
  23.     public   void  destroy() {  
  24.         // TODO Auto-generated method stub   
  25.     }  
  26.   
  27.     @Override   
  28.     public   void  doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)  throws  IOException, ServletException {  
  29.         FilterInvocation invocation = new  FilterInvocation(request, response, filterChain);  
  30.         invoke(invocation);  
  31.     }  
  32.       
  33.     public   void  invoke(FilterInvocation filterInvocation)  throws  IOException, ServletException {  
  34.         InterceptorStatusToken token = super .beforeInvocation(filterInvocation);  
  35.         try  {  
  36.             filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());  
  37.         } finally  {  
  38.             super .afterInvocation(token,  null );  
  39.         }  
  40.     }  
  41.   
  42.     @Override   
  43.     public   void  init(FilterConfig arg0)  throws  ServletException {  
  44.         // TODO Auto-generated method stub   
  45.     }  
  46.   
  47.     @Override   
  48.     public  Class<?  extends  Object> getSecureObjectClass() {  
  49.         return  FilterInvocation. class ;  
  50.     }  
  51.   
  52.     @Override   
  53.     public  SecurityMetadataSource obtainSecurityMetadataSource() {  
  54.         return   this .securityMetadataSource;  
  55.     }  
  56.   
  57.     public   void  setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {  
  58.         this .securityMetadataSource = securityMetadataSource;  
  59.     }  
  60.   
  61.     public  FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {  
  62.         return   this .securityMetadataSource;  
  63.     }  
  64. }  

 

接着定義一個類,實現了UserDetailsService接口,主要用於獲取登錄用戶信息和用戶所具有的角色

Java代碼  收藏代碼
  1. package  xxx.xxx.xxx.commons.permissionengine.web.security;  
  2.   
  3.   
  4. import  java.util.ArrayList;  
  5. import  java.util.Collection;  
  6. import  java.util.Set;  
  7.   
  8. import  javax.annotation.Resource;  
  9.   
  10. import  org.springframework.dao.DataAccessException;  
  11. import  org.springframework.security.core.GrantedAuthority;  
  12. import  org.springframework.security.core.authority.GrantedAuthorityImpl;  
  13. import  org.springframework.security.core.userdetails.UserDetails;  
  14. import  org.springframework.security.core.userdetails.UserDetailsService;  
  15. import  org.springframework.security.core.userdetails.UsernameNotFoundException;  
  16.   
  17. import  xxx.xxx.xxx.commons.permissionengine.entity.Role;  
  18. import  xxx.xxx.xxx.commons.permissionengine.entity.User;  
  19. import  xxx.xxx.xxx.commons.permissionengine.entity.UserDetailInfo;  
  20. import  xxx.xxx.xxx.commons.permissionengine.service.IUserService;  
  21.   
  22. public   class  SecurityUserDetailService  implements  UserDetailsService {  
  23.   
  24.     private  IUserService userService;  
  25.       
  26.     /**  
  27.      * 獲取登錄用戶信息並添加到security上下文環境  
  28.      */   
  29.     @Override   
  30.     public  UserDetails loadUserByUsername(String name) throws  UsernameNotFoundException, DataAccessException {  
  31.         //定義存放用戶角色信息的集合   
  32.         Collection<GrantedAuthority> authorities = new  ArrayList<GrantedAuthority>();  
  33.         //通過已經經過驗證的登錄用戶的用戶名查找登錄用戶信息   
  34.         User user = userService.findUserByProperty("name" , name);  
  35.         //定義一個userDetailInfo對象,該類實現了spring security UserDetails接口,因爲已經經過驗證的登錄用戶會保持在   
  36.         //spring security上下文環境中,通過該上下文環境獲取登錄用戶信息返回的是UserDetails類型,因此需要定義一個類實現該接口   
  37.         UserDetailInfo userDetailInfo = null ;  
  38.         if (user !=  null ) {  
  39.             //獲取登錄用戶信息的角色列表   
  40.             Set<Role> roles = user.getRoles();  
  41.             for (Role role : roles) {  
  42.                 //利用角色用戶具有的角色的編號構造一個GrantedAuthority對象,並把該對象添加到集合中   
  43.                 GrantedAuthorityImpl grantedAuthorityImpl = new  GrantedAuthorityImpl( "ROLE_" +role.getNo());  
  44.                 authorities.add(grantedAuthorityImpl);  
  45.             }  
  46.             userDetailInfo = new  UserDetailInfo();  
  47.             userDetailInfo.setUserName(user.getName());  
  48.             userDetailInfo.setPassword(user.getPassword());  
  49.             //把角色信息添加到userDetailInfo對象中   
  50.             userDetailInfo.setAuthorities(authorities);  
  51.         }  
  52.         return  userDetailInfo;  
  53.     }  
  54.       
  55.     @Resource (name =  "userService" )  
  56.     public   void  setUserService(IUserService userService) {  
  57.         this .userService = userService;  
  58.     }  
  59. }  

 

再接着定義一個實現了FilterInvocationSecurityMetadataSource的類:

Java代碼  收藏代碼
  1. package  xxx.xxx.xxx.commons.permissionengine.web.security;  
  2.   
  3. import  java.util.ArrayList;  
  4. import  java.util.Collection;  
  5. import  java.util.HashMap;  
  6. import  java.util.Iterator;  
  7. import  java.util.Map;  
  8.   
  9. import  javax.annotation.Resource;  
  10.   
  11. import  org.springframework.security.access.ConfigAttribute;  
  12. import  org.springframework.security.access.SecurityConfig;  
  13. import  org.springframework.security.web.FilterInvocation;  
  14. import  org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
  15. import  org.springframework.security.web.util.AntUrlPathMatcher;  
  16. import  org.springframework.security.web.util.UrlMatcher;  
  17.   
  18. import  xxx.xxx.xxx.commons.permissionengine.service.IMenuService;  
  19.   
  20. /**  
  21.  * 資源源數據管理器  
  22.  * @author Keven  
  23.  *  
  24.  */   
  25. public   class  SecurityMetadataSource  implements  FilterInvocationSecurityMetadataSource {  
  26.       
  27.     private  IMenuService menuService;  
  28.     //定義一個url匹配工具類   
  29.     private  UrlMatcher urlMatcher =  new  AntUrlPathMatcher();  
  30.       
  31.     private   static  Map<String, Collection<ConfigAttribute>> menuMap =  null ;  
  32.       
  33.     //該構造方法由spring容器調用   
  34.     public  SecurityMetadataSource() {  
  35.         loadMenuDefine();  
  36.     }  
  37.       
  38.     private   void  loadMenuDefine() {  
  39.         menuMap = new  HashMap<String, Collection<ConfigAttribute>>();  
  40.         //初始化匿名用戶所擁有的權限   
  41.         Collection<ConfigAttribute> guestAtts = new  ArrayList<ConfigAttribute>();  
  42.         ConfigAttribute guestAttribute = new  SecurityConfig( "ROLE_Guest" );  
  43.         guestAtts.add(guestAttribute);  
  44.         menuMap.put("/showLogin.do*" , guestAtts);  
  45.     }  
  46.   
  47.     @Override   
  48.     public  Collection<ConfigAttribute> getAllConfigAttributes() {  
  49.         return   null ;  
  50.     }  
  51.   
  52.     @Override   
  53.     public  Collection<ConfigAttribute> getAttributes(Object object)  throws  IllegalArgumentException {  
  54.         //獲取請求url   
  55.         String url = ((FilterInvocation)object).getRequestUrl();  
  56.         //從數據庫獲取資源與角色的對應關係,並設置初始化的資源_角色到該Map   
  57.         menuService.setMenuMap(menuMap);  
  58.         //獲取資源列表   
  59.         Iterator<String> iter = menuMap.keySet().iterator();  
  60.         while (iter.hasNext()) {  
  61.             String menuUrl = iter.next();  
  62.             //防止把null值加入到map,報空指針異常   
  63.             if (menuUrl !=  null ) {  
  64.                 //請求url與角色所擁有的權限做匹配   
  65.                 if (urlMatcher.pathMatchesUrl(menuUrl, url))  
  66.                     return  menuMap.get(menuUrl);  
  67.             }  
  68.         }  
  69.         return   null ;  
  70.     }  
  71.   
  72.     @Override   
  73.     public   boolean  supports(Class<?> clazz) {  
  74.         return   true ;  
  75.     }  
  76.   
  77.     @Resource (name =  "menuService" )  
  78.     public   void  setMenuService(IMenuService menuService) {  
  79.         this .menuService = menuService;  
  80.     }  
  81. }  

 

再接着定義一個實現了AccessDecisionManager的類:

Java代碼  收藏代碼
  1. package  xxx.xxx.xxx.commons.permissionengine.web.security;  
  2.   
  3. import  java.util.Collection;  
  4. import  java.util.Iterator;  
  5.   
  6. import  org.springframework.security.access.AccessDecisionManager;  
  7. import  org.springframework.security.access.AccessDeniedException;  
  8. import  org.springframework.security.access.ConfigAttribute;  
  9. import  org.springframework.security.access.SecurityConfig;  
  10. import  org.springframework.security.authentication.InsufficientAuthenticationException;  
  11. import  org.springframework.security.core.Authentication;  
  12. import  org.springframework.security.core.GrantedAuthority;  
  13.   
  14. /**  
  15.  * 決策管理器,用於判斷用戶需要訪問的資源與用戶所擁有的角色是否匹配  
  16.  * @author Keven  
  17.  *  
  18.  */   
  19. public   class  SecurityAccessDecisionManager  implements  AccessDecisionManager {  
  20.   
  21.     @Override   
  22.     public   void  decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)  throws  AccessDeniedException, InsufficientAuthenticationException {  
  23.         if (configAttributes ==  null )  
  24.             return ;  
  25.         //獲取資源與角色對應關係列表   
  26.         Iterator<ConfigAttribute> iter = configAttributes.iterator();  
  27.         while (iter.hasNext()) {  
  28.             ConfigAttribute configAttribute = iter.next();  
  29.             //獲取訪問該資源需要的角色   
  30.             String needRole = ((SecurityConfig)configAttribute).getAttribute();  
  31.             //從上下文環境獲取用戶所具有的角色   
  32.             for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {  
  33.                 //判斷用戶擁有的角色是否與訪問該資源所需要的角色匹配   
  34.                 if (needRole.equals(grantedAuthority.getAuthority()))  
  35.                     return ;  
  36.             }  
  37.         }  
  38.         throw   new  AccessDeniedException( "權限不足!" );  
  39.     }  
  40.   
  41.     @Override   
  42.     public   boolean  supports(ConfigAttribute arg0) {  
  43.         return   true ;  
  44.     }  
  45.   
  46.     @Override   
  47.     public   boolean  supports(Class<?> arg0) {  
  48.         return   true ;  
  49.     }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章