javascript
【Spring】12、Spring Security 四种使用方式
spring security使用分類:
如何使用spring security,相信百度過的都知道,總共有四種用法,從簡到深為:1、不用數(shù)據(jù)庫,全部數(shù)據(jù)寫在配置文件,這個也是官方文檔里面的demo;2、使用數(shù)據(jù)庫,根據(jù)spring security默認實現(xiàn)代碼設計數(shù)據(jù)庫,也就是說數(shù)據(jù)庫已經固定了,這種方法不靈活,而且那個數(shù)據(jù)庫設計得很簡陋,實用性差;3、spring security和Acegi不同,它不能修改默認filter了,但支持插入filter,所以根據(jù)這個,我們可以插入自己的filter來靈活使用;4、暴力手段,修改源碼,前面說的修改默認filter只是修改配置文件以替換filter而已,這種是直接改了里面的源碼,但是這種不符合OO設計原則,而且不實際,不可用。
本文面向讀者:
因為本文準備介紹第三種方法,所以面向的讀者是已經具備了spring security基礎知識的。不過不要緊,讀者可以先看一下這個教程,看完應該可以使用第二種方法開發(fā)了。
spring security的簡單原理:
使用眾多的攔截器對url攔截,以此來管理權限。但是這么多攔截器,筆者不可能對其一一來講,主要講里面核心流程的兩個。 首先,權限管理離不開登陸驗證的,所以登陸驗證攔截器AuthenticationProcessingFilter要講; 還有就是對訪問的資源管理吧,所以資源管理攔截器AbstractSecurityInterceptor要講; 但攔截器里面的實現(xiàn)需要一些組件來實現(xiàn),所以就有了AuthenticationManager、accessDecisionManager等組件來支撐。 現(xiàn)在先大概過一遍整個流程,用戶登陸,會被AuthenticationProcessingFilter攔截,調用AuthenticationManager的實現(xiàn),而且AuthenticationManager會調用ProviderManager來獲取用戶驗證信息(不同的Provider調用的服務不同,因為這些信息可以是在數(shù)據(jù)庫上,可以是在LDAP服務器上,可以是xml配置文件上等),如果驗證通過后會將用戶的權限信息封裝一個User放到spring的全局緩存SecurityContextHolder中,以備后面訪問資源時使用。 訪問資源(即授權管理),訪問url時,會通過AbstractSecurityInterceptor攔截器攔截,其中會調用FilterInvocationSecurityMetadataSource的方法來獲取被攔截url所需的全部權限,在調用授權管理器AccessDecisionManager,這個授權管理器會通過spring的全局緩存SecurityContextHolder獲取用戶的權限信息,還會獲取被攔截的url和被攔截url所需的全部權限,然后根據(jù)所配的策略(有:一票決定,一票否定,少數(shù)服從多數(shù)等),如果權限足夠,則返回,權限不夠則報錯并調用權限不足頁面。 雖然講得好像好復雜,讀者們可能有點暈,不過不打緊,真正通過代碼的講解在后面,讀者可以看完后面的代碼實現(xiàn),再返回看這個簡單的原理,可能會有不錯的收獲。?
spring security使用實現(xiàn)(基于spring security3.1.4):
javaEE的入口:web.xml:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!--?xml version="1.0" encoding="UTF-8"?--> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> ?????<!--加載Spring XML配置文件 --> ????<context-param> ????????<param-name>contextConfigLocation</param-name> ????????<param-value> classpath:securityConfig.xml?????????? </param-value> ????</context-param> ??????<!-- Spring Secutiry3.1的過濾器鏈配置 --> ????<filter> ????????<filter-name>springSecurityFilterChain</filter-name> ????????<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> ????</filter> ????<filter-mapping> ????????<filter-name>springSecurityFilterChain</filter-name> ????????<url-pattern>/*</url-pattern> ????</filter-mapping> ???????<!-- Spring 容器啟動監(jiān)聽器 --> ????<listener> ????????<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> ????</listener>?? ??????<!--系統(tǒng)歡迎頁面 --> ????<welcome-file-list> ????????<welcome-file>index.jsp</welcome-file> ????</welcome-file-list> </web-app> |
上面那個配置不用多說了吧 直接上spring security的配置文件securityConfig.xml:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <!--?xml version="1.0" encoding="UTF-8"?--> <b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="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.1.xsd"> ??<!--登錄頁面不過濾 --> ????<http pattern="/login.jsp" security="none"> ????<http access-denied-page="/accessDenied.jsp"> ????????<form-login login-page="/login.jsp"> ????????<!--訪問/http://blog.csdn.net/u012367513/article/details/admin.jsp資源的用戶必須具有ROLE_ADMIN的權限 --> ????????<!-- <intercept-url pattern="/http://blog.csdn.net/u012367513/article/details/admin.jsp" access="ROLE_ADMIN" /> --> ????????<!--訪問/**資源的用戶必須具有ROLE_USER的權限 --> ????????<!-- <intercept-url pattern="/**" access="ROLE_USER" /> --> ????????<session-management> ????????????<concurrency-control max-sessions="1" error-if-maximum-exceeded="false"> ????????</concurrency-control></session-management> ????????<!--增加一個filter,這點與 Acegi是不一樣的,不能修改默認的filter了, 這個filter位于FILTER_SECURITY_INTERCEPTOR之前 --> ????????<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"> ????</custom-filter></form-login></http> ????<!--一個自定義的filter,必須包含 authenticationManager,accessDecisionManager,securityMetadataSource三個屬性, ????????我們的所有控制將在這三個類中實現(xiàn),解釋詳見具體配置 --> ????<b:bean id="myFilter" class="com.erdangjiade.spring.security.MyFilterSecurityInterceptor"> ????????<b:property name="authenticationManager" ref="authenticationManager"> ????????<b:property name="accessDecisionManager" ref="myAccessDecisionManagerBean"> ????????<b:property name="securityMetadataSource" ref="securityMetadataSource"> ????</b:property></b:property></b:property></b:bean> ????<!--驗證配置,認證管理器,實現(xiàn)用戶認證的入口,主要實現(xiàn)UserDetailsService接口即可 --> ????? ????????? ????????????<!--如果用戶的密碼采用加密的話 <password-encoder hash="md5" /> --> ????????</authentication-provider> ????</authentication-manager> ????<!--在這個類中,你就可以從數(shù)據(jù)庫中讀入用戶的密碼,角色信息,是否鎖定,賬號是否過期等 --> ????<b:bean id="myUserDetailService" class="com.erdangjiade.spring.security.MyUserDetailService"> ????<!--訪問決策器,決定某個用戶具有的角色,是否有足夠的權限去訪問某個資源 --> ????<b:bean id="myAccessDecisionManagerBean" class="com.erdangjiade.spring.security.MyAccessDecisionManager"> ????</b:bean> ????<!--資源源數(shù)據(jù)定義,將所有的資源和權限對應關系建立起來,即定義某一資源可以被哪些角色訪問 --> ????<b:bean id="securityMetadataSource" class="com.erdangjiade.spring.security.MyInvocationSecurityMetadataSource"> ??????????? ?</b:bean></b:bean></http></b:beans> |
其實所有配置都在里面,首先這個版本的spring security不支持了filter=none的配置了,改成了獨立的,里面你可以配登陸頁面、權限不足的返回頁面、注銷頁面等,上面那些配置,我注銷了一些資源和權限的對應關系,筆者這里不需要在這配死它,可以自己寫攔截器來獲得資源與權限的對應關系。 session-management是用來防止多個用戶同時登陸一個賬號的。
最重要的是筆者自己寫的攔截器myFilter(終于講到重點了),首先這個攔截器會加載在FILTER_SECURITY_INTERCEPTOR之前(配置文件上有說),最主要的是這個攔截器里面配了三個處理類,第一個是authenticationManager,這個是處理驗證的,這里需要特別說明的是:這個類不單只這個攔截器用到,還有驗證攔截器AuthenticationProcessingFilter也用到 了,而且實際上的登陸驗證也是AuthenticationProcessingFilter攔截器調用authenticationManager來處理的,我們這個攔截器只是為了拿到驗證用戶信息而已(這里不太清楚,因為authenticationManager筆者設了斷點,用戶登陸后再也沒調用這個類了,而且調用這個類時不是筆者自己寫的那個攔截器調用的,看了spring技術內幕這本書才知道是AuthenticationProcessingFilter攔截器調用的)。 securityMetadataSource這個用來加載資源與權限的全部對應關系的,并提供一個通過資源獲取所有權限的方法。
accessDecisionManager這個也稱為授權器,通過登錄用戶的權限信息、資源、獲取資源所需的權限來根據(jù)不同的授權策略來判斷用戶是否有權限訪問資源。
authenticationManager類可以有許多provider(提供者)提供用戶驗證信息,這里筆者自己寫了一個類myUserDetailService來獲取用戶信息。
MyUserDetailService:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package com.erdangjiade.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 { ????? ????//登陸驗證時,通過username獲取用戶的所有權限信息, ????//并返回User放到spring的全局緩存SecurityContextHolder中,以供授權器使用 ????public UserDetails loadUserByUsername(String username) ????????????throws UsernameNotFoundException, DataAccessException {?? ????????Collection<grantedauthority> auths=new ArrayList<grantedauthority>(); ????????? ????????GrantedAuthorityImpl auth2=new GrantedAuthorityImpl("ROLE_ADMIN"); ????????GrantedAuthorityImpl auth1=new GrantedAuthorityImpl("ROLE_USER"); ????????? ????????if(username.equals("lcy")){ ????????????auths=new ArrayList<grantedauthority>(); ????????????auths.add(auth1); ????????????auths.add(auth2);????? ????????}???? ????????? ????????User user = new User(username, "lcy", true, true, true, true, auths); ????????return user;? ????????} ????} </grantedauthority></grantedauthority></grantedauthority> |
其中UserDetailsService接口是spring提供的,必須實現(xiàn)的。別看這個類只有一個方法,而且這么簡單,其中內涵玄機。 讀者看到這里可能就大感疑惑了,不是說好的用數(shù)據(jù)庫嗎?對,但別急,等筆者慢慢給你們解析。 首先,筆者為什么不用數(shù)據(jù)庫,還不是為了讀者們測試方便,并簡化spring security的流程,讓讀者抓住主線,而不是還要煩其他事(導入數(shù)據(jù)庫,配置數(shù)據(jù)庫,寫dao等)。 這里筆者只是用幾個數(shù)據(jù)模擬了從數(shù)據(jù)庫中拿到的數(shù)據(jù),也就是說ROLE_ADMIN、ROLE_USER、lcy(第一個是登陸賬號)、lcy(第二個是密碼)是從數(shù)據(jù)庫拿出來的,這個不難實現(xiàn)吧,如果需要數(shù)據(jù)庫時,讀者可以用自己寫的dao通過參數(shù)username來查詢出這個用戶的權限信息(或是角色信息,就是那個ROLE_*,對必須是ROLE_開頭的,不然spring security不認賬的,其實是spring security里面做了一個判斷,必須要ROLE_開頭,讀者可以百度改一下),再返回spring自帶的數(shù)據(jù)模型User即可。 這個寫應該比較清晰、靈活吧,總之數(shù)據(jù)讀者們通過什么方法獲取都行,只要返回一個User對象就行了。(這也是筆者為什么要重寫這個類的原因)?
通過MyUserDetailService拿到用戶信息后,authenticationManager對比用戶的密碼(即驗證用戶),然后這個AuthenticationProcessingFilter攔截器就過咯。?
下面要說的是另外一個攔截器,就是筆者自己寫的攔截器MyFilterSecurityInterceptor:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | package com.erdangjiade.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; public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor? implements Filter {? ????? ????//配置文件注入 ????private FilterInvocationSecurityMetadataSource securityMetadataSource; ????? ????//登陸后,每次訪問資源都通過這個攔截器攔截 ????public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ????????FilterInvocation fi = new FilterInvocation(request, response, chain); ????????invoke(fi);? ????????} ????? ????public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {? ????????return this.securityMetadataSource;? ????????}?? ????? ????public Class<!--? extends Object--> getSecureObjectClass() { ????????return FilterInvocation.class;??? ????????}? ????? ????public void invoke(FilterInvocation fi) throws IOException, ServletException { ????????//fi里面有一個被攔截的url ????????//里面調用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的所有權限 ????????//再調用MyAccessDecisionManager的decide方法來校驗用戶的權限是否足夠 ????????InterceptorStatusToken token = super.beforeInvocation(fi); ????????try { ????????????//執(zhí)行下一個攔截器 ????????????fi.getChain().doFilter(fi.getRequest(), fi.getResponse());?? ????????????} finally { ????????????????super.afterInvocation(token, null);? ????????????}?? ????????}? ????public SecurityMetadataSource obtainSecurityMetadataSource() { ????????return this.securityMetadataSource;?? ????????} ????public void setSecurityMetadataSource( ????????????FilterInvocationSecurityMetadataSource newSource) ????{ ????????this.securityMetadataSource = newSource; ????} ????public void destroy() {? ????????? ????}?? ????public void init(FilterConfig arg0) throws ServletException {? ????????? ????}? } |
繼承AbstractSecurityInterceptor、實現(xiàn)Filter是必須的。 首先,登陸后,每次訪問資源都會被這個攔截器攔截,會執(zhí)行doFilter這個方法,這個方法調用了invoke方法,其中fi斷點顯示是一個url(可能重寫了toString方法吧,但是里面還有一些方法的),最重要的是beforeInvocation這個方法,它首先會調用MyInvocationSecurityMetadataSource類的getAttributes方法獲取被攔截url所需的權限,在調用MyAccessDecisionManager類decide方法判斷用戶是否夠權限。弄完這一切就會執(zhí)行下一個攔截器。?
再看一下這個MyInvocationSecurityMetadataSource的實現(xiàn):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package com.erdangjiade.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 com.erdangjiade.spring.security.tool.AntUrlPathMatcher; import com.erdangjiade.spring.security.tool.UrlMatcher; public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { ????private UrlMatcher urlMatcher = new AntUrlPathMatcher(); ????private static Map<string, collection<configattribute="">> resourceMap = null; ????? ????//tomcat啟動時實例化一次 ????public MyInvocationSecurityMetadataSource() { ????????loadResourceDefine();? ????????}?? ????//tomcat開啟時加載一次,加載所有url和權限(或角色)的對應關系 ????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 cano = new SecurityConfig("ROLE_NO"); ????????attsno.add(cano); ????????resourceMap.put("/http://blog.csdn.net/u012367513/article/details/other.jsp", attsno);?? ????????}? ????? ????//參數(shù)是要訪問的url,返回這個url對于的所有權限(或角色) ????public Collection<configattribute> getAttributes(Object object) throws IllegalArgumentException { ????????// 將參數(shù)轉為url??? ????????String url = ((FilterInvocation)object).getRequestUrl();?? ????????Iterator<string>ite = resourceMap.keySet().iterator(); ????????while (ite.hasNext()) {???????? ????????????String resURL = ite.next();? ????????????if (urlMatcher.pathMatchesUrl(resURL, url)) { ????????????????return resourceMap.get(resURL);???????? ????????????????}?????? ????????????} ????????return null;??? ????????}? ????public boolean supports(Class<!--?-->clazz) { ????????????return true;? ????????????} ????public Collection<configattribute> getAllConfigAttributes() { ????????return null;? ????????} ????} </configattribute></string></configattribute></configattribute></configattribute></configattribute></configattribute></string,></string,> |
實現(xiàn)FilterInvocationSecurityMetadataSource接口也是必須的。 首先,這里也是模擬了從數(shù)據(jù)庫中獲取信息。 其中l(wèi)oadResourceDefine方法不是必須的,這個只是加載所有的資源與權限的對應關系并緩存起來,避免每次獲取權限都訪問數(shù)據(jù)庫(提高性能),然后getAttributes根據(jù)參數(shù)(被攔截url)返回權限集合。 這種緩存的實現(xiàn)其實有一個缺點,因為loadResourceDefine方法是放在構造器上調用的,而這個類的實例化只在web服務器啟動時調用一次,那就是說loadResourceDefine方法只會調用一次,如果資源和權限的對應關系在啟動后發(fā)生了改變,那么緩存起來的就是臟數(shù)據(jù),而筆者這里使用的就是緩存數(shù)據(jù),那就會授權錯誤了。但如果資源和權限對應關系是不會改變的,這種方法性能會好很多。 現(xiàn)在說回有數(shù)據(jù)庫的靈活實現(xiàn),讀者看到這,可能會說,這還不簡單,和上面MyUserDetailService類一樣使用dao靈活獲取數(shù)據(jù)就行啦。 如果讀者這樣想,那只想到了一半,想一下spring的機制(依賴注入),dao需要依賴注入吧,但這是在啟動時候,那個dao可能都還沒加載,所以這里需要讀者自己寫sessionFactory,自己寫hql或sql,對,就在loadResourceDefine方法里面寫(這個應該會寫吧,基礎來的)。那如果說想用第二種方法呢(就是允許資源和權限的對應關系改變的那個),那更加簡單,根本不需要loadResourceDefine方法了,直接在getAttributes方法里面調用dao(這個是加載完,后來才會調用的,所以可以使用dao),通過被攔截url獲取數(shù)據(jù)庫中的所有權限,封裝成Collection返回就行了。(靈活、簡單) 注意:接口UrlMatcher和實現(xiàn)類AntUrlPathMatcher是筆者自己寫的,這本來是spring以前版本有的,現(xiàn)在沒有了,但是覺得好用就用會來了,直接上代碼(讀者也可以自己寫正則表達式驗證被攔截url和緩存或數(shù)據(jù)庫的url是否匹配):
| 1 2 3 4 5 6 7 8 | package com.erdangjiade.spring.security.tool; public interface UrlMatcher{ ????Object compile(String paramString); ????boolean pathMatchesUrl(Object paramObject, String paramString); ????String getUniversalMatchPattern(); ????boolean requiresLowerCaseUrl(); } |
?
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | package com.erdangjiade.spring.security.tool; import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; ??public class AntUrlPathMatcher implements UrlMatcher {? ??????private boolean requiresLowerCaseUrl; ??????private PathMatcher pathMatcher; ??????public AntUrlPathMatcher()?? { ??????????this(true); ??????? ??}? ??????public AntUrlPathMatcher(boolean requiresLowerCaseUrl) ??????{? ??????????this.requiresLowerCaseUrl = true; ??????this.pathMatcher = new AntPathMatcher(); ??????this.requiresLowerCaseUrl = requiresLowerCaseUrl; ??????} ??????? ??????public Object compile(String path) { ??????????if (this.requiresLowerCaseUrl) { ??????????????return path.toLowerCase();? ??????????????}?? ??????????return path;? ??????}? ??????? ??????public void setRequiresLowerCaseUrl(boolean requiresLowerCaseUrl){ ??????????? ??????????this.requiresLowerCaseUrl = requiresLowerCaseUrl; ??????} ??????? ??????public boolean pathMatchesUrl(Object path, String url) { ??????????if (("/**".equals(path)) || ("**".equals(path))) { ??????????????return true;???? ??????????????}? ??????????? ??????????return this.pathMatcher.match((String)path, url); ??????} ??????? ??????public String getUniversalMatchPattern() { ??????????return"/**";? ??????} ??????? ??????public boolean requiresLowerCaseUrl() { ??????????return this.requiresLowerCaseUrl;? ??????}? ??????? ??????public String toString() {? ??????????return super.getClass().getName() + "[requiresLowerCase='" ??????+ this.requiresLowerCaseUrl + "']";? ??????} ??} |
然后MyAccessDecisionManager類的實現(xiàn):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package com.erdangjiade.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 { ????? ????//檢查用戶是否夠權限訪問資源 ????//參數(shù)authentication是從spring的全局緩存SecurityContextHolder中拿到的,里面是用戶的權限信息 ????//參數(shù)object是url ????//參數(shù)configAttributes所需的權限 ????public void decide(Authentication authentication, Object object,??? ????????????Collection<configattribute> configAttributes) ????????????????????throws AccessDeniedException, InsufficientAuthenticationException { ????????if(configAttributes == null){ ????????????return;?????? ????????}? ????????? ????????Iterator<configattribute> ite=configAttributes.iterator(); ????????while(ite.hasNext()){ ????????????ConfigAttribute ca=ite.next();? ????????????String needRole=((SecurityConfig)ca).getAttribute(); ????????????for(GrantedAuthority ga : authentication.getAuthorities()){ ????????????????if(needRole.equals(ga.getAuthority())){? ????????????????????? ????????????????????return;????????????? ????????}??????????? ????}????? } ????????//注意:執(zhí)行這里,后臺是會拋異常的,但是界面會跳轉到所配的access-denied-page頁面 ????????throw new AccessDeniedException("no right");?? }?? ????public boolean supports(ConfigAttribute attribute) { ????????return true; ????}? ????public boolean supports(Class<!--?-->clazz) { ????????return true; ????????} ????}</configattribute></configattribute> |
接口AccessDecisionManager也是必須實現(xiàn)的。 decide方法里面寫的就是授權策略了,筆者的實現(xiàn)是,沒有明說需要權限的(即沒有對應的權限的資源),可以訪問,用戶具有其中一個或多個以上的權限的可以訪問。這個就看需求了,需要什么策略,讀者可以自己寫其中的策略邏輯。通過就返回,不通過拋異常就行了,spring security會自動跳到權限不足頁面(配置文件上配的)。?
就這樣,整個流程過了一遍。?
剩下的頁面代碼
本來想給這個demo的源碼出來的,但是筆者覺得,通過這個教程一步一步讀下來,并自己敲一遍代碼,會比直接運行一遍demo印象更深刻,并且更容易理解里面的原理。 而且我的源碼其實都公布出來了: login.jsp:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <%@page language="java" import="java.util.*" pageEncoding="UTF-8"%> <title>登錄</title> ????<form action="j_spring_security_check" method="POST"> ????<table> ????????<tbody><tr> ????????????<td>用戶:</td> ????????????<td><input type="'text'" name="'j_username'"></td> ????????</tr> ????????<tr> ????????????<td>密碼:</td> ????????????<td><input type="'password'" name="'j_password'"></td> ????????</tr> ????????<tr> ????????????<td><input name="reset" type="reset"></td> ????????????<td><input name="submit" type="submit"></td> ????????</tr> ????</tbody></table> ????</form> |
index.jsp:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>? <%@taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>? ?? ?? ?? <title>My?JSP?'index.jsp'?starting?page</title>? ?? ????? <h3>這是首頁</h3>歡迎 ????<sec:authentication property="name"> ! ????? ????<br>? ????進入admin頁面? ????進入其它頁面? ?? ?? </sec:authentication> |
http://blog.csdn.net/u012367513/article/details/admin.jsp:
?| 1 2 3 4 5 6 7 8 9 | <%@page language="java" import="java.util.*" pageEncoding="utf-8"%> <title>My JSP 'http://blog.csdn.net/u012367513/article/details/admin.jsp' starting page</title> ????歡迎來到管理員頁面. ????<br> |
accessDenied.jsp:
| 1 2 3 4 5 6 7 8 9 | <%@page language="java" import="java.util.*" pageEncoding="utf-8"%> <title>My JSP 'http://blog.csdn.net/u012367513/article/details/admin.jsp' starting page</title> ????歡迎來到管理員頁面. ????<br> |
http://blog.csdn.net/u012367513/article/details/other.jsp:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> ??? ????<base href="<%=basePath%>"> ????? ????<title>My JSP 'http://blog.csdn.net/u012367513/article/details/other.jsp' starting page</title> ????? ????<meta http-equiv="pragma" content="no-cache"> ????<meta http-equiv="cache-control" content="no-cache"> ????<meta http-equiv="expires" content="0">??? ????<meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> ????<meta http-equiv="description" content="This is my page"> ????<!-- ????<link rel="stylesheet" type="text/css" href="styles.css"> ????--> ??? ??? ??? ????<h3>這里是Other頁面</h3> ?? |
項目圖:
<img src="http://www.2cto.com/uploadfile/Collfiles/20140829/20140829091240286.png" alt="n峨n竩�漽j喎�" http:="" www.2cto.com="" ym"="" target="_blank" class="keylink" style="border-width: 0px; padding: 0px; margin: 0px; list-style: none; width: 322px; height: 464px;">源碼和jar包都在這個教程里面,為什么不直接給?筆者的目的是讓讀者跟著教程敲一遍代碼,使印象深刻(相信做這行的都知道,同樣一段代碼,看過和敲過的區(qū)別是多么的大),所以不惜如此來強迫大家了。?
轉自:http://blog.csdn.net/u013516966/article/details/46688765
總結
以上是生活随笔為你收集整理的【Spring】12、Spring Security 四种使用方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器双网卡冗余备份技术的实现
- 下一篇: 将weex项目打包的关键点