spring security 3 自定义认证,授权示例

1,建一个web project,并导入所有需要的lib。

2,配置web.xml,使用Spring的机制装载:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"  
  3.      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   
  5.      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">  
  6.     <context-param>  
  7.         <param-name>contextConfigLocation</param-name>  
  8.         <param-value>classpath:applicationContext*.xml</param-value>  
  9.     </context-param>  
  10.   
  11.     <listener>  
  12.         <listener-class>  
  13.              org.springframework.web.context.ContextLoaderListener  
  14.         </listener-class>  
  15.     </listener>  
  16.   
  17.     <filter>  
  18.         <filter-name>springSecurityFilterChain</filter-name>  
  19.         <filter-class>  
  20.              org.springframework.web.filter.DelegatingFilterProxy  
  21.         </filter-class>  
  22.     </filter>  
  23.     <filter-mapping>  
  24.         <filter-name>springSecurityFilterChain</filter-name>  
  25.         <url-pattern>/*</url-pattern>  
  26.     </filter-mapping>  
  27.   
  28.   
  29.     <welcome-file-list>  
  30.         <welcome-file>login.jsp</welcome-file>  
  31.     </welcome-file-list>  
  32. </web-app>  

 这个文件中的内容我相信大家都很熟悉了,不再多说了。

2,来看看applicationContext-security.xml这个配置文件,关于Spring Security的配置均在其中:

  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.     <http access-denied-page="/403.jsp"><!-- 当访问被拒绝时,会转到403.jsp -->  
  11.         <intercept-url pattern="/login.jsp" filters="none" />  
  12.         <form-login login-page="/login.jsp"  
  13.              authentication-failure-url="/login.jsp?error=true"  
  14.              default-target-url="/index.jsp" />  
  15.         <logout logout-success-url="/login.jsp" />  
  16.         <http-basic />  
  17.         <!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->  
  18.         <custom-filter before="FILTER_SECURITY_INTERCEPTOR"  
  19.              ref="myFilter" />  
  20.     </http>  
  21.   
  22.     <!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,  
  23.      我们的所有控制将在这三个类中实现,解释详见具体配置 -->  
  24.     <beans:bean id="myFilter" class="com.robin.erp.fwk.security.MyFilterSecurityInterceptor">  
  25.         <beans:property name="authenticationManager"  
  26.              ref="authenticationManager" />  
  27.         <beans:property name="accessDecisionManager"  
  28.              ref="myAccessDecisionManagerBean" />  
  29.         <beans:property name="securityMetadataSource"  
  30.              ref="securityMetadataSource" />  
  31.     </beans:bean>  
  32.       
  33.     <!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->  
  34.     <authentication-manager alias="authenticationManager">  
  35.         <authentication-provider  
  36.             user-service-ref="myUserDetailService">  
  37.             <!--    如果用户的密码采用加密的话,可以加点“盐”  
  38.                  <password-encoder hash="md5" />  
  39.             -->  
  40.         </authentication-provider>  
  41.     </authentication-manager>  
  42.     <beans:bean id="myUserDetailService"  
  43.          class="com.robin.erp.fwk.security.MyUserDetailService" />  
  44.   
  45.     <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->  
  46.     <beans:bean id="myAccessDecisionManagerBean"  
  47.          class="com.robin.erp.fwk.security.MyAccessDecisionManager">  
  48.     </beans:bean>  
  49.       
  50.     <!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->  
  51.     <beans:bean id="securityMetadataSource"  
  52.          class="com.robin.erp.fwk.security.MyInvocationSecurityMetadataSource" />  
  53.   
  54. </beans:beans>  

 3,来看看自定义filter的实现:

  1. package com.example.spring.security;  
  2. import java.io.IOException;  
  3.   
  4. import javax.servlet.Filter;  
  5. import javax.servlet.FilterChain;  
  6. import javax.servlet.FilterConfig;  
  7. import javax.servlet.ServletException;  
  8. import javax.servlet.ServletRequest;  
  9. import javax.servlet.ServletResponse;  
  10.   
  11. import org.springframework.security.access.SecurityMetadataSource;  
  12. import org.springframework.security.access.intercept.AbstractSecurityInterceptor;  
  13. import org.springframework.security.access.intercept.InterceptorStatusToken;  
  14. import org.springframework.security.web.FilterInvocation;  
  15. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
  16.   
  17. public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor  
  18.         implements Filter {  
  19.   
  20.     private FilterInvocationSecurityMetadataSource securityMetadataSource;  
  21.   
  22.     // ~ Methods  
  23.     // ========================================================================================================  
  24.   
  25.     /** 
  26.       * Method that is actually called by the filter chain. Simply delegates to 
  27.       * the {@link #invoke(FilterInvocation)} method. 
  28.       *  
  29.       * @param request 
  30.       *             the servlet request 
  31.       * @param response 
  32.       *             the servlet response 
  33.       * @param chain 
  34.       *             the filter chain 
  35.       *  
  36.       * @throws IOException 
  37.       *              if the filter chain fails 
  38.       * @throws ServletException 
  39.       *              if the filter chain fails 
  40.      */  
  41.     public void doFilter(ServletRequest request, ServletResponse response,  
  42.              FilterChain chain) throws IOException, ServletException {  
  43.          FilterInvocation fi = new FilterInvocation(request, response, chain);  
  44.          invoke(fi);  
  45.      }  
  46.   
  47.     public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {  
  48.         return this.securityMetadataSource;  
  49.      }  
  50.   
  51.     public Class<? extends Object> getSecureObjectClass() {  
  52.         return FilterInvocation.class;  
  53.      }  
  54.   
  55.     public void invoke(FilterInvocation fi) throws IOException,  
  56.              ServletException {  
  57.          InterceptorStatusToken token = super.beforeInvocation(fi);  
  58.         try {  
  59.              fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  
  60.          } finally {  
  61.             super.afterInvocation(token, null);  
  62.          }  
  63.      }  
  64.   
  65.     public SecurityMetadataSource obtainSecurityMetadataSource() {  
  66.         return this.securityMetadataSource;  
  67.      }  
  68.   
  69.     public void setSecurityMetadataSource(  
  70.              FilterInvocationSecurityMetadataSource newSource) {  
  71.         this.securityMetadataSource = newSource;  
  72.      }  
  73.   
  74.      @Override  
  75.     public void destroy() {  
  76.      }  
  77.   
  78.      @Override  
  79.     public void init(FilterConfig arg0) throws ServletException {  
  80.      }  
  81.   
  82. }  

 最核心的代码就是invoke方法中的InterceptorStatusToken token = super.beforeInvocation(fi);这一句,即在执行doFilter之前,进行权限的检查,而具体的实现已经交给accessDecisionManager了。

4,来看看authentication-provider的实现:

  1. package com.example.spring.security;  
  2. import java.util.ArrayList;  
  3. import java.util.Collection;  
  4.   
  5. import org.springframework.dao.DataAccessException;  
  6. import org.springframework.security.core.GrantedAuthority;  
  7. import org.springframework.security.core.authority.GrantedAuthorityImpl;  
  8. import org.springframework.security.core.userdetails.User;  
  9. import org.springframework.security.core.userdetails.UserDetails;  
  10. import org.springframework.security.core.userdetails.UserDetailsService;  
  11. import org.springframework.security.core.userdetails.UsernameNotFoundException;  
  12.   
  13. public class MyUserDetailService implements UserDetailsService {  
  14.   
  15.      @Override  
  16.     public UserDetails loadUserByUsername(String username)  
  17.             throws UsernameNotFoundException, DataAccessException {  
  18.          Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>();  
  19.          GrantedAuthorityImpl auth2=new GrantedAuthorityImpl("ROLE_ADMIN");  
  20.          auths.add(auth2);  
  21.         if(username.equals("robin1")){  
  22.              auths=new ArrayList<GrantedAuthority>();  
  23.              GrantedAuthorityImpl auth1=new GrantedAuthorityImpl("ROLE_ROBIN");  
  24.              auths.add(auth1);  
  25.          }  
  26.           
  27. //         User(String username, String password, boolean enabled, boolean accountNonExpired,  
  28. //                     boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities) {  
  29.          User user = new User(username,  
  30.                 "robin", true, true, true, true, auths);  
  31.         return user;  
  32.      }  
  33.       
  34. }  

在这个类中,你就可以从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等,我想这么简单的代码就不再多解释了。

5,对于资源的访问权限的定义,我们通过实现FilterInvocationSecurityMetadataSource这个接口来初始化数据。

  1. package com.example.spring.security;  
  2. import java.util.ArrayList;  
  3. import java.util.Collection;  
  4. import java.util.HashMap;  
  5. import java.util.Iterator;  
  6. import java.util.Map;  
  7.   
  8. import org.springframework.security.access.ConfigAttribute;  
  9. import org.springframework.security.access.SecurityConfig;  
  10. import org.springframework.security.web.FilterInvocation;  
  11. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
  12. import org.springframework.security.web.util.AntUrlPathMatcher;  
  13. import org.springframework.security.web.util.UrlMatcher;  
  14. /** 
  15. *  
  16. * 此类在初始化时,应该取到所有资源及其对应角色的定义 
  17. *  
  18. * @author Robin 
  19. *  
  20. */  
  21. public class MyInvocationSecurityMetadataSource  
  22.         implements FilterInvocationSecurityMetadataSource {  
  23.     private UrlMatcher urlMatcher = new AntUrlPathMatcher();;  
  24.     private static Map<String, Collection<ConfigAttribute>> resourceMap = null;  
  25.   
  26.     public MyInvocationSecurityMetadataSource() {  
  27.          loadResourceDefine();  
  28.      }  
  29.   
  30.     private void loadResourceDefine() {  
  31.          resourceMap = new HashMap<String, Collection<ConfigAttribute>>();  
  32.          Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();  
  33.          ConfigAttribute ca = new SecurityConfig("ROLE_ADMIN");  
  34.          atts.add(ca);  
  35.          resourceMap.put("/index.jsp", atts);  
  36.          resourceMap.put("/i.jsp", atts);  
  37.      }  
  38.   
  39.     // According to a URL, Find out permission configuration of this URL.  
  40.     public Collection<ConfigAttribute> getAttributes(Object object)  
  41.             throws IllegalArgumentException {  
  42.         // guess object is a URL.  
  43.          String url = ((FilterInvocation)object).getRequestUrl();  
  44.          Iterator<String> ite = resourceMap.keySet().iterator();  
  45.         while (ite.hasNext()) {  
  46.              String resURL = ite.next();  
  47.             if (urlMatcher.pathMatchesUrl(url, resURL)) {  
  48.                 return resourceMap.get(resURL);  
  49.              }  
  50.          }  
  51.         return null;  
  52.      }  
  53.   
  54.     public boolean supports(Class<?> clazz) {  
  55.         return true;  
  56.      }  
  57.       
  58.     public Collection<ConfigAttribute> getAllConfigAttributes() {  
  59.         return null;  
  60.      }  
  61.   
  62. }  

看看loadResourceDefine方法,我在这里,假定index.jsp和i.jsp这两个资源,需要ROLE_ADMIN角色的用户才能访问。

这个类中,还有一个最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。注意,我例子中使用的是AntUrlPathMatcher这个path matcher来检查URL是否与资源定义匹配,事实上你还要用正则的方式来匹配,或者自己实现一个matcher。

6,剩下的就是最终的决策了,make a decision,其实也很容易,呵呵。

  1. package com.example.spring.security;  
  2. import java.util.Collection;  
  3. import java.util.Iterator;  
  4.   
  5. import org.springframework.security.access.AccessDecisionManager;  
  6. import org.springframework.security.access.AccessDeniedException;  
  7. import org.springframework.security.access.ConfigAttribute;  
  8. import org.springframework.security.access.SecurityConfig;  
  9. import org.springframework.security.authentication.InsufficientAuthenticationException;  
  10. import org.springframework.security.core.Authentication;  
  11. import org.springframework.security.core.GrantedAuthority;  
  12.   
  13.   
  14. public class MyAccessDecisionManager implements AccessDecisionManager {  
  15.   
  16.     //In this method, need to compare authentication with configAttributes.  
  17.     // 1, A object is a URL, a filter was find permission configuration by this URL, and pass to here.  
  18.     // 2, Check authentication has attribute in permission configuration (configAttributes)  
  19.     // 3, If not match corresponding authentication, throw a AccessDeniedException.  
  20.     public void decide(Authentication authentication, Object object,  
  21.              Collection<ConfigAttribute> configAttributes)  
  22.             throws AccessDeniedException, InsufficientAuthenticationException {  
  23.         if(configAttributes == null){  
  24.             return ;  
  25.          }  
  26.          System.out.println(object.toString());  //object is a URL.  
  27.          Iterator<ConfigAttribute> ite=configAttributes.iterator();  
  28.         while(ite.hasNext()){  
  29.              ConfigAttribute ca=ite.next();  
  30.              String needRole=((SecurityConfig)ca).getAttribute();  
  31.             for(GrantedAuthority ga:authentication.getAuthorities()){  
  32.                 if(needRole.equals(ga.getAuthority())){  //ga is user's role.  
  33.                     return;  
  34.                  }  
  35.              }  
  36.          }  
  37.         throw new AccessDeniedException("no right");  
  38.      }  
  39.   
  40.      @Override  
  41.     public boolean supports(ConfigAttribute attribute) {  
  42.         // TODO Auto-generated method stub  
  43.         return true;  
  44.      }  
  45.   
  46.      @Override  
  47.     public boolean supports(Class<?> clazz) {  
  48.         return true;  
  49.      }  
  50.   
  51.   
  52. }  

 在这个类中,最重要的是decide方法,如果不存在对该资源的定义,直接放行;否则,如果找到正确的角色,即认为拥有权限,并放行,否则throw new AccessDeniedException("no right");这样,就会进入上面提到的403.jsp页面

原文地址:https://www.cnblogs.com/yechanglv/p/6941814.html