zoukankan      html  css  js  c++  java
  • spring security 3 自定义认证,授权示例

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

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

    Xml代码  收藏代码
    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的配置均在其中:

    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.     <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的实现:

    Java代码  收藏代码
    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的实现:

    Java代码  收藏代码
    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这个接口来初始化数据。

    Java代码  收藏代码
    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,其实也很容易,呵呵。

    Java代码  收藏代码
    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页面

  • 相关阅读:
    [Javascript]发布一个自己写的日期控件:DateTimeList
    Oracle PL/SQL 编程手册(SQL大全)
    [乱七八糟][转]程序版吉祥三宝
    [乱七八糟][转]这不是你想象中的软件产业
    [随文杂记]生男好还是生女好?
    [SqlServer]链接数据库存储过程
    [音乐天堂]辛德勒名单原声大碟
    [C#]WinFrom中的DataGrid单击选择行
    [乱七八糟]《进化论——人类科学史上最大的谎言》
    [乱七八糟]《阿甘正传》点评
  • 原文地址:https://www.cnblogs.com/yanduanduan/p/5142153.html
Copyright © 2011-2022 走看看