您好,登錄后才能下訂單哦!
如何利用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"/>
關于如何利用spring實現security問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。