不知道個人web.xml 你們都理解了沒。 廢話確實有點多,可能不少知識點,你們都知道,但是我學的時候,壓根什麼都不懂啊....java
這篇咱們要講劊子手 securityConfig。 爲何要說他是劊子手呢? 由於他是無良掌櫃的小工,直接的操盤手......web
<?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-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <http access-denied-page = "/accessDenied.jsp"> <!-- 訪問拒絕頁面 --> <form-login login-page="/login.jsp"/> <!-- 定義登錄界面 --> <intercept-url pattern="/login.jsp" filters="none"/> <session-management> <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/> <!-- 用戶最大登陸數設置爲1 ,超過則引起異常 --> </session-management> <custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/> <!-- 自定義FILTER ,FilterSecurityInterceptor 負責受權--> </http> <!-- myFilter --> <beans:bean id = "myFilter" class = "com.qbt.spring.security.MyFilterSecurityInterceptor"> <beans:property name="authenticationManager" ref ="authenticationManager"></beans:property> <!-- 登錄驗證 ,驗證你的用戶名密碼噼裏啪啦--> <beans:property name="securityMetadataSource" ref = "securityMetadataSource"></beans:property> <!-- 資源數據源的定義 ,神馬權限對應神馬資源 噼裏啪啦--> <beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean"></beans:property> <!-- 訪問決策 有沒有權限訪問資源 噼裏啪啦--> </beans:bean> <!-- 驗證配置,認證管理器,實現UserDetailService接口 --> <!-- authenticationManager 能夠有多個provider提供信息,咱們用myUserDetailService獲取信息 --> <!-- Spring Security中進行身份驗證的是AuthenticationManager接口,ProviderManager是它的一個默認實現, 但它並不用來處理身份認證,而是委託給配置好的AuthenticationProvider,每一個AuthenticationProvider會輪流檢查身份認證。 檢查後或者返回Authentication對象或者拋出異常 --> <authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="myUserDetailService"></authentication-provider> </authentication-manager> <!-- 獲取user數據,能夠從數據庫中獲取用戶密碼,角色等! --> <beans:bean id = "myUserDetailService" class = "com.qbt.spring.security.MyUserDetailService"></beans:bean> <!-- 訪問決策器,決定用戶的角色,訪問的權限 --> <beans:bean id = "myAccessDecisionManagerBean" class = "com.qbt.spring.security.MyAccessDecisionManager"></beans:bean> <!-- 資源數據源的定義 什麼資源對應什麼權限,或者什麼資源能被什麼角色訪問--> <beans:bean id = "securityMetadataSource" class = "com.qbt.spring.security.MyInvocationSecurityMetadataSource"></beans:bean> </beans:beans>
能夠看出來,這裏的核心過濾器是咱們本身實現的MyFilterSecurityInterceptorspring
//下面,貼出他的代碼sql
package com.qbt.spring.security; 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; /** *登錄後,每次訪問資源都會訪問這個攔截器 ,執行doFilter, 調用invoke, *super.beforeInvocation() 會調用SecurityMetadataSource 的getAttributes方法獲取fi對應的全部權限,再調用decide的方法,判斷是否有權限 *而後調用下一個攔截器。 * */ public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{ //配置文件注入 private FilterInvocationSecurityMetadataSource securityMetadataSource; //登錄後,每次訪問資源都經過這個攔截器攔截 @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } private void invoke(FilterInvocation fi) throws IOException, ServletException { //fi 封裝了request,response,chain //裏面調用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的全部權限 //再調用MyAccessDecisionManager的decide方法來校驗用戶的權限是否足夠 InterceptorStatusToken token = super.beforeInvocation(fi); //這裏的token裏面究竟是什麼呢? 八一八 來! /** * 源碼--- * protected InterceptorStatusToken beforeInvocation(Object object) { Assert.notNull(object, "Object was null"); //預言 //省略 Collection attributes = obtainSecurityMetadataSource().getAttributes(object); //調用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的全部權限 if(attributes == null) { //拋異常省略 return null; } //省略 //判斷是否須要對認證明體從新認證,默認爲否 Authentication authenticated = authenticateIfRequired(); try {//decide方法來校驗用戶的權限是否足夠 accessDecisionManager.decide(authenticated, object, attributes); } catch(AccessDeniedException accessDeniedException) {//拋異常 publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; } //省略 return new InterceptorStatusToken(authenticated, true, attributes, object); } * */ try{ fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally{ super.afterInvocation(token, null); } } @Override public void init(FilterConfig arg0) throws ServletException { } //相對應的MyAccessDecisionManager的supports方法必須放回true,不然會提醒類型錯誤 @Override public Class<? extends Object> getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource ){ this.securityMetadataSource = newSource; } @Override public void destroy() { } }
//累啊...數據庫
而後咱們來看看 authenticationManager緩存
package com.qbt.spring.security; import java.util.ArrayList; import java.util.Collection; import org.springframework.dao.DataAccessException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; public class MyUserDetailService implements UserDetailsService{ /** * 獲取用戶信息,返回User放到Spring的全局緩存SecurityContentHolder中,讓其餘過濾器使用 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { UserDetails user = null; Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>(); //定義一個權限集合 GrantedAuthorityImpl auth1 = new GrantedAuthorityImpl("ROLE_ADMIN"); //定義一個管理員權限 GrantedAuthorityImpl auth2 = new GrantedAuthorityImpl("ROLE_USER"); //定義一個用戶權限 System.out.println("**********MyUserDetailService登錄驗證,經過用戶名獲取權限************"); if(username.equals("admin")){ auths.add(auth2); auths.add(auth1); }else{ auths.add(auth2); } // username, password, enable, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities user = new User(username, "admin", true, true, true, true, auths); return user; } }
而後再看看MyInvocationSecurityMetadataSourcetomcat
package com.qbt.spring.security; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; 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.security.web.util.AntUrlPathMatcher; import org.springframework.security.web.util.UrlMatcher; /** * @author ORC * FilterInvocationSecurityMetadataSource 繼承於 SecurityMetadataSource * */ public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { private UrlMatcher urlMatcher = new AntUrlPathMatcher(); public static Map<String,Collection<ConfigAttribute>> resourceMap = null; public MyInvocationSecurityMetadataSource () { loadResourceDefine(); } /** * 獲取角色和資源的相對應的關係,在tomcat啓動時加載。 * 由於是在tomcat啓動時就要加載,因此若是權限對應關係改變的話,就須要從新獲取 * 若是要調用Dao的話,可是在啓動的時候,這個Dao可能尚未加載 * 因此若是要在這裏調用數據庫的話,要本身寫sessionfaction,sql/hql * 還有一種方法就是,在getAttributes方法裏調用Dao,由於這個方法是在tomcat啓動以後才調用的 */ private void loadResourceDefine() { resourceMap = new HashMap<String,Collection<ConfigAttribute>>(); Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); ConfigAttribute ca = new SecurityConfig("ROLE_USER"); atts.add(ca); resourceMap.put("/index.jsp", atts); Collection<ConfigAttribute> attsNo = new ArrayList<ConfigAttribute>(); ConfigAttribute no = new SecurityConfig("ROLE_NO"); attsNo.add(no); resourceMap.put("/other.jsp", attsNo); System.out.println("*************MyInvocationSecurityMetadataSource 調用,獲取角色和資源對應值*******************"); } @Override public Collection<ConfigAttribute> getAttributes(Object obj) throws IllegalArgumentException { String url = ((FilterInvocation)obj).getRequestUrl(); System.out.println("*************MyInvocationSecurityMetadataSource getAttribute調用,獲取資源所對應的角色集合*******************"); Iterator<String>ite = resourceMap.keySet().iterator(); while(ite.hasNext()){ String resUrl = ite.next(); if(urlMatcher.pathMatchesUrl(resUrl, url)) { return resourceMap.get(resUrl); } } return null; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> class1) { return true; } }
再看MyAccessDecisionManager安全
package com.qbt.spring.security; 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 MyAccessDecisionManager implements AccessDecisionManager{ @Override public void decide(Authentication authentication, Object obj, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if(configAttributes == null) { return; } System.out.println("*************MyAccessDecesionManager 判斷用戶是否有權限**************"); Iterator<ConfigAttribute> itr = configAttributes.iterator(); while(itr.hasNext()){ ConfigAttribute ca = itr.next(); String needRole = ((SecurityConfig)ca).getAttribute(); for(GrantedAuthority ga : authentication.getAuthorities()){ if(needRole.equals(ga.getAuthority())){ return; } } } throw new AccessDeniedException("No Right"); } //這個 supports(ConfigAttribute) 方法在啓動的時候被 //AbstractSecurityInterceptor調用,來決定AccessDecisionManager //是否能夠執行傳遞ConfigAttribute @Override public boolean supports(ConfigAttribute configattribute) { return false; } //supports(Class)方法被安全攔截器實現調用, //包含安全攔截器將顯示的AccessDecisionManager支持安全對象的類型。 @Override public boolean supports(Class<?> class1) { return true; } }