溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何利用spring實現(xiàn)security

發(fā)布時間:2020-11-10 15:17:31 來源:億速云 閱讀:182 作者:Leah 欄目:開發(fā)技術(shù)

如何利用spring實現(xiàn)security?針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

spring security實現(xiàn)方式大致可以分為這幾種:

    1.配置文件實現(xiàn),只需要在配置文件中指定攔截的url所需要權(quán)限、配置userDetailsService指定用戶名、密碼、對應(yīng)權(quán)限,就可以實現(xiàn)。

    2.實現(xiàn)UserDetailsService,loadUserByUsername(String userName)方法,根據(jù)userName來實現(xiàn)自己的業(yè)務(wù)邏輯返回UserDetails的實現(xiàn)類,需要自定義User類實現(xiàn)UserDetails,比較重要的方法是getAuthorities(),用來返回該用戶所擁有的權(quán)限。

    3.通過自定義filter重寫spring security攔截器,實現(xiàn)動態(tài)過濾用戶權(quán)限。

    4.通過自定義filter重寫spring security攔截器,實現(xiàn)自定義參數(shù)來檢驗用戶,并且過濾權(quán)限。

1.最簡單配置spring-security.xml,實現(xiàn)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"> 
   <!-- 配置權(quán)限攔截,訪問所有url,都需要用戶登錄,且擁有ROLE_USER權(quán)限 -->
    <security:intercept-url pattern="/**" access="ROLE_USER" /> 
      
  </security:http> 
 
  <security:authentication-manager alias="authenticationManager"> 
    <security:authentication-provider> 
     <!-- 配置默認(rèn)用戶,用戶名:admin 密碼:123456 擁有權(quán)限: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.實現(xiàn)UserDetailsService

先整理下spring secruity驗證流程:

springSecurity的登錄驗證是由org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter這個過濾器來完成的,在該類的父類AbstractAuthenticationProcessingFilter中有一個AuthenticationManager接口屬性,驗證工作主要是通過這個AuthenticationManager接口的實例來完成的。在默認(rèn)情況下,springSecurity框架會把org.springframework.security.authentication.ProviderManager類的實例注入到該屬性

UsernamePasswordAuthenticationFilter的驗證過程如下:

1. 首先過濾器會調(diào)用自身的attemptAuthentication方法,從request中取出authentication, authentication是在org.springframework.security.web.context.SecurityContextPersistenceFilter過濾器中通過捕獲用戶提交的登錄表單中的內(nèi)容生成的一個org.springframework.security.core.Authentication接口實例.

2. 拿到authentication對象后,過濾器會調(diào)用ProviderManager類的authenticate方法,并傳入該對象

3.ProviderManager類的authenticate方法中會調(diào)用類中的List<AuthenticationProvider> providers集合中的各個AuthenticationProvider接口實現(xiàn)類中的authenticate(Authentication authentication)方法進行驗證,由此可見,真正的驗證邏輯是由各個AuthenticationProvider接口實現(xiàn)類來完成的。DaoAuthenticationProvider類是默認(rèn)情況下注入的一個AuthenticationProvider接口實現(xiàn)類

4.provider的實現(xiàn)類在驗證用戶時,會調(diào)用userDetailsService的實現(xiàn)類的loadUserByUsername方法來獲取用戶信息,

首先spring-security配置文件

<&#63;xml version="1.0" encoding="UTF-8"&#63;>

<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” 需要使用表達式方式來寫權(quán)限-->
 <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登陸成功跳轉(zhuǎn)的page ,authentication-failure-url="/login.jsp&#63;error=true"這里是登陸失敗跳轉(zhuǎn)的page-->
   <form-login login-page="/login.jsp" default-target-url="/jsp/index/main.jsp" authentication-failure-url="/login.jsp&#63;error=true"/>
   <!-- 記住密碼 --> 
<!--   <remember-me key="elim" user-service-ref="securityManager"/> -->
 </http>
 
 <authentication-manager alias="authenticationManager">
 <!-- 
  authentication-provider 引用UserDetailsService實現(xiàn)類時使用user-service-ref屬性,引用authentication實現(xiàn)類時,使用ref屬性
  這兩個屬性的區(qū)別在于 
    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>
 
 <!-- 實現(xiàn)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實現(xiàn):

/**
 * 
 */
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 = &#63; 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的實現(xiàn)user不為空,則驗證通過
  }
  
}

UserDetails實現(xiàn):

/**
 * 
 */
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<&#63; extends GrantedAuthority> getAuthorities() {
 // 根據(jù)自定義邏輯來返回用戶權(quán)限,如果用戶權(quán)限返回空或者和攔截路徑對應(yīng)權(quán)限不同,驗證不通過
 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.實現(xiàn)動態(tài)過濾用戶權(quán)限

在spring-security配置文件的http標(biāo)簽中添加如下配置

<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對應(yīng)的所有權(quán)限 -->
 <beans:bean id="secureResourceFilterInvocationDefinitionSource" class="com.ultrapower.me.util.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />
<!-- 校驗用戶的權(quán)限是否足夠 -->
 <beans:bean id="mesecurityAccessDecisionManager" class="com.ultrapower.me.util.security.interceptor.SecurityAccessDecisionManager" />

securityInterceptor繼承AbstractSecurityInterceptor過濾器,實現(xiàn)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
    //里面調(diào)用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應(yīng)的所有權(quán)限
    //再調(diào)用MyAccessDecisionManager的decide方法來校驗用戶的權(quán)限是否足夠
    InterceptorStatusToken token = super.beforeInvocation(fi);
    try {
     //執(zhí)行下一個攔截器
     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<&#63;> 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
 }
}

登陸后,每次訪問資源都會被這個攔截器攔截,會執(zhí)行doFilter這個方法,這個方法調(diào)用了invoke方法,其中fi斷點顯示是一個url(可能重寫了toString方法吧,但是里面還有一些方法的),最重要的是beforeInvocation這個方法,它首先會調(diào)用MyInvocationSecurityMetadataSource類的getAttributes方法獲取被攔截url所需的權(quán)限,在調(diào)用MyAccessDecisionManager類decide方法判斷用戶是否夠權(quán)限。弄完這一切就會執(zhí)行下一個攔截器。

secureResourceFilterInvocationDefinitionSource實現(xiàn)

/**
 * 
 */
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>>();

  /* 
   * 初始化用戶權(quán)限,為了簡便操作沒有從數(shù)據(jù)庫獲取
   * 實際操作可以從數(shù)據(jù)庫中獲取所有資源路徑url所對應(yīng)的權(quán)限
   */
  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();
   //循環(huán)資源路徑,當(dāng)訪問的Url和資源路徑url匹配時,返回該Url所需要的權(quán)限
    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實現(xiàn)

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 {
  
 /**
 * 檢查用戶是否夠權(quán)限訪問資源
 * authentication 是從spring的全局緩存SecurityContextHolder中拿到的,里面是用戶的權(quán)限信息
 * object 是url
 * configAttributes 所需的權(quán)限
 * @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 {
 // 對應(yīng)url沒有權(quán)限時,直接跳出方法
  if(configAttributes == null){ 
      return;    
    } 
    
    Iterator<ConfigAttribute> ite=configAttributes.iterator();
    //判斷用戶所擁有的權(quán)限,是否符合對應(yīng)的Url權(quán)限,如果實現(xiàn)了UserDetailsService,則用戶權(quán)限是loadUserByUsername返回用戶所對應(yīng)的權(quán)限
    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;       
        }      
      }   
    } 
    //注意:執(zhí)行這里,后臺是會拋異常的,但是界面會跳轉(zhuǎn)到所配的access-denied-page頁面
    throw new AccessDeniedException("no right"); 
 }
 @Override
 public boolean supports(ConfigAttribute attribute) {
 return true;
 }
 @Override
 public boolean supports(Class<&#63;> clazz) {
 return true;
 }

}

4.實現(xiàn)AuthenticationProvider,自定義參數(shù)驗證

這種驗證以前項目用過,現(xiàn)在沒有寫示例代碼,先寫下大概流程和需要用到的類

這種驗證的好處:可以在自定義登錄界面添加登錄時需要的參數(shù),如多個驗證碼等、可以修改默認(rèn)登錄名稱和密碼的參數(shù)名

整體流程:

1.用戶登錄時,先經(jīng)過自定義的passcard_filter過濾器,該過濾器繼承了AbstractAuthenticationProcessingFilter,并且綁定了登錄失敗和成功時需要的處理器(跳轉(zhuǎn)頁面使用)

2.執(zhí)行attemptAuthentication方法,可以通過request獲取登錄頁面?zhèn)鬟f的參數(shù),實現(xiàn)自己的邏輯,并且把對應(yīng)參數(shù)set到AbstractAuthenticationToken的實現(xiàn)類中

3.驗證邏輯走完后,調(diào)用 this.getAuthenticationManager().authenticate(token);方法,執(zhí)行AuthenticationProvider的實現(xiàn)類的supports方法

4.如果返回true則繼續(xù)執(zhí)行authenticate方法

5.在authenticate方法中,首先可以根據(jù)用戶名獲取到用戶信息,再者可以拿自定義參數(shù)和用戶信息做邏輯驗證,如密碼的驗證

6.自定義驗證通過以后,獲取用戶權(quán)限set到User中,用于springSecurity做權(quán)限驗證

7.this.getAuthenticationManager().authenticate(token)方法執(zhí)行完后,會返回Authentication,如果不為空,則說明驗證通過

8.驗證通過后,可實現(xiàn)自定義邏輯操作,如記錄cookie信息

9.attemptAuthentication方法執(zhí)行完成后,由springSecuriy來進行對應(yīng)權(quán)限驗證,成功于否會跳轉(zhuǎn)到相對應(yīng)處理器設(shè)置的界面。

1.自定義PassCardAuthenticationToken類,繼承AbstractAuthenticationToken類,用于定義參數(shù),需要實現(xiàn)的方法

/**
 * 憑證,用戶密碼
 */
 @Override
 public Object getCredentials() {
 return password;
 }

 /**
 * 當(dāng)事人,登錄名 用戶Id
 */
 @Override
 public Object getPrincipal() {
 return userID;
 }

2.User類要實現(xiàn)Authentication,需要實現(xiàn)的方法

/**
 * 返回用戶所屬權(quán)限
 */
 @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;
 }
 /**
 * 是否認(rèn)證
 */
 @Override
 public boolean isAuthenticated() {
 return this.authenticated;
 }
 /**
 * 設(shè)置是否認(rèn)證字段
 */
 @Override
 public void setAuthenticated(boolean isAuthenticated)
  throws IllegalArgumentException {
 this.authenticated=isAuthenticated;
 }

3.需要userService實現(xiàn)AuthenticationProvider的 authenticate(Authentication authentication)方法

  @SuppressWarnings("unchecked")
 @Override
 public Authentication authenticate(Authentication authentication)
  throws AuthenticationException {
 PassCardAuthenticationToken token=(PassCardAuthenticationToken)authentication;
 /*
  * 這里進行邏輯認(rèn)證操作,可以獲取token中的屬性來自定義驗證邏輯,代碼驗證邏輯可以不用管
  * 如果使用UserDetailsService的實現(xiàn)類來驗證,就只能獲取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中激活密碼卡且系統(tǒng)使用密碼卡
  
  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);
  /*
  * 導(dǎo)入一次角色權(quán)限,并且把權(quán)限set到User中,用于spring驗證用戶權(quán)限(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<&#63; extends Object> authentication)方法,authentication要

/**
 * 如果此處驗證不通過,是不會執(zhí)行authentication方法的
 */
 @Override
 public boolean supports(Class<&#63; extends Object> authentication) {
 return authentication.equals(PassCardAuthenticationToken.class);
 }

4.定義filter,實現(xiàn)AbstractAuthenticationProcessingFilter的attemptAuthentication方法,用于獲取在登錄頁面?zhèn)鬟f過來的參數(shù),spring默認(rèn)只獲取userName(j_username),password(j_username),而且實現(xiàn)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;
 }

 /**
 * 實現(xiàn)AbstractAuthenticationProcessingFilter的有參構(gòu)造
 * 沒記錯的話,相當(dāng)于該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獲取頁面?zhèn)鬟f過來的參數(shù),并且set到相應(yīng)的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");
 //....此處省略獲取參數(shù),并且驗證、賦值的邏輯
 Authentication auth = null;
 
 try {
  //此處調(diào)用getAuthenticationManager的authenticate方法,當(dāng)supports方法返回true時執(zhí)行authenticate方法
  auth = this.getAuthenticationManager().authenticate(token);
  
  //此處為登錄成功后,相應(yīng)的處理邏輯
  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();
 /*
 *該處理器實現(xiàn)了AuthenticationSuccessHandler, AuthenticationFailureHandler
 *用于處理登錄成功或者失敗后,跳轉(zhuǎn)的界面
 */
 AuthenticationResultHandler handler = new AuthenticationResultHandler();
 handler.setForward(forward);
 handler.setLoginFailurePage(failurePage);
 handler.setLoginSuccessPage(successPage);
 handler.setCertLoginUrl(certLoginUrl);
 //設(shè)置父類中的處理器
 this.setAuthenticationSuccessHandler(handler);
 this.setAuthenticationFailureHandler(handler);

 }

}

最后為spring-security配置文件中的配置,需要添加authentication-provider的引用,和filter的配置

<security:authentication-manager alias="authenticationManager">
 <!-- 注意,這里僅僅是系統(tǒng)默認(rèn)的認(rèn)證機制,請在正式系統(tǒng)中明確知道其功能再使用 -->
 <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"/>

關(guān)于如何利用spring實現(xiàn)security問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI