zoukankan      html  css  js  c++  java
  • 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

    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:security="http://www.springframework.org/schema/security"  
        xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans  
              http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
              http://www.springframework.org/schema/security  
              http://www.springframework.org/schema/security/spring-security-4.0.xsd">  
      
        <!-- use-expressions:Spring 表达式语言配置访问控制 -->  
        <security:http auto-config="true" use-expressions="false">  
        		<!-- 配置权限拦截,访问所有url,都需要用户登录,且拥有ROLE_USER权限 -->
            <security:intercept-url pattern="/**" access="ROLE_USER" />  
               
        </security:http>  
      
        <security:authentication-manager alias="authenticationManager">  
            <security:authentication-provider>  
            		<!-- 配置默认用户,用户名:admin 密码:123456 拥有权限:ROLE_USER -->
                <security:user-service>  
                    <security:user name="admin" password="123456"  
                        authorities="ROLE_USER" />  
                </security:user-service>  
            </security:authentication-provider>  
             
        </security:authentication-manager>  
            
    </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配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        					http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                            http://www.springframework.org/schema/security 
                            http://www.springframework.org/schema/security/spring-security.xsd">
      <!-- 	use-expressions=”true” 需要使用表达式方式来写权限-->
    	<http auto-config="true"  use-expressions="false">      
    	   <!--这是spring 提供的http/https信道安全的这个是重要的!你的请求信道是安全的!-->
    	   <!--
    	   释放用户登陆page 允许任何人访问该页面 ,IS_AUTHENTICATED_ANONYMOUSLY表示不拦截
    	   另一种不拦截资源的配置:<http pattern="/login.jsp" security="none">
    	   -->
    	
    	   <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
    	   
    	    <!-- 配置用户正常访问page-->
    	    <intercept-url pattern="/**" access="ROLE_USER"/>
    	    
    	    <!-- 自定义用户登陆page default-target-url登陆成功跳转的page ,authentication-failure-url="/login.jsp?error=true"这里是登陆失败跳转的page-->
    	    <form-login login-page="/login.jsp" default-target-url="/jsp/index/main.jsp" authentication-failure-url="/login.jsp?error=true"/>
    	    <!-- 记住密码 -->	
    <!-- 	    <remember-me key="elim" user-service-ref="securityManager"/> -->
    	 </http>
    	 
    	<authentication-manager alias="authenticationManager">
    		<!-- 
    			 authentication-provider 引用UserDetailsService实现类时使用user-service-ref属性,引用authentication实现类时,使用ref属性
    			 这两个属性的区别在于  
           ref:直接将ref依赖的bean注入到AuthenticationProvider的providers集合中  
           user-service-ref:定义DaoAuthenticationProvider的bean注入到AuthenticationProvider的providers集合中,  
           并且DaoAuthenticationProvider的变量userDetailsService由user-service-ref依赖的bean注入。
    		-->
    		<authentication-provider user-service-ref="msecurityManager">
    			<!-- 密码加密 -->
    			<password-encoder ref="myPasswordEncoder"/>
    		</authentication-provider>
    	</authentication-manager>
    	
    	<!-- 实现UserDetailsService -->
    	<beans:bean id="msecurityManager" class="com.ultrapower.me.util.security.support.SecurityManagerSupport"></beans:bean>
    	<!-- 密码加密 -->
    	<beans:bean id="myPasswordEncoder" class="com.ultrapower.me.util.security.MyPasswordEncoder"/>
    	
    </beans:beans>


    userDetailsService实现:

    /**
     * 
     */
    package com.ultrapower.me.util.security.support;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    
    import com.ultrapower.me.util.Constants;
    import com.ultrapower.me.util.dbDao.SpringBeanUtil;
    import com.ultrapower.me.util.security.SecurityManager;
    import com.ultrapower.me.util.security.entity.Resource;
    import com.ultrapower.me.util.security.entity.Role;
    import com.ultrapower.me.util.security.entity.User;
    import com.ultrapower.me.util.task.PasswordUtils;
    
    
    public class SecurityManagerSupport  implements UserDetailsService{
    	private   Log   log   = LogFactory.getLog(this.getClass().getName()); 
         
    
    	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
    //        List<User> users = getHibernateTemplate().find("FROM User user WHERE user.name = ? AND user.disabled = false", userName);
        	log.info("SecurityManagerSupport.loadUserByUsername.userName:"+userName);
        	
        	User user =null;
        	if("admin".equals(userName)){   	
        	    Set<Role> roles = new HashSet<Role>() ;
    	    	Role role = new Role();
    	    	role.setRoleid("ROLE_USER");
    	    	role.setRoleName("ROLE_USER");
    	    	
    	    	Set<Resource> resources=new HashSet<Resource>() ;
    	    	
    	    	Resource res = new Resource();
    	    	res.setResid("ME001");
    	    	res.setResName("首页");
    	    	res.setResUrl("/jsp/index/main.jsp");
    	    	res.setType("ROLE_USER");
    	    	res.setRoles(roles);
    	    	resources.add(res);
    	    	
    	    	role.setResources(resources);
    	    	
    	    	roles.add(role);
    	        user = new User();
    	    	user.setAccount("admin");
    	    	user.setDisabled(false);
    	    	user.setPassword(PasswordUtils.entryptPassword(Constants.securityKey));
    	    	log.info(user.getPassword());
    	    	user.setRoles(roles);	    	
        	}
          return user;//返回UserDetails的实现user不为空,则验证通过
        }
        
    }
    

    UserDetails实现:
    /**
     * 
     */
    package com.ultrapower.me.util.security.entity;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import org.apache.commons.lang.StringUtils;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
     
    public class User implements UserDetails {
    	
    	private static final long serialVersionUID = 8026813053768023527L;
    
       
    	private String account;
    	
    	private String name;
    	
    	private String password;
    	
    	private boolean disabled;
     
    	private Set<Role> roles;
    	
     
    	private Map<String, List<Resource>> roleResources;
    	
    	/**
    	 * The default constructor
    	 */
    	public User() {
    		
    	}
    	
    	/**
    	 * Returns the authorites string
    	 * 
    	 * eg. 
    	 *    downpour --- ROLE_ADMIN,ROLE_USER
    	 *    robbin --- ROLE_ADMIN
    	 * 
    	 * @return
    	 */
    	public String getAuthoritiesString() {
    	    List<String> authorities = new ArrayList<String>();
    	    for(GrantedAuthority authority : this.getAuthorities()) {
    	        authorities.add(authority.getAuthority());
    	    }
    	    return StringUtils.join(authorities, ",");
    	}
    
    	@Override
    	public Collection<? extends GrantedAuthority> getAuthorities() {
    		// 根据自定义逻辑来返回用户权限,如果用户权限返回空或者和拦截路径对应权限不同,验证不通过
    		if(!roles.isEmpty()){
    			List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
    			GrantedAuthority au = new SimpleGrantedAuthority("ROLE_USER");
    			list.add(au);
    			return list;
    		}
    		return null;
    	}
    
    	/* 
    	 * 密码
    	 */
    	public String getPassword() {
    		return password;
    	}
    
    	/* 
    	 * 用户名
    	 */
    	public String getUsername() {
    		return name;
    	}
    
    	/* 
    	 *帐号是否不过期,false则验证不通过
    	 */
    	public boolean isAccountNonExpired() {
    		return true;
    	}
    
    	/* 
    	 * 帐号是否不锁定,false则验证不通过
    	 */
    	public boolean isAccountNonLocked() {
    		return true;
    	}
    
    	/* 
    	 * 凭证是否不过期,false则验证不通过
    	 */
    	public boolean isCredentialsNonExpired() {
    		return true;
    	}
    
    	/* 
    	 * 该帐号是否启用,false则验证不通过
    	 */
    	public boolean isEnabled() {
    		return !disabled;
    	}
    
     
    
    	/**
    	 * @return the name
    	 */
    	public String getName() {
    		return name;
    	}
    
    	/**
    	 * @return the disabled
    	 */
    	public boolean isDisabled() {
    		return disabled;
    	}
    
    	/**
    	 * @return the roles
    	 */
    	public Set<Role> getRoles() {
    		return roles;
    	}
    
    	/**
    	 * @return the roleResources
    	 */
    	public Map<String, List<Resource>> getRoleResources() {
    		// init roleResources for the first time
    		System.out.println("---------------------------------------------------");
    		if(this.roleResources == null) {
    			
    			this.roleResources = new HashMap<String, List<Resource>>();
    			
    			for(Role role : this.roles) {
    				String roleName = role.getRoleName();
    				Set<Resource> resources = role.getResources();
    				for(Resource resource : resources) {
    					String key = roleName + "_" + resource.getType();
    					if(!this.roleResources.containsKey(key)) {
    						this.roleResources.put(key, new ArrayList<Resource>());
    					}
    					this.roleResources.get(key).add(resource);					
    				}
    			}
    			
    		}
    		return this.roleResources;
    	}
    
     
    	/**
    	 * @param name the name to set
    	 */
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	/**
    	 * @param password the password to set
    	 */
    	public void setPassword(String password) {
    		this.password = password;
    	}
    
    	/**
    	 * @param disabled the disabled to set
    	 */
    	public void setDisabled(boolean disabled) {
    		this.disabled = disabled;
    	}
    
    	/**
    	 * @param roles the roles to set
    	 */
    	public void setRoles(Set<Role> roles) {
    		this.roles = roles;
    	}
    
    	public String getAccount() {
    		return account;
    	}
    
    	public void setAccount(String account) {
    		this.account = account;
    	}
    
    	public void setRoleResources(Map<String, List<Resource>> roleResources) {
    		this.roleResources = roleResources;
    	}
    	
    }
    

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

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


     
     
    securityInterceptor继承AbstractSecurityInterceptor过滤器,实现Filter过滤器
    package com.ultrapower.me.util.security.interceptor;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import org.springframework.security.access.SecurityMetadataSource;
    import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
    import org.springframework.security.access.intercept.InterceptorStatusToken;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    
    public class SecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
    
    	//配置文件注入
        private FilterInvocationSecurityMetadataSource securityMetadataSource;
    	
    	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
    		return securityMetadataSource;
    	}
    
    	public void setSecurityMetadataSource(
    			FilterInvocationSecurityMetadataSource securityMetadataSource) {
    		this.securityMetadataSource = securityMetadataSource;
    	}
    
    	@Override
    	public void doFilter(ServletRequest request, ServletResponse response,
    			FilterChain chain) throws IOException, ServletException {
    		// TODO Auto-generated method stub
    		
    		FilterInvocation fi = new FilterInvocation(request, response, chain);
    		//fi里面有一个被拦截的url
            //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
            //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
            InterceptorStatusToken token = super.beforeInvocation(fi);
            try {
    	        //执行下一个拦截器
    	        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());   
            } finally { 
                super.afterInvocation(token, null);  
            }   
    		
    	}
    
    	@Override
    	public void init(FilterConfig arg0) throws ServletException {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public Class<?> getSecureObjectClass() {
    		// TODO Auto-generated method stub
    		return FilterInvocation.class; 
    	}
    
    	@Override
    	public SecurityMetadataSource obtainSecurityMetadataSource() {
    		// TODO Auto-generated method stub
    		return this.securityMetadataSource;   
    	}
    
    	@Override
    	public void destroy() {
    		// TODO Auto-generated method stub
    	}
    }
    

    登陆后,每次访问资源都会被这个拦截器拦截,会执行doFilter这个方法,这个方法调用了invoke方法,其中fi断点显示是一个url(可能重写了toString方法吧,但是里面还有一些方法的),最重要的是beforeInvocation这个方法,它首先会调用MyInvocationSecurityMetadataSource类的getAttributes方法获取被拦截url所需的权限,在调用MyAccessDecisionManager类decide方法判断用户是否够权限。弄完这一切就会执行下一个拦截器。
     
    secureResourceFilterInvocationDefinitionSource实现
    /**
     * 
     */
    package com.ultrapower.me.util.security.interceptor;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    import javax.servlet.ServletContext;
    
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.access.SecurityConfig;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    import org.springframework.util.AntPathMatcher;
    import org.springframework.util.PathMatcher;
    
    
    public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
        
        private PathMatcher matcher;
        
        private static Map<String, Collection<ConfigAttribute>> map = new HashMap<String, Collection<ConfigAttribute>>();
    
        /* 
         * 初始化用户权限,为了简便操作没有从数据库获取
         * 实际操作可以从数据库中获取所有资源路径url所对应的权限
         */
        public void afterPropertiesSet() throws Exception {
            this.matcher = new AntPathMatcher();//用来匹配访问资源路径
            Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); 
            ConfigAttribute ca = new SecurityConfig("ROLE_USER");
            atts.add(ca); 
            map.put("/jsp/index/main.jsp", atts);  
            Collection<ConfigAttribute> attsno =new ArrayList<ConfigAttribute>();
            ConfigAttribute cano = new SecurityConfig("ROLE_NO");
            attsno.add(cano);
            map.put("/http://blog.csdn.net/u012367513/article/details/other.jsp", attsno);   
        }
        
         
    
        @Override
    	public Collection<ConfigAttribute> getAttributes(Object object)
    			throws IllegalArgumentException {
    		// TODO Auto-generated method stub
        	FilterInvocation filterInvocation = (FilterInvocation) object;
        	
        	String requestURI = filterInvocation.getRequestUrl();
        	//循环资源路径,当访问的Url和资源路径url匹配时,返回该Url所需要的权限
            for(Iterator<Map.Entry<String, Collection<ConfigAttribute>>> iter = map.entrySet().iterator(); iter.hasNext();) {
    	          Map.Entry<String, Collection<ConfigAttribute>> entry = iter.next();
    	          String url = entry.getKey();
    	          
    	          if(matcher.match(url, requestURI)) {
    	              return map.get(requestURI);
    	          }
            }
          
    		return null;
    	}
    
    	@Override
    	public Collection<ConfigAttribute> getAllConfigAttributes() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	/* (non-Javadoc)
         * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
         */
    	@SuppressWarnings("rawtypes")
    	public Collection getConfigAttributeDefinitions() {
            return null;
        }
    
        /* (non-Javadoc)
         * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)
         */
    	public boolean supports(@SuppressWarnings("rawtypes") Class clazz) {
            return true;
        }
        
        /**
         * 
         * @param filterInvocation
         * @return
         */
        @SuppressWarnings("unchecked")
    	private Map<String, String> getUrlAuthorities(org.springframework.security.web.FilterInvocation filterInvocation) {
            ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
            return (Map<String, String>)servletContext.getAttribute("urlAuthorities");
        }
    
    }
    

    mesecurityAccessDecisionManager实现
    package com.ultrapower.me.util.security.interceptor;
    
    import java.util.Collection;
    import java.util.Iterator;
    
    import org.springframework.security.access.AccessDecisionManager;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.access.SecurityConfig;
    import org.springframework.security.authentication.InsufficientAuthenticationException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    
    public class SecurityAccessDecisionManager implements AccessDecisionManager {
        
    	/**
    	 * 检查用户是否够权限访问资源
    	 * authentication 是从spring的全局缓存SecurityContextHolder中拿到的,里面是用户的权限信息
    	 * object 是url
    	 * configAttributes 所需的权限
    	 * @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
    	 */
    	@Override
    	public void decide(Authentication authentication, Object object,
    			Collection<ConfigAttribute> configAttributes)
    			throws AccessDeniedException, InsufficientAuthenticationException {
    		// 对应url没有权限时,直接跳出方法
    	   if(configAttributes == null){ 
               return;       
           }  
            
           Iterator<ConfigAttribute> ite=configAttributes.iterator();
           //判断用户所拥有的权限,是否符合对应的Url权限,如果实现了UserDetailsService,则用户权限是loadUserByUsername返回用户所对应的权限
           while(ite.hasNext()){
               ConfigAttribute ca=ite.next();  
               String needRole=((SecurityConfig)ca).getAttribute();
               for(GrantedAuthority ga : authentication.getAuthorities()){ 
            	   System.out.println(":::::::::::::"+ga.getAuthority());
                   if(needRole.equals(ga.getAuthority())){  
                       return;              
                   }            
               }      
           } 
           //注意:执行这里,后台是会抛异常的,但是界面会跳转到所配的access-denied-page页面
           throw new AccessDeniedException("no right");  
    	}
    	@Override
    	public boolean supports(ConfigAttribute attribute) {
    		return true;
    	}
    	@Override
    	public boolean supports(Class<?> clazz) {
    		return true;
    	}
    
    }
    

    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类,用于定义参数,需要实现的方法
    /**
    	 * 凭证,用户密码
    	 */
    	@Override
    	public Object getCredentials() {
    		return password;
    	}
    
    	/**
    	 * 当事人,登录名 用户Id
    	 */
    	@Override
    	public Object getPrincipal() {
    		return userID;
    	}


    2.User类要实现Authentication,需要实现的方法
    /**
    	 * 返回用户所属权限
    	 */
    	@Override
    	public Collection<GrantedAuthority> getAuthorities() {
    		return this.accesses;
    	}
    	
    	@Override
    	public Object getCredentials() {
    		return null;
    	}
    	@Override
    	public Object getDetails() {
    		return null;
    	}
    	/**
    	 * 登录名称
    	 */
    	@Override
    	public Object getPrincipal() {
    		return loginName;
    	}
    	/**
    	 * 是否认证
    	 */
    	@Override
    	public boolean isAuthenticated() {
    		return this.authenticated;
    	}
    	/**
    	 * 设置是否认证字段
    	 */
    	@Override
    	public void setAuthenticated(boolean isAuthenticated)
    			throws IllegalArgumentException {
    		this.authenticated=isAuthenticated;
    	}


    3.需要userService实现AuthenticationProvider的 authenticate(Authentication authentication)方法
      
    @SuppressWarnings("unchecked")
    	@Override
    	public Authentication authenticate(Authentication authentication)
    			throws AuthenticationException {
    		PassCardAuthenticationToken token=(PassCardAuthenticationToken)authentication;
    		/*
    		 * 这里进行逻辑认证操作,可以获取token中的属性来自定义验证逻辑,代码验证逻辑可以不用管
    		 * 如果使用UserDetailsService的实现类来验证,就只能获取userName,不够灵活
    		 */
    		if(token.getUserID()!=null&&token.getPassword()!=null){
    			User user=(User)this.getDao().executeQueryUnique("User.loadByLoginName", QueryCmdType.QUERY_NAME, token.getUserID());
    			
    			String password=token.getPassword();
    			if(this.passwordEncoder!=null){
    				password=this.passwordEncoder.encodePassword(password, null);
    			}
    			
    			if(!password.equalsIgnoreCase(user.getPassword())){
    				
    				token.setErrCode("2");
    				return null;
    			}
    			
    			if( token.isEnablePasscard() && usePassCard ){//token中激活密码卡且系统使用密码卡
    				
    				int position1=((token.getRow1()-1)*7)+token.getColumn1();
    				int position2=((token.getRow2()-1)*7)+token.getColumn2();
    				//System.out.println( "---pos:"+position1+"---"+position2 );
    				
    				if(user.getPassCardId()==null){
    					token.setErrCode("10");
    					return null;
    				}
    				PassCard passcard=this.passCardDao.findById(user.getPassCardId(), false);
    						
    				if(passcard==null||passcard.getStatus()==PassCardHelper.STATUS_CANCEL ){
    					token.setErrCode("10");
    					return null;
    				}
    				if(passcard.getConfusedContent()==null || passcard.getConfusedContent().length()<7*7*32 ){
    					token.setErrCode("10");
    					return null;
    				}
    				
    				String content=passcard.getConfusedContent();
    				int perLen=content.length()/49;
    				String str1=content.substring((position1-1)*perLen, position1*perLen);
    				String str2=content.substring((position2-1)*perLen, position2*perLen);
    				String inputStr1=token.getCard1();
    				String inputStr2=token.getCard2();
    				if(this.passwordEncoder!=null){
    					inputStr1 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr1));
    					inputStr2 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr2));
    				}
    			
    				if((!str1.equalsIgnoreCase(inputStr1))||(!str2.equalsIgnoreCase(inputStr2))){
    					token.setErrCode("10");
    					return null;
    				}
    			}
    			user.setLastIp(token.getIp());
    			user.setLastLogin(new Date());
    			this.getDao().saveOrUpdate(user);			
    			user.setAuthenticated(true);
    			/*
    			 * 导入一次角色权限,并且把权限set到User中,用于spring验证用户权限(getAuthorities方法)
    			 */
    			List<UserRole> userRoles=(List<UserRole>)this.getDao().executeQueryList("UserRole.listRoleByUserID", QueryCmdType.QUERY_NAME, -1, -1, user.getId());
    			Set<GrantedAuthority> accesses=new HashSet<GrantedAuthority>();
    			for(UserRole ur:userRoles){
    				accesses.add(ur.getRole());				
    			}
    			user.getOrg().getOrgName();
    			if(user.getOrg().getCertTypes()!=null) user.getOrg().getCertTypes().size();//延迟载入一下
    			user.setAccesses(accesses);
    			return user;
    		}
    		return null;
    	}
       重写supports(Class<? extends Object> authentication)方法,authentication要
    /**
    	 * 如果此处验证不通过,是不会执行authentication方法的
    	 */
    	@Override
    	public boolean supports(Class<? extends Object> authentication) {
    		return authentication.equals(PassCardAuthenticationToken.class);
    	}

    4.定义filter,实现AbstractAuthenticationProcessingFilter的attemptAuthentication方法,用于获取在登录页面传递过来的参数,spring默认只获取userName(j_username),password(j_username),而且实现UserDetailsService时只传递username
     
    import java.io.IOException;
    import java.util.Date;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.apache.log4j.Logger;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
    import org.springframework.util.StringUtils;
    
    import cn.edu.jszg.cert.user.UserLog;
    import cn.edu.jszg.cert.user.UserLogService;
    import cn.edu.jszg.cert.web.WebApplicationConfiguration;
    import cn.edu.jszg.cert.web.controller.portal.auth.RemoteDataValidator;
    
    import com.google.code.kaptcha.servlet.KaptchaServlet;
    
    public class PasscardAuthenticationProcessingFilter extends
    		AbstractAuthenticationProcessingFilter {
    	private String successPage = "/home/admin/index";
    	private String failurePage = "/public/adminLoginEntry";
    	private boolean forward = false;
    	private boolean useVerifyCode=true;
    	private String certLoginUrl;
    	
    	static Logger logger = Logger.getLogger(PasscardAuthenticationProcessingFilter.class);
    	
    	private WebApplicationConfiguration config;
    	private UserLogService userLogService;	
    	
    	public void setConfig(WebApplicationConfiguration config) {
    		this.config = config;
    	}
    
    	/**
    	 * 实现AbstractAuthenticationProcessingFilter的有参构造
    	 * 没记错的话,相当于该filter的访问路径 
    	 */
    	protected PasscardAuthenticationProcessingFilter() {
    		super("/adminLoginCheck");
    	}
    
    	public void setUseVerifyCode(boolean useVerifyCode) {
    		this.useVerifyCode = useVerifyCode;
    	}
    
    	public void setUserLogService(UserLogService userLogService) {
    		this.userLogService = userLogService;
    	}
    	
    	public boolean validate(HttpServletRequest request) {
    		String userId = request.getParameter("username");
    		String md2 = request.getParameter("m");
    		String l = request.getParameter("l");
    		if (userId == null || md2 == null || l == null) {
    			return false;
    		}
    		long longTime = Long.parseLong(l);
    		if (longTime < new Date().getTime()) {
    			return false;
    		}
    
    		
    		try {
    			String md1 = RemoteDataValidator.genExamMd5Digest(userId, longTime);
    			if (md1.equals(md2))
    				return true;
    			
    		} catch (Exception e) {			
    			//e.printStackTrace();
    		}
    		
    		return false;
    	}
    
    	/**
    	 * 可以通过request获取页面传递过来的参数,并且set到相应的token中
    	 */
    	@Override
    	public Authentication attemptAuthentication(HttpServletRequest request,
    			HttpServletResponse response) throws AuthenticationException,
    			IOException, ServletException {
    		
    //		logger.warn("-----------------start证书登录用户----------");
    		HttpSession s = request.getSession(true);
    		PassCardAuthenticationToken token = new PassCardAuthenticationToken();
    		
    		String verifyCode = request.getParameter("verifyCode");
    		String userID = request.getParameter("username");
    		//....此处省略获取参数,并且验证、赋值的逻辑
    		Authentication auth = null;
    		
    		try {
    			//此处调用getAuthenticationManager的authenticate方法,当supports方法返回true时执行authenticate方法
    			auth = this.getAuthenticationManager().authenticate(token);
    			
    			//此处为登录成功后,相应的处理逻辑
    			if (auth == null || !auth.isAuthenticated()) {
    				s.setAttribute("__login_error", token.getErrCode());
    			} else  {
    				s.removeAttribute("__login_error");
    				s.removeAttribute("__login_username");
    				s.removeAttribute("__cert_userid");
    				if( token.isEnablePasscard()) {
    					s.removeAttribute("__passcard_row1");
    					s.removeAttribute("__passcard_row2");
    					s.removeAttribute("__passcard_column1");
    					s.removeAttribute("__passcard_column2");
    				}
    			}
    		} catch (AuthenticationException e) {
    			s.setAttribute("__login_error", token.getErrCode());
    			throw e;
    		}
    		
    	
    		return auth;
    	}
    
    	public void setSuccessPage(String successPage) {
    		this.successPage = successPage;
    	}
    
    	public void setFailurePage(String failurePage) {
    		this.failurePage = failurePage;
    	}
    
    	public void setForward(boolean forward) {
    		this.forward = forward;
    	}
    
    	public void setCertLoginUrl(String certLoginUrl) {
    		this.certLoginUrl = certLoginUrl;
    	}
    
    	@Override
    	public void afterPropertiesSet() {
    		super.afterPropertiesSet();
    		/*
    		*该处理器实现了AuthenticationSuccessHandler, AuthenticationFailureHandler
    		*用于处理登录成功或者失败后,跳转的界面
    		*/
    		AuthenticationResultHandler handler = new AuthenticationResultHandler();
    		handler.setForward(forward);
    		handler.setLoginFailurePage(failurePage);
    		handler.setLoginSuccessPage(successPage);
    		handler.setCertLoginUrl(certLoginUrl);
    		//设置父类中的处理器
    		this.setAuthenticationSuccessHandler(handler);
    		this.setAuthenticationFailureHandler(handler);
    
    	}
    
    }

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

  • 相关阅读:
    NGINX proxy_pass 域名解析问题
    NGINX源码分析——概览
    NGINX源代码自我总结(一)
    XWindow、Server、Client和QT、GTK之间的关系
    UBUNTU 字符界面来回切换
    ECshop 数据库表结构
    通读Cheerio文档
    Node.js 0.12: 正确发送HTTP POST请求
    npm配置镜像、设置代理
    配置 Windows 下的 nodejs C++ 模块编译环境 安装 node-gyp
  • 原文地址:https://www.cnblogs.com/jiadp/p/9273511.html
Copyright © 2011-2022 走看看