使用Mongodb+Shiro+SpringMVC实现动态权限分配
此次的文档只对Mongodb整合Shiro并且实现动态权限分配做整理,其它的内容以后会补上。
第一步、创建在web.xml中配置 Spring 、Shiro
shiroFilter 过滤器是用来将请求交给shiro来管理
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 5 id="WebApp_ID" version="2.5"> 6 <welcome-file-list> 7 <welcome-file></welcome-file> 8 </welcome-file-list> 9 10 <!-- 加载spring的配置 --> 11 <context-param> 12 <param-name>contextConfigLocation</param-name> 13 <param-value>classpath:application-config.xml,classpath:dispatcher-shiro.xml</param-value> 14 </context-param> 15 <filter> 16 <filter-name>encodingFilter</filter-name> 17 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 18 <init-param> 19 <param-name>encoding</param-name> 20 <param-value>utf-8</param-value> 21 </init-param> 22 <init-param> 23 <param-name>forceEncoding</param-name> 24 <param-value>true</param-value> 25 </init-param> 26 </filter> 27 <filter-mapping> 28 <filter-name>encodingFilter</filter-name> 29 <url-pattern>/*</url-pattern> 30 </filter-mapping> 31 <filter> 32 <filter-name>XSSCheckFilter</filter-name> 33 <filter-class>com.java.filter.XSSCheckFilter</filter-class> 34 <init-param> 35 <param-name>errorPath</param-name> 36 <param-value>/fail/error</param-value> 37 </init-param> 38 </filter> 39 <filter-mapping> 40 <filter-name>XSSCheckFilter</filter-name> 41 <url-pattern>/*</url-pattern> 42 </filter-mapping> 43 <filter> 44 <filter-name>shiroFilter</filter-name> 45 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 46 <init-param> 47 <param-name>targetFilterLifecycle</param-name> 48 <param-value>true</param-value> 49 </init-param> 50 </filter> 51 <filter-mapping> 52 <filter-name>shiroFilter</filter-name> 53 <url-pattern>/*</url-pattern> 54 </filter-mapping> 55 <listener> 56 <listener-class>com.java.listener.Log4jListener</listener-class> 57 </listener> 58 <listener> 59 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 60 </listener> 61 <servlet> 62 <servlet-name>dispatcherServlet</servlet-name> 63 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 64 <init-param> 65 <param-name>contextConfigLocation</param-name> 66 <param-value>classpath:dispatcher-servlet.xml,classpath:dispatcher-shiro.xml</param-value> 67 </init-param> 68 <load-on-startup>1</load-on-startup> 69 </servlet> 70 <servlet-mapping> 71 <servlet-name>dispatcherServlet</servlet-name> 72 <url-pattern>/</url-pattern> 73 </servlet-mapping> 74 <error-page> 75 <error-code>404</error-code> 76 <location>/fail/nopage</location> 77 </error-page> 78 <error-page> 79 <exception-type>org.apache.shiro.authz.UnauthorizedException</exception-type> 80 <location>/fail/nopromission</location> 81 </error-page> 82 <error-page> 83 <exception-type>org.apache.shiro.authz.AuthorizationException</exception-type> 84 <location>/fail/nopromission</location> 85 </error-page> 86 </web-app>
第二步、创建application-config.xml 用来配置Mongodb链接数据库 并且注入mongodbTemplate
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mongo="http://www.springframework.org/schema/data/mongo" 5 xsi:schemaLocation=" 6 http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-4.1.xsd 10 http://www.springframework.org/schema/tx 11 http://www.springframework.org/schema/tx/spring-tx-4.1.xsd 12 http://www.springframework.org/schema/data/mongo 13 http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd"> 14 <!-- 自动扫包 --> 15 <context:component-scan base-package="com.java.*"> 16 <context:exclude-filter type="annotation" 17 expression="org.springframework.stereotype.Controller" /> 18 </context:component-scan> 19 20 <mongo:mongo host="127.0.0.1" port="27017"> 21 <mongo:options connections-per-host="10" 22 threads-allowed-to-block-for-connection-multiplier="5" 23 connect-timeout="10000" max-wait-time="120000" auto-connect-retry="true" 24 socket-keep-alive="false" socket-timeout="0" slave-ok="false" 25 write-number="1" write-timeout="0" write-fsync="true" /> 26 </mongo:mongo> 27 28 <mongo:db-factory dbname="webFramework" mongo-ref="mongo" /> 29 <!-- <mongo:db-factory dbname="webFrameworktest" mongo-ref="mongo" /> --> 30 31 <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> 32 <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" /> 33 </bean> 34 35 </beans>
第三步、创建dispatch-servlet.xml 配置文件, 配置springMVC的相关配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 9 http://www.springframework.org/schema/aop 10 http://www.springframework.org/schema/aop/spring-aop-4.1.xsd 11 http://www.springframework.org/schema/context 12 http://www.springframework.org/schema/context/spring-context-4.1.xsd 13 http://www.springframework.org/schema/tx 14 http://www.springframework.org/schema/tx/spring-tx-4.1.xsd 15 http://www.springframework.org/schema/mvc 16 http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd"> 17 <!-- 加载Properties文件 --> 18 <!-- <bean id="configurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 19 <property name="locations"> <list> <value>classpath:email.properties</value> 20 </list> </property> </bean> --> 21 22 <mvc:view-controller path="/" view-name="redirect:user/login" /> 23 24 <aop:aspectj-autoproxy /> 25 <!-- 注解探测器(组件扫描)@Controller --> 26 <context:component-scan base-package="com.java.*"> 27 <context:exclude-filter type="annotation" 28 expression="org.springframework.stereotype.Repository" /> 29 <context:exclude-filter type="annotation" 30 expression="org.springframework.stereotype.Service" /> 31 </context:component-scan> 32 33 <!-- 启动springMVC的注解功能,他会自动注册HandlerMapping,HandlerAdapter,ExceptionResolver的相关实例 --> 34 <mvc:annotation-driven> 35 <mvc:message-converters register-defaults="true"> 36 <bean class="org.springframework.http.converter.StringHttpMessageConverter"> 37 <property name="supportedMediaTypes" value="text/plain;charset=UTF-8" /> 38 </bean> 39 <ref bean="mappingJacksonHttpMessageConverter" /> 40 </mvc:message-converters> 41 </mvc:annotation-driven> 42 <!-- enable autowire --> 43 44 45 <mvc:view-controller path="/" view-name="redirect:user/login"/><!--设置默认的主页 --> 46 47 <!-- 处理JSON数据转换的 --> 48 <bean id="mappingJacksonHttpMessageConverter" 49 class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> 50 <!-- 为了处理返回的JSON数据的编码,默认是ISO-88859-1的,这里把它设置为UTF-8,解决有乱码的情况 --> 51 <property name="supportedMediaTypes"> 52 <list> 53 <value>application/json;charset=UTF-8</value> 54 </list> 55 </property> 56 </bean> 57 58 59 <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀(如果最后一个还是表示文件夹,则最后的斜杠不要漏了) 使用JSP --> 60 <!-- 默认的视图解析器在上边的解析错误时使用 (默认使用html)- --> 61 62 <bean id="viewResolver" 63 class="org.springframework.web.servlet.view.InternalResourceViewResolver" 64 p:prefix="/WEB-INF/" p:suffix=".jsp" /> 65 66 67 <!--由于web.xml中设置是由springMVC拦截所有请求,于是在读取静态资源文件的时候就会受到影响(说白了就是读不到) 经过下面的配置,该标签的作用就是所有页面中引用/css/**的资源,都会从/resources/styles/ 68 里面查找 --> 69 <mvc:resources mapping="/logs/**" location="/logs/" /> 70 <mvc:resources mapping="/file/**" location="/file/" /> 71 <mvc:resources mapping="/assets/**" location="/WEB-INF/assets/" /> 72 <mvc:resources mapping="/FileUpload/**" location="/WEB-INF/FileUpload/" /> 73 74 <!--由于web.xml中设置是由springMVC拦截所有请求,于是在读取静态资源文件的时候就会受到影响(说白了就是读不到) 经过下面的配置,该标签的作用就是所有页面中引用/css/**的资源,都会从/resources/styles/ 75 里面查找 --> 76 <!-- <mvc:resources mapping="/logs/**" location="/logs/" /> <mvc:resources 77 mapping="/file/**" location="/file/" /> <mvc:resources mapping="/assets/**" 78 location="/WEB-INF/assets/"/> --> 79 <!-- 拦截器 --> 80 81 <!-- 多个拦截器,顺序执行 --> 82 <!-- 如果不配置或/*,将拦截所有的Controller --> 83 <mvc:interceptors> 84 <mvc:interceptor> 85 <mvc:mapping path="/*" /> 86 <mvc:mapping path="/*/*" /> 87 <mvc:exclude-mapping path="/user/login" /> 88 <mvc:exclude-mapping path="/user/loginToIndex" /> 89 <mvc:exclude-mapping path="/init/toinitPage" /> 90 <mvc:exclude-mapping path="/init/toInit" /> 91 <bean class="com.java.interceptor.LoginInterceptor"> 92 </bean> 93 </mvc:interceptor> 94 </mvc:interceptors> 95 96 <!-- 文件上传 --> 97 <bean id="multipartResolver" 98 class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 99 <!-- set the max upload size 100M --> 100 <property name="maxUploadSize"> 101 <value>104857600</value> 102 </property> 103 <property name="maxInMemorySize"> 104 <value>1024000</value> 105 </property> 106 </bean> 107 108 109 110 111 </beans>
第四步、配置dispatch-shiro.xml配置文件,用来配置shiro的安全管理,缓存管理,加密,以及资源授权等
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:p="http://www.springframework.org/schema/p" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 9 http://www.springframework.org/schema/aop 10 http://www.springframework.org/schema/aop/spring-aop-4.1.xsd 11 http://www.springframework.org/schema/context 12 http://www.springframework.org/schema/context/spring-context-4.1.xsd 13 http://www.springframework.org/schema/tx 14 http://www.springframework.org/schema/tx/spring-tx-4.1.xsd 15 http://www.springframework.org/schema/mvc 16 http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd"> 17 <bean 18 class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 19 depends-on="lifecycleBeanPostProcessor"> 20 <property name="proxyTargetClass" value="true" /> 21 </bean> 22 <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> 23 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> 24 25 <!-- shiro-core 核心包中 MDB加密算法,需要对密码进行加密处理 --> 26 <!-- <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.Md5CredentialsMatcher"></bean> --> 27 <!-- 缓存管理 --> 28 <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean> 29 30 <bean id="mongodbRealm" class="com.java.shiro.MongoDBRealm"> 31 <!-- 设置加密模式 --> 32 <!-- <property name="credentialsMatcher" ref="credentialsMatcher"></property> --> 33 </bean> 34 35 <!-- shiro安全管理器 --> 36 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 37 <property name="realm" ref="mongodbRealm"></property> 38 <property name="cacheManager" ref="cacheManager"></property> 39 </bean> 40 41 <bean 42 class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> 43 <property name="securityManager" ref="securityManager" /> 44 </bean> 45 <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 --> 46 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 47 <property name="securityManager" ref="securityManager" /> 48 <property name="loginUrl" value="/user/login" /> 49 <property name="successUrl" value="/user/index" /> 50 <property name="unauthorizedUrl" value="/fail/nopromission" /> 51 <!-- 自定义权限配置 --> 52 <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" /> 53 </bean> 54 55 56 <!-- 自定义filterChainDefinitionMap --> 57 <bean id="chainDefinitionSectionMetaSource" class="com.java.shiro.ChainDefinitionSectionMetaSource"> 58 <property name="filterChainDefinitions"> 59 <value> 60 /user/loginToIndex = anon 61 /assets/** = anon 62 /fail/nopromission = anon 63 /fail/nopage = anon 64 /fail/error = anon 65 /public/** = anon 66 /FileUpload/** = anon 67 /init/toinitPage = anon 68 /init/toInit = anon 69 </value> 70 </property> 71 </bean> 72 73 74 <!-- <bean id="shiroFilter" class="com.java.shiro.ShiroPermissionFactory"> 75 调用我们配置的权限管理器 76 <property name="securityManager" ref="securityManager" /> 77 配置我们的登录请求地址 78 <property name="loginUrl" value="/admin/tologin" /> 79 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 80 <property name="successUrl" value="/admin/loginToIndex" /> 81 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 82 <property name="unauthorizedUrl" value="/fail/nopromission" /> 83 权限配置 84 <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource"></property> 85 <property name="filterChainDefinitions"> 86 <value> 87 /admin/tologin = anon 88 /assets/** =anon 89 /fail/nopromission =anon 90 </value> 91 </property> 92 </bean> 93 --> 94 95 </beans>
第五步、自定义filterChainDefinitionMap ,创建ChainDefinitionSectionMetaSource
1 package com.java.shiro; 2 3 import java.util.List; 4 import java.util.Map; 5 6 import javax.servlet.ServletContext; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.swing.plaf.synth.SynthColorChooserUI; 9 10 import org.apache.commons.lang3.StringUtils; 11 import org.apache.shiro.config.Ini; 12 import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 13 import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager; 14 import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver; 15 import org.apache.shiro.web.servlet.AbstractShiroFilter; 16 import org.jfree.util.Log; 17 import org.springframework.beans.factory.FactoryBean; 18 import org.springframework.beans.factory.annotation.Autowired; 19 import org.springframework.context.ApplicationContext; 20 import org.springframework.context.support.ClassPathXmlApplicationContext; 21 import org.springframework.data.mongodb.core.query.Query; 22 import org.springframework.stereotype.Repository; 23 import org.springframework.web.context.request.RequestContextHolder; 24 import org.springframework.web.context.request.ServletRequestAttributes; 25 import org.springframework.web.context.support.WebApplicationContextUtils; 26 27 import com.java.manage.dao.ResourceDao; 28 import com.java.manage.pojo.Resource; 29 30 public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section> { 31 32 private String filterChainDefinitions; 33 34 35 public Ini.Section getObject() throws Exception { 36 ApplicationContext ac = new ClassPathXmlApplicationContext(new String[] { "application-config.xml" }); 37 ResourceDao resourceDao = (ResourceDao) ac.getBean("resourceDaoImpl"); 38 // 获取所有Resource 39 Ini ini = new Ini(); 40 // 加载默认的url 41 ini.load(filterChainDefinitions); 42 Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME); 43 List<Resource> list = resourceDao.find(new Query()); 44 // 循环Resource的url,逐个添加到section中。section就是filterChainDefinitionMap, 45 // 里面的键就是链接URL,值就是存在什么条件才能访问该链接 46 for (Resource resource : list) { 47 // 构成permission字符串 48 if (StringUtils.isNotEmpty(resource.getResUrl() + "") 49 && StringUtils.isNotEmpty(resource.getResKey() + "")) { 50 String permission = "perms[" + resource.getResKey() + "]"; 51 Log.info("所有权限信息" + permission); 52 // 不对角色进行权限验证 53 // 如需要则 permission = "roles[" + resources.getResKey() + "]"; 54 // map.put(resource.getResUrl() + "", permission); 55 section.put(resource.getResUrl() + "", permission); 56 } 57 } 58 /* 59 * for (Map.Entry<String, String> entry :map.entrySet()) { 60 * 61 * System.out.println("Key = " + entry.getKey() + ", Value = " + 62 * entry.getValue()); 63 * 64 * } 65 */ 66 67 // 所有资源的访问权限,必须放在最后 68 /* section.put("/**", "authc"); */ 69 /** 70 * 如果需要一个用户只能登录一处地方,,修改为 section.put("/**", 71 * "authc,kickout,sysUser,user"); 72 **/ 73 section.put("/**", "authc"); 74 return section; 75 } 76 77 78 79 /** 80 * 通过filterChainDefinitions对默认的url过滤定义 81 * 82 * @param filterChainDefinitions 83 * 默认的url过滤定义 84 */ 85 public void setFilterChainDefinitions(String filterChainDefinitions) { 86 this.filterChainDefinitions = filterChainDefinitions; 87 } 88 89 public Class<?> getObjectType() { 90 return this.getClass(); 91 } 92 93 public boolean isSingleton() { 94 return false; 95 } 96 }
第六步、自定义MongoDBRealm
1 package com.java.shiro; 2 3 import java.util.List; 4 5 import org.apache.shiro.SecurityUtils; 6 import org.apache.shiro.authc.AuthenticationException; 7 import org.apache.shiro.authc.AuthenticationInfo; 8 import org.apache.shiro.authc.AuthenticationToken; 9 import org.apache.shiro.authc.SimpleAuthenticationInfo; 10 import org.apache.shiro.authc.UsernamePasswordToken; 11 import org.apache.shiro.authz.AuthorizationInfo; 12 import org.apache.shiro.authz.SimpleAuthorizationInfo; 13 import org.apache.shiro.realm.AuthorizingRealm; 14 import org.apache.shiro.session.Session; 15 import org.apache.shiro.subject.PrincipalCollection; 16 import org.slf4j.Logger; 17 import org.slf4j.LoggerFactory; 18 import org.springframework.beans.factory.annotation.Autowired; 19 20 import com.java.manage.pojo.Resource; 21 import com.java.manage.pojo.User; 22 import com.java.manage.service.ResourceService; 23 import com.java.manage.service.UserService; 24 import com.java.util.Common; 25 import com.java.util.Constant; 26 27 public class MongoDBRealm extends AuthorizingRealm { 28 private static final Logger log = LoggerFactory.getLogger(MongoDBRealm.class); 29 @Autowired 30 private UserService userServiceImpl; 31 @Autowired 32 private ResourceService resourceService; 33 34 /* 35 * @Autowired private ResUserService resUserService; 36 */ 37 38 @Override 39 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 40 // 获取登录的用户名 41 String accountName = SecurityUtils.getSubject().getPrincipal().toString(); 42 // 获取shirosession 43 log.info("当前登录用户:" + accountName); 44 if (accountName != null || accountName != "") { 45 Session session = SecurityUtils.getSubject().getSession(); 46 try { 47 User user = (User) session.getAttribute(Constant.USER_SESSION); 48 if (user != null) { 49 50 // 通过用户名获取用户对象 51 User u = this.userServiceImpl.findUserById(user.getId()); 52 List<Resource> rs = u.getResource(); 53 // 权限信息对象info,用来存放查出的用户所有角色(role)及权限(permission) 54 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 55 for (Resource r : rs) { 56 log.info("资源:" + r.getName() + ":" + r.getResUrl()); 57 info.addStringPermission(r.getResKey()); 58 } 59 session.setAttribute("resourceslist", rs); 60 log.info("当前登录用户访问资源权限:" + info); 61 return info; 62 } 63 } catch (Exception e) { 64 // TODO Auto-generated catch block 65 e.printStackTrace(); 66 } 67 } 68 return null; 69 } 70 71 @Override 72 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 73 // 获取用户的名称 74 UsernamePasswordToken usertoken = (UsernamePasswordToken) token; 75 try { 76 77 // 根据获取到的用户信息从数据库查询是否存在该用户名下的信息 78 User user = this.userServiceImpl.findUserByAccountName(usertoken.getUsername()); 79 80 if (user != null) { 81 // 当验证都通过后,把用户信息放在session里 82 Session session = SecurityUtils.getSubject().getSession(); 83 // List<ResUser> ru= 84 // this.resUserService.findResourcesIdByUserId(user.getId()); 85 User u = this.userServiceImpl.findUserById(user.getId()); 86 87 // 通过集合获取资源 88 // List<Resource> rs=this.resourceService.findlistResource(ru); 89 List<Resource> rs = u.getResource(); 90 session.setAttribute(Constant.USER_SESSION, user); 91 session.setAttribute("userSessionId", user.getId()); 92 session.setAttribute("resourceslist", rs); 93 return new SimpleAuthenticationInfo(user.getAccountName(), user.getPassWord(), Constant.REALMNAME); 94 } else { 95 return null; 96 } 97 } catch (Exception e) { 98 // TODO Auto-generated catch block 99 e.printStackTrace(); 100 } 101 102 return null; 103 } 104 105 }
欢迎大家加入java资源免费分享群,群号:814657026
<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=0d0276c9d13e09101c2aa111b6cc2b5e43f1465cbe431c198a02cc729288094f"><img border="0" src="//pub.idqqimg.com/wpa/images/group.png" alt="MQ" title="MQ"></a>