最近項目須要添加權限攔截,經討論決定採用spring security4.2.2!廢話少說直接上乾貨!css
spring security 4.2.2文檔:http://docs.spring.io/spring-security/site/docs/4.2.2.RELEASE/reference/htmlsingle/#el-access-webhtml
spring security 3 中文2文檔:http://www.mossle.com/docs/auth/html/index.htmljava
須要在pom.xml裏配置spring security的依賴,能夠經過http://mvnrepository.com/search?q=spring+securitye查詢不一樣版本須要的spring的版本支持。jquery
1 <!--spring security --> 2 <dependency> 3 <groupId>org.springframework.security</groupId> 4 <artifactId>spring-security-core</artifactId> 5 <version>4.2.2.RELEASE</version> 6 </dependency> 7 8 <dependency> 9 <groupId>org.springframework.security</groupId> 10 <artifactId>spring-security-web</artifactId> 11 <version>4.2.2.RELEASE</version> 12 </dependency> 13 14 <dependency> 15 <groupId>org.springframework.security</groupId> 16 <artifactId>spring-security-config</artifactId> 17 <version>4.2.2.RELEASE</version> 18 </dependency> 19 <dependency> 20 <groupId>org.springframework.security</groupId> 21 <artifactId>spring-security-taglibs</artifactId> 22 <version>4.2.2.RELEASE</version> 23 </dependency>
須要在web.xml中配置Spring Security控制權限過濾器,web
1 <filter> 2 <filter-name>springSecurityFilterChain</filter-name> 3 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>springSecurityFilterChain</filter-name> 7 <url-pattern>/*</url-pattern> 8 </filter-mapping>
下面最主要的就是spring-security.xml的配置了算法
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns="http://www.springframework.org/schema/security" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/security 8 http://www.springframework.org/schema/security/spring-security.xsd" > 9 10 <!-- 打印調試信息,僅在開發環境中使用 --> 11 <!-- <debug/> --> 12 13 <!-- 不須要被攔截的請求 --> 14 <http pattern="/loginPage" security="none"/> 15 <http pattern="/scripts/**" security="none"/> 16 17 <!-- 18 登陸頁面可使用第一種: 19 <http pattern="/login.jsp" security="none"></http> 20 <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" ...... 21 這種方式直接訪問.jsp 22 也可使用Controller來控制,兩種方式登陸頁面login.jsp的位置不同!!! 23 下面是第二種方式: 24 <http pattern="/login" security="none"></http> 25 <form-login login-page="/login" login-processing-url="/login" ...... 26 第一個配置告訴spring security,相似於/login的url請求不作過濾處理,而第二個配置信息又告訴spring security url爲/login的post請求登陸請求處理。正是這種衝突致使了405錯誤的發生。 27 既然知道了錯誤緣由,那麼只要避免衝突就能解決這個問題:使登陸頁的請求和登陸處理的請求不一致,而後只配置登陸頁的請求不作攔截處理. 28 我採用的方法是使用默認的登陸URL /login,修改登陸頁面跳轉url爲/loginPage。請自行修改代碼和配置信息 29 這樣是否是就大工告成了呢?很遺憾,當你啓動程序時,輸出用戶名和密碼,無論正確與否,都會有404 Not Found等着你,這又是爲何呢? 30 登陸請求404錯誤 31 首先,建議你看一下spring security自動生成的登陸頁源碼,你會發現有以下代碼 32 <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> 33 對於什麼是csrf,請自行參考網上的資料。 34 spring security默認狀況下csrf protection是開啓的,因爲咱們的登陸頁沒有配置csrf的相關信息,所以spring security內置的過濾器將此連接置爲無效連接 35 解決辦法就是配置csrf protection爲不可用狀態,在配置文件中增長 36 <csrf disabled="true"/> 37 --> 38 39 <!-- 40 一、<http auto-config="true">,他能夠自動配置login form,BSIC 認證和logout URL 和logout services,若是沒有特殊代表,這個的默認值是false。想要本身配置則設置爲"true" 41 二、Spring Security採用的是一種就近原則,就是說當用戶訪問的url資源知足多個intercepter-url時,系統將使用第一個符合條件的intercept-url進行權限控制 42 --> 43 <http auto-config="true" use-expressions="true"> 44 <!-- 45 另外一種權限表達式: 46 <http use-expressions="false"> 47 <intercept-url pattern='/**' access='ROLE_USER' /> 48 --> 49 <!-- 禁用CSRF保護,默認是啓用 --> 50 <csrf disabled="true"/> 51 52 <anonymous enabled="false"/> 53 54 <!-- 基於角色認證(必須擁有ROLE_XXX角色才能訪問全部/**/XXX/**資源) --> 55 <!-- 確保對功能URL訪問都須要權限 --> 56 <intercept-url pattern="/**/add/**" access="hasRole('ADD')"/> 57 <intercept-url pattern="/**/update/**" access="hasRole('UPDATE')"/> 58 <intercept-url pattern="/**/delete/**" access="hasRole('DELETE')"/> 59 <intercept-url pattern="/**/download/**" access="hasRole('DOWNLOAD')"/> 60 <intercept-url pattern="/**/access/**" access="hasRole('ADMIN')"/> 61 62 <!-- Ensures that any request to our application requires the user to be authenticated --> 63 <intercept-url pattern="/**" access="authenticated"/> 64 65 66 <!-- 67 實現免登錄驗證,默認有效時間是兩週,啓用rememberMe以後的兩週內,用戶均可以直接跳過系統,直接進入系統。 68 實際上,Spring Security中的rememberMe是依賴cookie實現的,當用戶在登陸時選擇使用rememberMe,系統就會在登陸成功後將爲用戶生成一個惟一標識,並將這個標識保存進cookie中 69 Spring Security生成的cookie名稱是SPRING_SECURITY_REMEMBER_ME_COOKIE,它的內容是一串加密的字符串, 70 當用戶再次訪問系統時,Spring Security將從這個cookie讀取用戶信息,並加以驗證。若是能夠證明cookie有效,就會自動將用戶登陸到系統中,併爲用戶授予對應的權限。 71 --> 72 73 <!-- 74 <remember-me remember-me-parameter="remember-me" 75 data-source-ref="dataSource"/> 76 spring security還提供了remember me的另外一種相對更安全的實現機制 :在客戶端的cookie中,僅保存一個無心義的加密串(與用戶名、密碼等敏感數據無關),而後在db中保存該加密串-用戶信息的對應關係,自動登陸時,用cookie中的加密串,到db中驗證,若是經過,自動登陸纔算經過。會自動在你的數據庫裏建立一個表:PERSISTENT_LOGINS。 77 若是不加data-source-ref="dataSource",會將上述信息放在內存中! 78 做者的項目有些特殊要求,全部採用了下面的方式:登陸後要在myAuthenticationSuccessHandler作特殊的處理! 79 --> 80 <remember-me authentication-success-handler-ref="myAuthenticationSuccessHandler"/> 81 82 <!-- 83 login-page : 表示用戶登錄時顯示咱們自定義的登陸頁面 84 authentication-failure-url : 登陸認證失敗轉向的url,當用戶輸入的登陸名和密碼不正確時,系統將再次跳轉到登陸頁面,並添加一個error=true參數做爲登錄失敗的標示,這個標識是咱們自定義的。 85 default-target-url : 登陸認證成功轉向的地址 86 --> 87 <form-login 88 login-page="/loginPage" 89 authentication-failure-url="/loginPage?error=true" 90 authentication-success-handler-ref="myAuthenticationSuccessHandler" 91 /> 92 93 <!-- 登出後,返回到登錄頁面 --> 94 <!-- <logout logout-success-url="/loginPage" logout-url="/logout"/> 95 delete-cookies="JSESSIONID":退出刪除JSESSIONID 96 --> 97 <logout /> 98 <!-- 99 控制同步session的過濾器 100 若是concurrency-control標籤配置了error-if-maximum-exceeded="true",max-sessions="1",那麼第二次登陸時,是登陸不了的。 101 若是error-if-maximum-exceeded="false",那麼第二次是可以登陸到系統的, 102 可是第一個登陸的帳號再次發起請求時,會跳轉到expired-url配置的url中, 103 若是沒有配置expired-url,則顯示: 104 This session has been expired (possibly due to multiple concurrent logins being attempted as the same user). 105 翻譯過來意思就是說:這個會話已通過期(可能因爲多個併發登陸嘗試相同的用戶) 106 --> 107 108 <session-management invalid-session-url="/loginPage" > 109 <concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/loginPage"/> 110 </session-management> 111 112 <!-- 指定本身的權限驗證過濾器,首先走本身的的過濾器 myFilter,若是被攔截就報沒有權限; 113 若是經過會走spring security自帶的攔截器,即上面配置的權限配置! 114 --> 115 <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter"/> 116 </http> 117 118 <!-- 權限認證Spring日誌監聽器 --> 119 <beans:bean class="org.springframework.security.authentication.event.LoggerListener"/> 120 <beans:bean class="org.springframework.security.access.event.LoggerListener"/> 121 122 <!-- 123 一個自定義的filter,必須包含authenticationManager,accessDecisionManager,securityMetadataSource三個屬性, 124 咱們的全部控制將在這三個類中實現,解釋詳見具體配置。 125 --> 126 <beans:bean id="myFilter" class="com.tcbd.common.interceptor.MyFilterSecurityInterceptor" > 127 <beans:property name="authenticationManager" ref="authenticationManager" /> 128 <beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" /> 129 <beans:property name="securityMetadataSource" ref="securityMetadataSource" /> 130 </beans:bean> 131 132 <!-- 驗證配置,實現用戶認證的入口,主要實現UserDetailsService接口便可 --> 133 <authentication-manager alias="authenticationManager" > 134 <authentication-provider ref="daoAuthenticationProvider" > 135 </authentication-provider> 136 </authentication-manager> 137 138 <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> 139 <beans:property name="userDetailsService" ref="myUserDetailService" /> 140 <beans:property name="passwordEncoder" ref="passwordEncoder" /> 141 </beans:bean> 142 <!-- spring推薦的單向加密算法 --> 143 <beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> 144 145 <!-- 在這個類中,讀入用戶的密碼,角色信息,是否鎖定,帳號是否過時等屬性信息 --> 146 <beans:bean id="myUserDetailService" class="com.tcbd.common.interceptor.MyUserDetailService" /> 147 148 149 <!-- 訪問決策器,決定某個用戶具備的角色,是否有足夠的權限去訪問某個資源 --> 150 <beans:bean id="myAccessDecisionManagerBean" class="com.tcbd.common.interceptor.MyAccessDecisionManager" ></beans:bean> 151 152 <!-- 資源源數據定義,即定義某一資源能夠被哪些角色訪問 --> 153 <beans:bean id="securityMetadataSource" class="com.tcbd.common.interceptor.MyFilterSecurityMetadataSource" /> 154 155 <beans:bean id="myAuthenticationSuccessHandler" class="com.tcbd.common.interceptor.MyAuthenticationSuccessHandler"/> 156 157 </beans:beans>
spring security爲咱們提供了三種通配符。
通配符:?
示例:/admin/g?t.jsp
匹配任意一個字符,/admin/g?t.jsp能夠匹配/admin/get.jsp和/admin/got.jsp或是/admin/gxt.do。不能匹配/admin/xxx.jsp。
通配符:*
示例:/admin/*.jsp
匹配任意多個字符,但不能跨越目錄。/*/index.jsp能夠匹配/admin/index.jsp和/user/index.jsp,可是不能匹配/index.jsp和/user/test/index.jsp。
通配符:**
示例:/**/index.jsp
能夠匹配任意多個字符,能夠跨越目錄,能夠匹配/index.jsp,/admin/index.jsp,/user/admin/index.jsp和/a/b/c/d/index.jspspring
1 import java.util.ArrayList; 2 import java.util.Collection; 3 4 import javax.annotation.Resource; 5 6 import org.apache.commons.lang.StringUtils; 7 import org.apache.log4j.Logger; 8 import org.springframework.dao.DataAccessException; 9 import org.springframework.security.core.GrantedAuthority; 10 import org.springframework.security.core.authority.SimpleGrantedAuthority; 11 import org.springframework.security.core.userdetails.User; 12 import org.springframework.security.core.userdetails.UserDetails; 13 import org.springframework.security.core.userdetails.UserDetailsService; 14 import org.springframework.security.core.userdetails.UsernameNotFoundException; 15 16 /** 17 * 從數據庫中讀入用戶的密碼,角色信息,是否鎖定,帳號是否過時等 18 * 19 */ 20 public class MyUserDetailService implements UserDetailsService { 21 @Resource 22 private UserService userService; 23 24 /** 25 * 數據庫交互獲取用戶擁有的權限角色,並設置權限 26 */ 27 @Override 28 public UserDetails loadUserByUsername(String userCode) throws UsernameNotFoundException, DataAccessException { 29 // 根據登陸用戶名獲取用戶信息 30 Users user = new Users(); 31 user.setUserCode(username); 32 user = userService.selectByModel(user); 33 if (null != user) { 34 // 存放權限 35 Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>(); 36 String action = user.getRole(); 37 if (StringUtils.isNotBlank(action)) { 38 String[] roleaCtion = action.split(","); 39 for (int i = 0; i < roleaCtion.length; i++) { 40 SimpleGrantedAuthority auth = new SimpleGrantedAuthority(roleaCtion[i]); 41 auths.add(auth); 42 } 43 } 44 //spring security自帶的User對象 45 User userDetails = new User(username, user.getPassword(), true, true, true, true, auths); 46 return userDetails; 47 } 48 return null; 49 } 50 }
1 import java.util.ArrayList; 2 import java.util.Collection; 3 import java.util.List; 4 import java.util.Map; 5 6 import javax.servlet.http.HttpServletRequest; 7 8 import org.apache.log4j.Logger; 9 import org.springframework.security.access.ConfigAttribute; 10 import org.springframework.security.access.SecurityConfig; 11 import org.springframework.security.web.FilterInvocation; 12 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; 13 14 public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { 15 private static Logger logger = Logger.getLogger(MyFilterSecurityMetadataSource.class); 16 17 public List<ConfigAttribute> getAttributes(Object object) { 18 FilterInvocation fi = (FilterInvocation) object; 19 HttpServletRequest request = fi.getRequest(); 20 String requestUrl = fi.getRequest().getRequestURI(); 21 List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(); 22 // 全部URL對應的角色,應用啓動就存放到靜態資源裏,獲得的結果是:不一樣的URL下,包含的多個角色 23 Map<String, String> resRoles = Constant.URL_ROLES; 24 25 for (Map.Entry<String, String> ent : resRoles.entrySet()) { 26 String url = ent.getKey(); 27 String roles = ent.getValue(); 28 //根據業務寫本身的匹配邏輯 29 if(requestUrl.startsWith(url)){ 30 attributes.addAll(SecurityConfig.createListFromCommaDelimitedString(roles)); 31 } 32 } 33 logger.debug("【"+request.getRequestURI()+"】 roles: "+attributes); 34 return attributes; 35 } 36 37 38 public Collection<ConfigAttribute> getAllConfigAttributes() { 39 return null; 40 } 41 42 public boolean supports(Class<?> clazz) { 43 return FilterInvocation.class.isAssignableFrom(clazz); 44 } 45 }
1 import java.util.Collection; 2 import java.util.Iterator; 3 4 import org.apache.commons.logging.Log; 5 import org.apache.commons.logging.LogFactory; 6 import org.springframework.security.access.AccessDecisionManager; 7 import org.springframework.security.access.AccessDeniedException; 8 import org.springframework.security.access.ConfigAttribute; 9 import org.springframework.security.access.SecurityConfig; 10 import org.springframework.security.authentication.InsufficientAuthenticationException; 11 import org.springframework.security.core.Authentication; 12 import org.springframework.security.core.GrantedAuthority; 13 14 /** 15 * 在這種方法中,須要與configAttributes比較驗證 16 * 一、一個對象是一個URL,一個過濾器被這個URL找到權限配置,並經過這裏 17 * 二、若是沒有匹配相應的認證,AccessDeniedException 18 * 19 */ 20 public class MyAccessDecisionManager implements AccessDecisionManager { 21 22 private static final Log logger = LogFactory.getLog(MyAccessDecisionManager.class); 23 24 /** 25 * 在這個類中,最重要的是decide方法,若是不存在對該資源的定義,直接放行; 不然,若是找到正確的角色,即認爲擁有權限,並放行,不然throw 26 * new AccessDeniedException("no right");這樣,就會進入上面提到的/accessDenied.jsp頁面。 27 * @param authentication :當前用戶所且有的角色 28 * @param object :當前請求的URL 29 * @param configAttributes :當前URL所且有的角色 30 */ 31 @Override 32 public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) 33 throws AccessDeniedException, InsufficientAuthenticationException { 34 // 資源所需的角色列表,若是角色列表爲空,則放行!繼續下一個攔截器。 35 if (configAttributes == null) { 36 return; 37 } 38 // 即將訪問的資源URL,如 : /admin.jsp 39 logger.info("URL :"+object); 40 // 遍歷所需的角色集合 41 Iterator<ConfigAttribute> ite = configAttributes.iterator(); 42 while (ite.hasNext()) { 43 ConfigAttribute ca = ite.next(); 44 // 該資源所須要的角色 45 String needRole = ((SecurityConfig) ca).getAttribute(); 46 // authentication.getAuthorities()獲取用戶所擁有的角色列表,如:OLE_DEFULT 47 for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { 48 // 將資源所須要的角色與用戶擁有的角色比較 49 if (needRole.equals(grantedAuthority.getAuthority())) { 50 // grantedAuthority is user's role. 51 // 角色相同,直接放行 52 return; 53 } 54 } 55 } 56 // 不然,提示沒有權限訪問該資源 57 throw new AccessDeniedException("no right"); 58 } 59 60 @Override 61 public boolean supports(ConfigAttribute attribute) { 62 return true; 63 } 64 65 @Override 66 public boolean supports(Class<?> clazz) { 67 return true; 68 } 69 70 }
1 import java.io.IOException; 2 3 import javax.servlet.Filter; 4 import javax.servlet.FilterChain; 5 import javax.servlet.FilterConfig; 6 import javax.servlet.ServletException; 7 import javax.servlet.ServletRequest; 8 import javax.servlet.ServletResponse; 9 10 import org.springframework.security.access.SecurityMetadataSource; 11 import org.springframework.security.access.intercept.AbstractSecurityInterceptor; 12 import org.springframework.security.access.intercept.InterceptorStatusToken; 13 import org.springframework.security.web.FilterInvocation; 14 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; 15 16 /** 17 * 18 */ 19 public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor 20 implements Filter { 21 22 private FilterInvocationSecurityMetadataSource securityMetadataSource; 23 24 /* get、set方法 */ 25 public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { 26 return securityMetadataSource; 27 } 28 29 public void setSecurityMetadataSource( 30 FilterInvocationSecurityMetadataSource securityMetadataSource) { 31 this.securityMetadataSource = securityMetadataSource; 32 } 33 34 @Override 35 public Class<? extends Object> getSecureObjectClass() { 36 return FilterInvocation.class; 37 } 38 39 @Override 40 public SecurityMetadataSource obtainSecurityMetadataSource() { 41 return this.securityMetadataSource; 42 } 43 44 @Override 45 public void doFilter(ServletRequest request, ServletResponse response, 46 FilterChain chain) throws IOException, ServletException { 47 48 FilterInvocation fi = new FilterInvocation(request, response, chain); 49 invoke(fi); 50 } 51 52 public void invoke(FilterInvocation fi) throws IOException, 53 ServletException { 54 /** 55 * 最核心的代碼就是@link InterceptorStatusToken token = super.beforeInvocation(fi); 56 * 它會調用咱們定義的MyInvocationSecurityMetadataSource.getAttributes方法和MyAccessDecisionManager.decide方法 57 * 這一句,即在執行doFilter以前,進行權限的檢查,而具體的實現已經交給@link MyAccessDecisionManager 了 58 */ 59 InterceptorStatusToken token = super.beforeInvocation(fi); 60 try { 61 //繼續走下一個攔截器,也就是org.springframework.security.web.access.intercept.FilterSecurityInterceptor 62 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); 63 } finally { 64 super.afterInvocation(token, null); 65 } 66 } 67 68 @Override 69 public void destroy() { 70 71 } 72 73 @Override 74 public void init(FilterConfig arg0) throws ServletException { 75 76 } 77 78 }
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <%@ taglib prefix='fmt' uri="http://java.sun.com/jsp/jstl/fmt" %> 4 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 5 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> 6 <!DOCTYPE html> 7 <html lang="zh-CN"> 8 <head> 9 <meta charset="utf-8"> 10 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 11 <meta name="viewport" content="width=device-width, initial-scale=1"> 12 <!-- 上述3個meta標籤*必須*放在最前面,任何其餘內容都*必須*跟隨其後! --> 13 <meta name="description" content=""> 14 <meta name="author" content=""> 15 <link rel="icon" href="/favicon.ico"> 16 17 <title><fmt:message key="application.title"></fmt:message></title> 18 19 <!-- Bootstrap core CSS --> 20 <link href="/scripts/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet"> 21 22 <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> 23 <!-- Custom styles for this template --> 24 25 <link href="/scripts/apps/css/signin.css" rel="stylesheet"> 26 27 <style type="text/css"> 28 .tip { 29 font-size: 10px; 30 color: red; 31 text-align: center; 32 } 33 34 </style> 35 </head> 36 37 <body> 38 <div class="container"> 39 <c:url var="loginUrl" value="/login" /> 40 <form action="${loginUrl}" id="login_form" class="form-signin" method="post"> 41 <div class="form-signin-heading text-center"> 42 <h2 ><fmt:message key="application.title"></fmt:message></h2> 43 </div> 44 <div id="tip" class="tip"></div> 45 <c:if test="${param.error != null}"> 46 <span style="color:red">用戶名或密碼有誤</span> 47 </c:if> 48 49 <label for="inputEmail" class="sr-only">登陸賬號</label> 50 <input type="text" id="inputUserCode" name="username" class="form-control" placeholder="登陸賬號" autofocus> 51 <label for="inputPassword" class="sr-only">登陸密碼</label> 52 <input type="password" id="inputPassword" name="password" class="form-control" placeholder="登陸密碼"> 53 <div class="input-group input-sm"> 54 <div class="checkbox"> 55 <label><input type="checkbox" id="rememberme" name="remember-me" value="true"> 下次自動登陸</label> 56 </div> 57 </div> 58 <button id="login_button" class="btn btn-lg btn-primary btn-block" type="submit">登陸</button> 59 </form> 60 61 </div> 62 63 <script src="/scripts/plugins/jQuery/jquery-2.2.3.min.js"></script> 64 </body> 65 </html>
在JSP中相應的操做按鈕上加上以下標籤,控制顯示:數據庫
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> 該標籤必定要加上!!!
<sec:authorize access="hasRole('ADD')">
<a href="/XXX/add">增長</a>
</sec:authorize>express
數據庫設計:user表裏有roleId,role表,resource-role表,resource表apache