权限管理—spring security的实现

spring security实现方式大致可以分为这几种:

    1.配置文件实现,只需要在配置文件中指定拦截的url所需要权限、配置userDetailsService指定用户名、密码、对应权限,就可以实现。

    2.实现UserDetailsService,loadUserByUsername(String userName)方法,根据userName来实现自己的业务逻辑返回UserDetails的实现类,需要自定义User类实现UserDetails,比较重要的方法是getAuthorities(),用来返回该用户所拥有的权限。

    3.通过自定义filter重写spring security拦截器,实现动态过滤用户权限。

    4.通过自定义filter重写spring security拦截器,实现自定义参数来检验用户,并且过滤权限。


1.最简单配置spring-security.xml,实现1

[html]  view plain  copy
  1. <beans xmlns="http://www.springframework.org/schema/beans"    
  2.     xmlns:security="http://www.springframework.org/schema/security"    
  3.     xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  5.           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd    
  6.           http://www.springframework.org/schema/security    
  7.           http://www.springframework.org/schema/security/spring-security-4.0.xsd">    
  8.     
  9.     <!-- use-expressions:Spring 表达式语言配置访问控制 -->    
  10.     <security:http auto-config="true" use-expressions="false">    
  11.             <!-- 配置权限拦截,访问所有url,都需要用户登录,且拥有ROLE_USER权限 -->  
  12.         <security:intercept-url pattern="/**" access="ROLE_USER" />    
  13.              
  14.     </security:http>    
  15.     
  16.     <security:authentication-manager alias="authenticationManager">    
  17.         <security:authentication-provider>    
  18.                 <!-- 配置默认用户,用户名:admin 密码:123456 拥有权限:ROLE_USER -->  
  19.             <security:user-service>    
  20.                 <security:user name="admin" password="123456"    
  21.                     authorities="ROLE_USER" />    
  22.             </security:user-service>    
  23.         </security:authentication-provider>    
  24.            
  25.     </security:authentication-manager>    
  26.           
  27. </beans>    


2.实现UserDetailsService

先整理下spring secruity验证流程:

springSecurity的登录验证是由org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter这个过滤器来完成的,在该类的父类AbstractAuthenticationProcessingFilter中有一个AuthenticationManager接口属性,验证工作主要是通过这个AuthenticationManager接口的实例来完成的。在默认情况下,springSecurity框架会把org.springframework.security.authentication.ProviderManager类的实例注入到该属性

UsernamePasswordAuthenticationFilter的验证过程如下:

1. 首先过滤器会调用自身的attemptAuthentication方法,从request中取出authentication, authentication是在org.springframework.security.web.context.SecurityContextPersistenceFilter过滤器中通过捕获用户提交的登录表单中的内容生成的一个org.springframework.security.core.Authentication接口实例.

2. 拿到authentication对象后,过滤器会调用ProviderManager类的authenticate方法,并传入该对象

3.ProviderManager类的authenticate方法中会调用类中的List<AuthenticationProvider> providers集合中的各个AuthenticationProvider接口实现类中的authenticate(Authentication authentication)方法进行验证,由此可见,真正的验证逻辑是由各个AuthenticationProvider接口实现类来完成的。DaoAuthenticationProvider类是默认情况下注入的一个AuthenticationProvider接口实现类

4.provider的实现类在验证用户时,会调用userDetailsService的实现类的loadUserByUsername方法来获取用户信息


首先spring-security配置文件

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.   
  3. <beans:beans xmlns="http://www.springframework.org/schema/security"  
  4.     xmlns:beans="http://www.springframework.org/schema/beans"  
  5.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  7.                         http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
  8.                         http://www.springframework.org/schema/security   
  9.                         http://www.springframework.org/schema/security/spring-security.xsd">  
  10.   <!--   use-expressions=”true” 需要使用表达式方式来写权限-->  
  11.     <http auto-config="true"  use-expressions="false">        
  12.        <!--这是spring 提供的http/https信道安全的这个是重要的!你的请求信道是安全的!-->  
  13.        <!--  
  14.        释放用户登陆page 允许任何人访问该页面 ,IS_AUTHENTICATED_ANONYMOUSLY表示不拦截  
  15.        另一种不拦截资源的配置:<http pattern="/login.jsp" security="none">  
  16.        -->  
  17.       
  18.        <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>  
  19.          
  20.         <!-- 配置用户正常访问page-->  
  21.         <intercept-url pattern="/**" access="ROLE_USER"/>  
  22.           
  23.         <!-- 自定义用户登陆page default-target-url登陆成功跳转的page ,authentication-failure-url="/login.jsp?error=true"这里是登陆失败跳转的page-->  
  24.         <form-login login-page="/login.jsp" default-target-url="/jsp/index/main.jsp" authentication-failure-url="/login.jsp?error=true"/>  
  25.         <!-- 记住密码 -->   
  26. <!--         <remember-me key="elim" user-service-ref="securityManager"/> -->  
  27.      </http>  
  28.        
  29.     <authentication-manager alias="authenticationManager">  
  30.         <!--   
  31.              authentication-provider 引用UserDetailsService实现类时使用user-service-ref属性,引用authentication实现类时,使用ref属性  
  32.              这两个属性的区别在于    
  33.        ref:直接将ref依赖的bean注入到AuthenticationProvider的providers集合中    
  34.        user-service-ref:定义DaoAuthenticationProvider的bean注入到AuthenticationProvider的providers集合中,    
  35.        并且DaoAuthenticationProvider的变量userDetailsService由user-service-ref依赖的bean注入。  
  36.         -->  
  37.         <authentication-provider user-service-ref="msecurityManager">  
  38.             <!-- 密码加密 -->  
  39.             <password-encoder ref="myPasswordEncoder"/>  
  40.         </authentication-provider>  
  41.     </authentication-manager>  
  42.       
  43.     <!-- 实现UserDetailsService -->  
  44.     <beans:bean id="msecurityManager" class="com.ultrapower.me.util.security.support.SecurityManagerSupport"></beans:bean>  
  45.     <!-- 密码加密 -->  
  46.     <beans:bean id="myPasswordEncoder" class="com.ultrapower.me.util.security.MyPasswordEncoder"/>  
  47.       
  48. </beans:beans>  


userDetailsService实现:

[java]  view plain  copy
  1. /** 
  2.  *  
  3.  */  
  4. package com.ultrapower.me.util.security.support;  
  5.   
  6. import java.util.ArrayList;  
  7. import java.util.HashMap;  
  8. import java.util.HashSet;  
  9. import java.util.List;  
  10. import java.util.Map;  
  11. import java.util.Set;  
  12.   
  13. import org.apache.commons.logging.Log;  
  14. import org.apache.commons.logging.LogFactory;  
  15. import org.springframework.dao.DataAccessException;  
  16. import org.springframework.jdbc.core.JdbcTemplate;  
  17. import org.springframework.security.core.userdetails.UserDetails;  
  18. import org.springframework.security.core.userdetails.UserDetailsService;  
  19. import org.springframework.security.core.userdetails.UsernameNotFoundException;  
  20.   
  21. import com.ultrapower.me.util.Constants;  
  22. import com.ultrapower.me.util.dbDao.SpringBeanUtil;  
  23. import com.ultrapower.me.util.security.SecurityManager;  
  24. import com.ultrapower.me.util.security.entity.Resource;  
  25. import com.ultrapower.me.util.security.entity.Role;  
  26. import com.ultrapower.me.util.security.entity.User;  
  27. import com.ultrapower.me.util.task.PasswordUtils;  
  28.   
  29.   
  30. public class SecurityManagerSupport  implements UserDetailsService{  
  31.     private   Log   log   = LogFactory.getLog(this.getClass().getName());   
  32.        
  33.   
  34.     public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {  
  35. //        List<User> users = getHibernateTemplate().find("FROM User user WHERE user.name = ? AND user.disabled = false", userName);  
  36.         log.info("SecurityManagerSupport.loadUserByUsername.userName:"+userName);  
  37.           
  38.         User user =null;  
  39.         if("admin".equals(userName)){         
  40.             Set<Role> roles = new HashSet<Role>() ;  
  41.             Role role = new Role();  
  42.             role.setRoleid("ROLE_USER");  
  43.             role.setRoleName("ROLE_USER");  
  44.               
  45.             Set<Resource> resources=new HashSet<Resource>() ;  
  46.               
  47.             Resource res = new Resource();  
  48.             res.setResid("ME001");  
  49.             res.setResName("首页");  
  50.             res.setResUrl("/jsp/index/main.jsp");  
  51.             res.setType("ROLE_USER");  
  52.             res.setRoles(roles);  
  53.             resources.add(res);  
  54.               
  55.             role.setResources(resources);  
  56.               
  57.             roles.add(role);  
  58.             user = new User();  
  59.             user.setAccount("admin");  
  60.             user.setDisabled(false);  
  61.             user.setPassword(PasswordUtils.entryptPassword(Constants.securityKey));  
  62.             log.info(user.getPassword());  
  63.             user.setRoles(roles);             
  64.         }  
  65.       return user;//返回UserDetails的实现user不为空,则验证通过  
  66.     }  
  67.       
  68. }  

UserDetails实现:

[java]  view plain  copy
  1. /** 
  2.  *  
  3.  */  
  4. package com.ultrapower.me.util.security.entity;  
  5.   
  6. import java.util.ArrayList;  
  7. import java.util.Collection;  
  8. import java.util.HashMap;  
  9. import java.util.List;  
  10. import java.util.Map;  
  11. import java.util.Set;  
  12.   
  13. import org.apache.commons.lang.StringUtils;  
  14. import org.springframework.security.core.GrantedAuthority;  
  15. import org.springframework.security.core.authority.SimpleGrantedAuthority;  
  16. import org.springframework.security.core.userdetails.UserDetails;  
  17.   
  18.    
  19. public class User implements UserDetails {  
  20.       
  21.     private static final long serialVersionUID = 8026813053768023527L;  
  22.   
  23.      
  24.     private String account;  
  25.       
  26.     private String name;  
  27.       
  28.     private String password;  
  29.       
  30.     private boolean disabled;  
  31.    
  32.     private Set<Role> roles;  
  33.       
  34.    
  35.     private Map<String, List<Resource>> roleResources;  
  36.       
  37.     /** 
  38.      * The default constructor 
  39.      */  
  40.     public User() {  
  41.           
  42.     }  
  43.       
  44.     /** 
  45.      * Returns the authorites string 
  46.      *  
  47.      * eg.  
  48.      *    downpour --- ROLE_ADMIN,ROLE_USER 
  49.      *    robbin --- ROLE_ADMIN 
  50.      *  
  51.      * @return 
  52.      */  
  53.     public String getAuthoritiesString() {  
  54.         List<String> authorities = new ArrayList<String>();  
  55.         for(GrantedAuthority authority : this.getAuthorities()) {  
  56.             authorities.add(authority.getAuthority());  
  57.         }  
  58.         return StringUtils.join(authorities, ",");  
  59.     }  
  60.   
  61.     @Override  
  62.     public Collection<? extends GrantedAuthority> getAuthorities() {  
  63.         // 根据自定义逻辑来返回用户权限,如果用户权限返回空或者和拦截路径对应权限不同,验证不通过  
  64.         if(!roles.isEmpty()){  
  65.             List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();  
  66.             GrantedAuthority au = new SimpleGrantedAuthority("ROLE_USER");  
  67.             list.add(au);  
  68.             return list;  
  69.         }  
  70.         return null;  
  71.     }  
  72.   
  73.     /*  
  74.      * 密码 
  75.      */  
  76.     public String getPassword() {  
  77.         return password;  
  78.     }  
  79.   
  80.     /*  
  81.      * 用户名 
  82.      */  
  83.     public String getUsername() {  
  84.         return name;  
  85.     }  
  86.   
  87.     /*  
  88.      *帐号是否不过期,false则验证不通过 
  89.      */  
  90.     public boolean isAccountNonExpired() {  
  91.         return true;  
  92.     }  
  93.   
  94.     /*  
  95.      * 帐号是否不锁定,false则验证不通过 
  96.      */  
  97.     public boolean isAccountNonLocked() {  
  98.         return true;  
  99.     }  
  100.   
  101.     /*  
  102.      * 凭证是否不过期,false则验证不通过 
  103.      */  
  104.     public boolean isCredentialsNonExpired() {  
  105.         return true;  
  106.     }  
  107.   
  108.     /*  
  109.      * 该帐号是否启用,false则验证不通过 
  110.      */  
  111.     public boolean isEnabled() {  
  112.         return !disabled;  
  113.     }  
  114.   
  115.    
  116.   
  117.     /** 
  118.      * @return the name 
  119.      */  
  120.     public String getName() {  
  121.         return name;  
  122.     }  
  123.   
  124.     /** 
  125.      * @return the disabled 
  126.      */  
  127.     public boolean isDisabled() {  
  128.         return disabled;  
  129.     }  
  130.   
  131.     /** 
  132.      * @return the roles 
  133.      */  
  134.     public Set<Role> getRoles() {  
  135.         return roles;  
  136.     }  
  137.   
  138.     /** 
  139.      * @return the roleResources 
  140.      */  
  141.     public Map<String, List<Resource>> getRoleResources() {  
  142.         // init roleResources for the first time  
  143.         System.out.println("---------------------------------------------------");  
  144.         if(this.roleResources == null) {  
  145.               
  146.             this.roleResources = new HashMap<String, List<Resource>>();  
  147.               
  148.             for(Role role : this.roles) {  
  149.                 String roleName = role.getRoleName();  
  150.                 Set<Resource> resources = role.getResources();  
  151.                 for(Resource resource : resources) {  
  152.                     String key = roleName + "_" + resource.getType();  
  153.                     if(!this.roleResources.containsKey(key)) {  
  154.                         this.roleResources.put(key, new ArrayList<Resource>());  
  155.                     }  
  156.                     this.roleResources.get(key).add(resource);                    
  157.                 }  
  158.             }  
  159.               
  160.         }  
  161.         return this.roleResources;  
  162.     }  
  163.   
  164.    
  165.     /** 
  166.      * @param name the name to set 
  167.      */  
  168.     public void setName(String name) {  
  169.         this.name = name;  
  170.     }  
  171.   
  172.     /** 
  173.      * @param password the password to set 
  174.      */  
  175.     public void setPassword(String password) {  
  176.         this.password = password;  
  177.     }  
  178.   
  179.     /** 
  180.      * @param disabled the disabled to set 
  181.      */  
  182.     public void setDisabled(boolean disabled) {  
  183.         this.disabled = disabled;  
  184.     }  
  185.   
  186.     /** 
  187.      * @param roles the roles to set 
  188.      */  
  189.     public void setRoles(Set<Role> roles) {  
  190.         this.roles = roles;  
  191.     }  
  192.   
  193.     public String getAccount() {  
  194.         return account;  
  195.     }  
  196.   
  197.     public void setAccount(String account) {  
  198.         this.account = account;  
  199.     }  
  200.   
  201.     public void setRoleResources(Map<String, List<Resource>> roleResources) {  
  202.         this.roleResources = roleResources;  
  203.     }  
  204.       
  205. }  

3.实现动态过滤用户权限

在spring-security配置文件的http标签中添加如下配置
[html]  view plain  copy
  1. <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="securityInterceptor"/>  
在spring-security配置文件中添加如下配置
[html]  view plain  copy
  1. <!--     自定义拦截器 -->  
  2.     <beans:bean id="securityInterceptor" class="com.ultrapower.me.util.security.interceptor.SecurityInterceptor">  
  3.         <beans:property name="authenticationManager" ref="authenticationManager"/>  
  4.         <beans:property name="accessDecisionManager" ref="mesecurityAccessDecisionManager"/>  
  5.         <beans:property name="securityMetadataSource" ref="secureResourceFilterInvocationDefinitionSource" />  
  6.     </beans:bean>  
  7. <!--     获取访问url对应的所有权限 -->  
  8.     <beans:bean id="secureResourceFilterInvocationDefinitionSource" class="com.ultrapower.me.util.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />  
  9. <!--     校验用户的权限是否足够 -->  
  10.     <beans:bean id="mesecurityAccessDecisionManager" class="com.ultrapower.me.util.security.interceptor.SecurityAccessDecisionManager" />  
  11.       




securityInterceptor继承AbstractSecurityInterceptor过滤器,实现Filter过滤器
[java]  view plain  copy
  1. package com.ultrapower.me.util.security.interceptor;  
  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 SecurityInterceptor extends AbstractSecurityInterceptor implements Filter{  
  19.   
  20.     //配置文件注入  
  21.     private FilterInvocationSecurityMetadataSource securityMetadataSource;  
  22.       
  23.     public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {  
  24.         return securityMetadataSource;  
  25.     }  
  26.   
  27.     public void setSecurityMetadataSource(  
  28.             FilterInvocationSecurityMetadataSource securityMetadataSource) {  
  29.         this.securityMetadataSource = securityMetadataSource;  
  30.     }  
  31.   
  32.     @Override  
  33.     public void doFilter(ServletRequest request, ServletResponse response,  
  34.             FilterChain chain) throws IOException, ServletException {  
  35.         // TODO Auto-generated method stub\  
  36.           
  37.         FilterInvocation fi = new FilterInvocation(request, response, chain);  
  38.         //fi里面有一个被拦截的url  
  39.         //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限  
  40.         //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够  
  41.         InterceptorStatusToken token = super.beforeInvocation(fi);  
  42.         try {  
  43.             //执行下一个拦截器  
  44.             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());     
  45.         } finally {   
  46.             super.afterInvocation(token, null);    
  47.         }     
  48.           
  49.     }  
  50.   
  51.     @Override  
  52.     public void init(FilterConfig arg0) throws ServletException {  
  53.         // TODO Auto-generated method stub  
  54.           
  55.     }  
  56.   
  57.     @Override  
  58.     public Class<?> getSecureObjectClass() {  
  59.         // TODO Auto-generated method stub  
  60.         return FilterInvocation.class;   
  61.     }  
  62.   
  63.     @Override  
  64.     public SecurityMetadataSource obtainSecurityMetadataSource() {  
  65.         // TODO Auto-generated method stub  
  66.         return this.securityMetadataSource;     
  67.     }  
  68.   
  69.     @Override  
  70.     public void destroy() {  
  71.         // TODO Auto-generated method stub  
  72.     }  
  73. }  

登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,这个方法调用了invoke方法,其中fi断点显示是一个url(可能重写了toString方法吧,但是里面还有一些方法的),最重要的是beforeInvocation这个方法,它首先会调用MyInvocationSecurityMetadataSource类的getAttributes方法获取被拦截url所需的权限,在调用MyAccessDecisionManager类decide方法判断用户是否够权限。弄完这一切就会执行下一个拦截器。

secureResourceFilterInvocationDefinitionSource实现
[java]  view plain  copy
  1. /** 
  2.  *  
  3.  */  
  4. package com.ultrapower.me.util.security.interceptor;  
  5.   
  6. import java.util.ArrayList;  
  7. import java.util.Collection;  
  8. import java.util.HashMap;  
  9. import java.util.Iterator;  
  10. import java.util.Map;  
  11.   
  12. import javax.servlet.ServletContext;  
  13.   
  14. import org.springframework.beans.factory.InitializingBean;  
  15. import org.springframework.security.access.ConfigAttribute;  
  16. import org.springframework.security.access.SecurityConfig;  
  17. import org.springframework.security.web.FilterInvocation;  
  18. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
  19. import org.springframework.util.AntPathMatcher;  
  20. import org.springframework.util.PathMatcher;  
  21.   
  22.   
  23. public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationSecurityMetadataSource, InitializingBean {  
  24.       
  25.     private PathMatcher matcher;  
  26.       
  27.     private static Map<String, Collection<ConfigAttribute>> map = new HashMap<String, Collection<ConfigAttribute>>();  
  28.   
  29.     /*  
  30.      * 初始化用户权限,为了简便操作没有从数据库获取 
  31.      * 实际操作可以从数据库中获取所有资源路径url所对应的权限 
  32.      */  
  33.     public void afterPropertiesSet() throws Exception {  
  34.         this.matcher = new AntPathMatcher();//用来匹配访问资源路径  
  35.         Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();   
  36.         ConfigAttribute ca = new SecurityConfig("ROLE_USER");  
  37.         atts.add(ca);   
  38.         map.put("/jsp/index/main.jsp", atts);    
  39.         Collection<ConfigAttribute> attsno =new ArrayList<ConfigAttribute>();  
  40.         ConfigAttribute cano = new SecurityConfig("ROLE_NO");  
  41.         attsno.add(cano);  
  42.         map.put("/http://blog.csdn.net/u012367513/article/details/other.jsp", attsno);     
  43.     }  
  44.       
  45.        
  46.   
  47.     @Override  
  48.     public Collection<ConfigAttribute> getAttributes(Object object)  
  49.             throws IllegalArgumentException {  
  50.         // TODO Auto-generated method stub  
  51.         FilterInvocation filterInvocation = (FilterInvocation) object;  
  52.           
  53.         String requestURI = filterInvocation.getRequestUrl();  
  54.         //循环资源路径,当访问的Url和资源路径url匹配时,返回该Url所需要的权限  
  55.         for(Iterator<Map.Entry<String, Collection<ConfigAttribute>>> iter = map.entrySet().iterator(); iter.hasNext();) {  
  56.               Map.Entry<String, Collection<ConfigAttribute>> entry = iter.next();  
  57.               String url = entry.getKey();  
  58.                 
  59.               if(matcher.match(url, requestURI)) {  
  60.                   return map.get(requestURI);  
  61.               }  
  62.         }  
  63.         
  64.         return null;  
  65.     }  
  66.   
  67.     @Override  
  68.     public Collection<ConfigAttribute> getAllConfigAttributes() {  
  69.         // TODO Auto-generated method stub  
  70.         return null;  
  71.     }  
  72.   
  73.     /* (non-Javadoc) 
  74.      * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions() 
  75.      */  
  76.     @SuppressWarnings("rawtypes")  
  77.     public Collection getConfigAttributeDefinitions() {  
  78.         return null;  
  79.     }  
  80.   
  81.     /* (non-Javadoc) 
  82.      * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class) 
  83.      */  
  84.     public boolean supports(@SuppressWarnings("rawtypes") Class clazz) {  
  85.         return true;  
  86.     }  
  87.       
  88.     /** 
  89.      *  
  90.      * @param filterInvocation 
  91.      * @return 
  92.      */  
  93.     @SuppressWarnings("unchecked")  
  94.     private Map<String, String> getUrlAuthorities(org.springframework.security.web.FilterInvocation filterInvocation) {  
  95.         ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();  
  96.         return (Map<String, String>)servletContext.getAttribute("urlAuthorities");  
  97.     }  
  98.   
  99. }  

mesecurityAccessDecisionManager实现
[java]  view plain  copy
  1. package com.ultrapower.me.util.security.interceptor;  
  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. public class SecurityAccessDecisionManager implements AccessDecisionManager {  
  15.       
  16.     /** 
  17.      * 检查用户是否够权限访问资源 
  18.      * authentication 是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息 
  19.      * object 是url 
  20.      * configAttributes 所需的权限 
  21.      * @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection) 
  22.      */  
  23.     @Override  
  24.     public void decide(Authentication authentication, Object object,  
  25.             Collection<ConfigAttribute> configAttributes)  
  26.             throws AccessDeniedException, InsufficientAuthenticationException {  
  27.         // 对应url没有权限时,直接跳出方法  
  28.        if(configAttributes == null){   
  29.            return;         
  30.        }    
  31.           
  32.        Iterator<ConfigAttribute> ite=configAttributes.iterator();  
  33.        //判断用户所拥有的权限,是否符合对应的Url权限,如果实现了UserDetailsService,则用户权限是loadUserByUsername返回用户所对应的权限  
  34.        while(ite.hasNext()){  
  35.            ConfigAttribute ca=ite.next();    
  36.            String needRole=((SecurityConfig)ca).getAttribute();  
  37.            for(GrantedAuthority ga : authentication.getAuthorities()){   
  38.                System.out.println(":::::::::::::"+ga.getAuthority());  
  39.                if(needRole.equals(ga.getAuthority())){    
  40.                    return;                
  41.                }              
  42.            }        
  43.        }   
  44.        //注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面  
  45.        throw new AccessDeniedException("no right");    
  46.     }  
  47.     @Override  
  48.     public boolean supports(ConfigAttribute attribute) {  
  49.         return true;  
  50.     }  
  51.     @Override  
  52.     public boolean supports(Class<?> clazz) {  
  53.         return true;  
  54.     }  
  55.   
  56. }  

4.实现AuthenticationProvider,自定义参数验证

这种验证以前项目用过,现在没有写示例代码,先写下大概流程和需要用到的类
这种验证的好处:可以在自定义登录界面添加登录时需要的参数,如多个验证码等、可以修改默认登录名称和密码的参数名

整体流程:
1.用户登录时,先经过自定义的passcard_filter过滤器,该过滤器继承了AbstractAuthenticationProcessingFilter,并且绑定了登录失败和成功时需要的处理器(跳转页面使用)
2.执行attemptAuthentication方法,可以通过request获取登录页面传递的参数,实现自己的逻辑,并且把对应参数set到AbstractAuthenticationToken的实现类中
3.验证逻辑走完后,调用 this.getAuthenticationManager().authenticate(token);方法,执行AuthenticationProvider的实现类的supports方法
4.如果返回true则继续执行authenticate方法
5.在authenticate方法中,首先可以根据用户名获取到用户信息,再者可以拿自定义参数和用户信息做逻辑验证,如密码的验证
6.自定义验证通过以后,获取用户权限set到User中,用于springSecurity做权限验证
7.this.getAuthenticationManager().authenticate(token)方法执行完后,会返回Authentication,如果不为空,则说明验证通过
8.验证通过后,可实现自定义逻辑操作,如记录cookie信息
9.attemptAuthentication方法执行完成后,由springSecuriy来进行对应权限验证,成功于否会跳转到相对应处理器设置的界面。

1.自定义PassCardAuthenticationToken类,继承AbstractAuthenticationToken类,用于定义参数,需要实现的方法
[java]  view plain  copy
  1. /** 
  2.      * 凭证,用户密码 
  3.      */  
  4.     @Override  
  5.     public Object getCredentials() {  
  6.         return password;  
  7.     }  
  8.   
  9.     /** 
  10.      * 当事人,登录名 用户Id 
  11.      */  
  12.     @Override  
  13.     public Object getPrincipal() {  
  14.         return userID;  
  15.     }  


2.User类要实现Authentication,需要实现的方法
[java]  view plain  copy
  1. /** 
  2.      * 返回用户所属权限 
  3.      */  
  4.     @Override  
  5.     public Collection<GrantedAuthority> getAuthorities() {  
  6.         return this.accesses;  
  7.     }  
  8.       
  9.     @Override  
  10.     public Object getCredentials() {  
  11.         return null;  
  12.     }  
  13.     @Override  
  14.     public Object getDetails() {  
  15.         return null;  
  16.     }  
  17.     /** 
  18.      * 登录名称 
  19.      */  
  20.     @Override  
  21.     public Object getPrincipal() {  
  22.         return loginName;  
  23.     }  
  24.     /** 
  25.      * 是否认证 
  26.      */  
  27.     @Override  
  28.     public boolean isAuthenticated() {  
  29.         return this.authenticated;  
  30.     }  
  31.     /** 
  32.      * 设置是否认证字段 
  33.      */  
  34.     @Override  
  35.     public void setAuthenticated(boolean isAuthenticated)  
  36.             throws IllegalArgumentException {  
  37.         this.authenticated=isAuthenticated;  
  38.     }  


3.需要userService实现AuthenticationProvider的 authenticate(Authentication authentication)方法
  
[java]  view plain  copy
  1. @SuppressWarnings("unchecked")  
  2.     @Override  
  3.     public Authentication authenticate(Authentication authentication)  
  4.             throws AuthenticationException {  
  5.         PassCardAuthenticationToken token=(PassCardAuthenticationToken)authentication;  
  6.         /* 
  7.          * 这里进行逻辑认证操作,可以获取token中的属性来自定义验证逻辑,代码验证逻辑可以不用管 
  8.          * 如果使用UserDetailsService的实现类来验证,就只能获取userName,不够灵活 
  9.          */  
  10.         if(token.getUserID()!=null&&token.getPassword()!=null){  
  11.             User user=(User)this.getDao().executeQueryUnique("User.loadByLoginName", QueryCmdType.QUERY_NAME, token.getUserID());  
  12.               
  13.             String password=token.getPassword();  
  14.             if(this.passwordEncoder!=null){  
  15.                 password=this.passwordEncoder.encodePassword(password, null);  
  16.             }  
  17.               
  18.             if(!password.equalsIgnoreCase(user.getPassword())){  
  19.                   
  20.                 token.setErrCode("2");  
  21.                 return null;  
  22.             }  
  23.               
  24.             if( token.isEnablePasscard() && usePassCard ){//token中激活密码卡且系统使用密码卡  
  25.                   
  26.                 int position1=((token.getRow1()-1)*7)+token.getColumn1();  
  27.                 int position2=((token.getRow2()-1)*7)+token.getColumn2();  
  28.                 //System.out.println( "---pos:"+position1+"---"+position2 );  
  29.                   
  30.                 if(user.getPassCardId()==null){  
  31.                     token.setErrCode("10");  
  32.                     return null;  
  33.                 }  
  34.                 PassCard passcard=this.passCardDao.findById(user.getPassCardId(), false);  
  35.                           
  36.                 if(passcard==null||passcard.getStatus()==PassCardHelper.STATUS_CANCEL ){  
  37.                     token.setErrCode("10");  
  38.                     return null;  
  39.                 }  
  40.                 if(passcard.getConfusedContent()==null || passcard.getConfusedContent().length()<7*7*32 ){  
  41.                     token.setErrCode("10");  
  42.                     return null;  
  43.                 }  
  44.                   
  45.                 String content=passcard.getConfusedContent();  
  46.                 int perLen=content.length()/49;  
  47.                 String str1=content.substring((position1-1)*perLen, position1*perLen);  
  48.                 String str2=content.substring((position2-1)*perLen, position2*perLen);  
  49.                 String inputStr1=token.getCard1();  
  50.                 String inputStr2=token.getCard2();  
  51.                 if(this.passwordEncoder!=null){  
  52.                     inputStr1 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr1));  
  53.                     inputStr2 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr2));  
  54.                 }  
  55.               
  56.                 if((!str1.equalsIgnoreCase(inputStr1))||(!str2.equalsIgnoreCase(inputStr2))){  
  57.                     token.setErrCode("10");  
  58.                     return null;  
  59.                 }  
  60.             }  
  61.             user.setLastIp(token.getIp());  
  62.             user.setLastLogin(new Date());  
  63.             this.getDao().saveOrUpdate(user);             
  64.             user.setAuthenticated(true);  
  65.             /* 
  66.              * 导入一次角色权限,并且把权限set到User中,用于spring验证用户权限(getAuthorities方法) 
  67.              */  
  68.             List<UserRole> userRoles=(List<UserRole>)this.getDao().executeQueryList("UserRole.listRoleByUserID", QueryCmdType.QUERY_NAME, -1, -1, user.getId());  
  69.             Set<GrantedAuthority> accesses=new HashSet<GrantedAuthority>();  
  70.             for(UserRole ur:userRoles){  
  71.                 accesses.add(ur.getRole());               
  72.             }  
  73.             user.getOrg().getOrgName();  
  74.             if(user.getOrg().getCertTypes()!=null) user.getOrg().getCertTypes().size();//延迟载入一下  
  75.             user.setAccesses(accesses);  
  76.             return user;  
  77.         }  
  78.         return null;  
  79.     }  

   重写supports(Class<? extends Object> authentication)方法,authentication要
[java]  view plain  copy
  1. /** 
  2.      * 如果此处验证不通过,是不会执行authentication方法的 
  3.      */  
  4.     @Override  
  5.     public boolean supports(Class<? extends Object> authentication) {  
  6.         return authentication.equals(PassCardAuthenticationToken.class);  
  7.     }  

4.定义filter,实现AbstractAuthenticationProcessingFilter的attemptAuthentication方法,用于获取在登录页面传递过来的参数,spring默认只获取userName(j_username),password(j_username),而且实现UserDetailsService时只传递username

[java]  view plain  copy
  1. import java.io.IOException;  
  2. import java.util.Date;  
  3.   
  4. import javax.servlet.ServletException;  
  5. import javax.servlet.http.Cookie;  
  6. import javax.servlet.http.HttpServletRequest;  
  7. import javax.servlet.http.HttpServletResponse;  
  8. import javax.servlet.http.HttpSession;  
  9.   
  10. import org.apache.log4j.Logger;  
  11. import org.springframework.security.core.Authentication;  
  12. import org.springframework.security.core.AuthenticationException;  
  13. import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;  
  14. import org.springframework.util.StringUtils;  
  15.   
  16. import cn.edu.jszg.cert.user.UserLog;  
  17. import cn.edu.jszg.cert.user.UserLogService;  
  18. import cn.edu.jszg.cert.web.WebApplicationConfiguration;  
  19. import cn.edu.jszg.cert.web.controller.portal.auth.RemoteDataValidator;  
  20.   
  21. import com.google.code.kaptcha.servlet.KaptchaServlet;  
  22.   
  23. public class PasscardAuthenticationProcessingFilter extends  
  24.         AbstractAuthenticationProcessingFilter {  
  25.     private String successPage = "/home/admin/index";  
  26.     private String failurePage = "/public/adminLoginEntry";  
  27.     private boolean forward = false;  
  28.     private boolean useVerifyCode=true;  
  29.     private String certLoginUrl;  
  30.       
  31.     static Logger logger = Logger.getLogger(PasscardAuthenticationProcessingFilter.class);  
  32.       
  33.     private WebApplicationConfiguration config;  
  34.     private UserLogService userLogService;    
  35.       
  36.     public void setConfig(WebApplicationConfiguration config) {  
  37.         this.config = config;  
  38.     }  
  39.   
  40.     /** 
  41.      * 实现AbstractAuthenticationProcessingFilter的有参构造 
  42.      * 没记错的话,相当于该filter的访问路径  
  43.      */  
  44.     protected PasscardAuthenticationProcessingFilter() {  
  45.         super("/adminLoginCheck");  
  46.     }  
  47.   
  48.     public void setUseVerifyCode(boolean useVerifyCode) {  
  49.         this.useVerifyCode = useVerifyCode;  
  50.     }  
  51.   
  52.     public void setUserLogService(UserLogService userLogService) {  
  53.         this.userLogService = userLogService;  
  54.     }  
  55.       
  56.     public boolean validate(HttpServletRequest request) {  
  57.         String userId = request.getParameter("username");  
  58.         String md2 = request.getParameter("m");  
  59.         String l = request.getParameter("l");  
  60.         if (userId == null || md2 == null || l == null) {  
  61.             return false;  
  62.         }  
  63.         long longTime = Long.parseLong(l);  
  64.         if (longTime < new Date().getTime()) {  
  65.             return false;  
  66.         }  
  67.   
  68.           
  69.         try {  
  70.             String md1 = RemoteDataValidator.genExamMd5Digest(userId, longTime);  
  71.             if (md1.equals(md2))  
  72.                 return true;  
  73.               
  74.         } catch (Exception e) {           
  75.             //e.printStackTrace();  
  76.         }  
  77.           
  78.         return false;  
  79.     }  
  80.   
  81.     /** 
  82.      * 可以通过request获取页面传递过来的参数,并且set到相应的token中 
  83.      */  
  84.     @Override  
  85.     public Authentication attemptAuthentication(HttpServletRequest request,  
  86.             HttpServletResponse response) throws AuthenticationException,  
  87.             IOException, ServletException {  
  88.           
  89. //      logger.warn("-----------------start证书登录用户----------");  
  90.         HttpSession s = request.getSession(true);  
  91.         PassCardAuthenticationToken token = new PassCardAuthenticationToken();  
  92.           
  93.         String verifyCode = request.getParameter("verifyCode");  
  94.         String userID = request.getParameter("username");  
  95.         //....此处省略获取参数,并且验证、赋值的逻辑  
  96.         Authentication auth = null;  
  97.           
  98.         try {  
  99.             //此处调用getAuthenticationManager的authenticate方法,当supports方法返回true时执行authenticate方法  
  100.             auth = this.getAuthenticationManager().authenticate(token);  
  101.               
  102.             //此处为登录成功后,相应的处理逻辑  
  103.             if (auth == null || !auth.isAuthenticated()) {  
  104.                 s.setAttribute("__login_error", token.getErrCode());  
  105.             } else  {  
  106.                 s.removeAttribute("__login_error");  
  107.                 s.removeAttribute("__login_username");  
  108.                 s.removeAttribute("__cert_userid");  
  109.                 if( token.isEnablePasscard()) {  
  110.                     s.removeAttribute("__passcard_row1");  
  111.                     s.removeAttribute("__passcard_row2");  
  112.                     s.removeAttribute("__passcard_column1");  
  113.                     s.removeAttribute("__passcard_column2");  
  114.                 }  
  115.             }  
  116.         } catch (AuthenticationException e) {  
  117.             s.setAttribute("__login_error", token.getErrCode());  
  118.             throw e;  
  119.         }  
  120.           
  121.       
  122.         return auth;  
  123.     }  
  124.   
  125.     public void setSuccessPage(String successPage) {  
  126.         this.successPage = successPage;  
  127.     }  
  128.   
  129.     public void setFailurePage(String failurePage) {  
  130.         this.failurePage = failurePage;  
  131.     }  
  132.   
  133.     public void setForward(boolean forward) {  
  134.         this.forward = forward;  
  135.     }  
  136.   
  137.     public void setCertLoginUrl(String certLoginUrl) {  
  138.         this.certLoginUrl = certLoginUrl;  
  139.     }  
  140.   
  141.     @Override  
  142.     public void afterPropertiesSet() {  
  143.         super.afterPropertiesSet();  
  144.         /* 
  145.         *该处理器实现了AuthenticationSuccessHandler, AuthenticationFailureHandler 
  146.         *用于处理登录成功或者失败后,跳转的界面 
  147.         */  
  148.         AuthenticationResultHandler handler = new AuthenticationResultHandler();  
  149.         handler.setForward(forward);  
  150.         handler.setLoginFailurePage(failurePage);  
  151.         handler.setLoginSuccessPage(successPage);  
  152.         handler.setCertLoginUrl(certLoginUrl);  
  153.         //设置父类中的处理器  
  154.         this.setAuthenticationSuccessHandler(handler);  
  155.         this.setAuthenticationFailureHandler(handler);  
  156.   
  157.     }  
  158.   
  159. }  

最后为spring-security配置文件中的配置,需要添加authentication-provider的引用,和filter的配置
[html]  view plain  copy
  1. <security:authentication-manager alias="authenticationManager">  
  2.         <!-- 注意,这里仅仅是系统默认的认证机制,请在正式系统中明确知道其功能再使用 -->  
  3.         <security:authentication-provider ref="acocunt_defaultAnthentiactionProvider"/>  
  4.         <security:authentication-provider ref="registrationService"/>  
  5.         <security:authentication-provider ref="enrollmentService"/>  
  6.         <security:authentication-provider ref="userService"/>  
  7.     </security:authentication-manager>      
  8.     <bean id="passcard_filter" class="cn.edu.jszg.cert.security.PasscardAuthenticationProcessingFilter">  
  9.         <property name="authenticationManager" ref="authenticationManager"/>  
  10.         <property name="useVerifyCode" value="true"/>  
  11.         <property name="failurePage" value="/portal/home/auth/"></property>  
  12.         <property name="config" ref="webAppConfig"/>  
  13.         <property name="userLogService" ref="userLogService" />  
  14.         <property name="certLoginUrl" value="${cert.login.url}"/>  
  15.     </bean>  
还要在http中添加<security:custom-filter ref="passcard_filter" after="SECURITY_CONTEXT_FILTER"/>

猜你喜欢

转载自blog.csdn.net/sdjadycsdn/article/details/80559478