最近项目须要添加权限拦截,经讨论决定采用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