spring security怎么做到细粒度并且动态地配置权限?

wenwen1 发布于 2013/12/09 16:41
阅读 11K+
收藏 2

我目前所做的security的配置如下:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans:beans xmlns="http://www.springframework.org/schema/security" 
             xmlns:beans="http://www.springframework.org/schema/beans" 
             xmlns:context="http://www.springframework.org/schema/context" 
             xmlns:lang="http://www.springframework.org/schema/lang" 
             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.1.xsd      
                                 http://www.springframework.org/schema/security    
                                 http://www.springframework.org/schema/security/spring-security-3.1.xsd        http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context-3.1.xsd">


<http auto-config="true">
<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>   
<intercept-url pattern="/pages/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/css/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/images/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/js/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_ADMIN,ROLE_USER,ROLE_SUPERADMIN,ROLE_ADMINISTRATOR,ROLE_PREEXTEACHING,ROLE_EXCENTERDIRECTOR,ROLE_LABMANAGER,ROLE_EXTEACHDIRECTOR,ROLE_EXPERIMENTALTEACHING,ROLE_SOFTWAREADMIN,ROLE_WUGUANSTAFF,ROLE_SUPERADMIN"/>
<intercept-url access="IS_AUTHENTICATED_REMEMBERED" pattern="/secure/*"/>
        <form-login  login-page="/index.jsp" login-processing-url="/j_spring_security_check" authentication-failure-url="/pages/login.jsp?login_error=true"  default-target-url="/myCurriculum" />
        <logout invalidate-session="true" logout-success-url="/pages/logout-redirect.jsp"/>
<remember-me key="shfclimsRMKey" user-service-ref="userDetailsService"/>
</http>
<!--配置过滤器  -->  
    <beans:bean id="myFilter" class="net.security.MySecurityFilter">   
        <!-- 用户拥有的权限 -->   
        <beans:property name="authenticationManager" ref="authenticationManager" />   
        <!--用户是否拥有所请求资源的权限   -->
        <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />   
        <!--资源与权限对应关系 -->  
        <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />   
    </beans:bean>   
    <authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="plaintext"/>
</authentication-provider>
</authentication-manager>
 <beans:bean id="myAccessDecisionManager" class="net.security.MyAccessDecisionManager"></beans:bean>   
    <beans:bean id="mySecurityMetadataSource" class="net.security.MySecurityMetadataSource">   
        <beans:constructor-arg name="dataSource" ref="springSecurityDataSource"></beans:constructor-arg>
        <!-- <beans:constructor-arg name="AuthorityDAO" ref="AuthorityDAO"></beans:constructor-arg>  -->
<!--         <beans:property name="dataSource" ref="springSecurityDataSource"/> -->
    </beans:bean>   
          <!-- <beans:bean id="ResourcesDAO" class="net.gvsun.dao.ResourcesDAOImpl" scope="prototype"></beans:bean> -->
          <!--     <beans:bean id="AuthorityDAO" class="net.gvsun.dao.AuthorityDAOImpl" scope="prototype"></beans:bean> -->
<beans:bean class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl" id="userDetailsService">
<beans:property name="rolePrefix" value="ROLE_"/>
<beans:property name="dataSource" ref="springSecurityDataSource"/>
<beans:property name="usersByUsernameQuery" value="SELECT username,password,enabled FROM user WHERE username = ?"/>
<beans:property name="authoritiesByUsernameQuery" value="SELECT u.username, a.authorityName FROM user u JOIN user_authority ua on u.username = ua.user_id JOIN authority a on ua.authority_id = a.id WHERE u.username = ?"/>
</beans:bean>
<global-method-security>
</global-method-security>
</beans:beans>

过滤器MySecurityFilter.java如下:

package net.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;


/**
 * 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
 * securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。
 * 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源,装配到HashMap中,
 * 供Spring Security使用,用于权限校验。
 * @author sparta 11/3/29
 *
 */


public class MySecurityFilter 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{
  
  InterceptorStatusToken  token = super.beforeInvocation(fi);
  
  try{
   fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
  }finally{
   super.afterInvocation(token, null);
  }
  
 }
  
 
 @Override
 public SecurityMetadataSource obtainSecurityMetadataSource(){
  return this.securityMetadataSource;
 }
 
 
 public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource){
  this.securityMetadataSource = securityMetadataSource;
 }
 
 
 public void destroy(){
  
 }
 
 public void init( FilterConfig filterconfig ) throws ServletException{
  
 }
 
 
}


myAccessDecisionManager.java如下:package net.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;


/**
 *AccessdecisionManager在Spring security中是很重要的。
 *
 *在验证部分简略提过了,所有的Authentication实现需要保存在一个GrantedAuthority对象数组中。 
 *这就是赋予给主体的权限。 GrantedAuthority对象通过AuthenticationManager
 *保存到 Authentication对象里,然后从AccessDecisionManager读出来,进行授权判断。 
 *
 *Spring Security提供了一些拦截器,来控制对安全对象的访问权限,例如方法调用或web请求。 
 *一个是否允许执行调用的预调用决定,是由AccessDecisionManager实现的。 
 *这个 AccessDecisionManager 被AbstractSecurityInterceptor调用,
 *它用来作最终访问控制的决定。 这个AccessDecisionManager接口包含三个方法: 
 *
 void decide(Authentication authentication, Object secureObject,
    List<ConfigAttributeDefinition> config) throws AccessDeniedException;
 boolean supports(ConfigAttribute attribute);
 boolean supports(Class clazz);
 
  从第一个方法可以看出来,AccessDecisionManager使用方法参数传递所有信息,这好像在认证评估时进行决定。 
  特别是,在真实的安全方法期望调用的时候,传递安全Object启用那些参数。 
  比如,让我们假设安全对象是一个MethodInvocation。 
  很容易为任何Customer参数查询MethodInvocation,
  然后在AccessDecisionManager里实现一些有序的安全逻辑,来确认主体是否允许在那个客户上操作。 
  如果访问被拒绝,实现将抛出一个AccessDeniedException异常。


  这个 supports(ConfigAttribute) 方法在启动的时候被
  AbstractSecurityInterceptor调用,来决定AccessDecisionManager
  是否可以执行传递ConfigAttribute。 
  supports(Class)方法被安全拦截器实现调用,
  包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。
 */
public class MyAccessDecisionManager implements AccessDecisionManager {
 
 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();
   
   //ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。
   for( GrantedAuthority ga: authentication.getAuthorities()){
    
    if(needRole.trim().equals(ga.getAuthority().trim())){


     return;
    }
    
   }
   
  }
  
  throw new AccessDeniedException("没有权限访问");
  
 }
 
 public boolean supports( ConfigAttribute attribute ){
  
  return true;


 }
 
 public boolean supports(Class<?> clazz){
  return true;


 }
 


}

mySecurityMetadataSource。java如下:

package net.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;


import org.springframework.beans.factory.annotation.Autowired;
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.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.jdbc.core.JdbcTemplate;


/**
 * 最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。 此类在初始化时,应该取到所有资源及其对应角色的定义。
 * 
 */
@Service
public class MySecurityMetadataSource implements
  FilterInvocationSecurityMetadataSource {


 @Autowired


 private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
 private JdbcTemplate jdbcTemplate;
 public MySecurityMetadataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
  loadResourceDefine();
 }
 private void loadResourceDefine() {  
  //查找到所有权限
     List<String> query=loadAuthorityByQuery();
  /*
   * 应当是资源为key, 权限为value。 资源通常为url, 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。
   * sparta
   */
  resourceMap = new HashMap<String, Collection<ConfigAttribute>>();


  for (String auth: query) {
/* //获取authority的authorityName
 String auth=authority.getAuthorityName();*/
   ConfigAttribute ca = new SecurityConfig(auth);
   //获取authority的resources
    List<String> resourceses=loadResourcesByQuery(auth);
    System.out.println("resourceses"+resourceses); 
   for (String res : resourceses) {
  //获取res的url
    String url = res; 
    /*
     * 判断资源文件和权限的对应关系,如果已经存在相关的资源url,则要通过该url为key提取出权限集合,将权限增加到权限集合中。
     * sparta
     */
    if (resourceMap.containsKey(url)) {


     Collection<ConfigAttribute> value = resourceMap.get(url);
     value.add(ca);
     resourceMap.put(url, value);
    } else {
     Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
     atts.add(ca);
     resourceMap.put(url, atts);
    }
   }


  }
 }
 @Override
 public Collection<ConfigAttribute> getAllConfigAttributes() {


  return null;
 }


 // 根据URL,找到相关的权限配置。
 @Override
 public Collection<ConfigAttribute> getAttributes(Object object)
   throws IllegalArgumentException {


  // object 是一个URL,被用户请求的url。
  String url = ((FilterInvocation) object).getRequestUrl();
  
        int firstQuestionMarkIndex = url.indexOf("?");


        if (firstQuestionMarkIndex != -1) {
            url = url.substring(0, firstQuestionMarkIndex);
        }
        System.out.println("url"+url); 
  Iterator<String> ite = resourceMap.keySet().iterator();


  while (ite.hasNext()) {
   String resURL = ite.next();
   System.out.println("resURL"+resURL); 
   /*FilterInvocation filterInvocation = (FilterInvocation) object;
   HttpServletRequest request = filterInvocation.getHttpRequest();
   RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL);
   System.out.println("匹配"+requestMatcher.matches(request));*/
   if (resURL.equals(url)) {
  System.out.println("匹配"+resURL.equals(url));
    return resourceMap.get(resURL);
   }
  }


  return null;
 }


 @Override
 public boolean supports(Class<?> arg0) {


  return true;
 }
public  List<String> loadAuthorityByQuery()
 {
String sql="select authorityName from authority";
List<String> strLst =this.jdbcTemplate.queryForList(sql, String.class); 
return strLst;
 }
public List<String> loadResourcesByQuery(String authName){
String sql="SELECT r.url FROM resources r JOIN resources_authority ra on r.id = ra.resources_id JOIN authority a on ra.authority_id = a.id WHERE a.authorityName ='"+authName+"'";
List<String> strLst =this.jdbcTemplate.queryForList(sql, String.class); 
return strLst;
}
}


数据库设计如下:

DROP TABLE IF EXISTS `authority`;
CREATE TABLE `authority` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `authorityName` varchar(255) DEFAULT NULL COMMENT '权限英文名',
  `cname` varchar(255) DEFAULT NULL COMMENT '权限中文名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;


-- ----------------------------
-- Records of authority
-- ----------------------------
INSERT INTO `authority` VALUES ('1', 'USER', '学生');
INSERT INTO `authority` VALUES ('2', 'ADMIN', '教师');
INSERT INTO `authority` VALUES ('3', 'PREEXTEACHING', '实验教学副院长');
INSERT INTO `authority` VALUES ('4', 'EXCENTERDIRECTOR', '实验中心主任');
INSERT INTO `authority` VALUES ('5', 'LABMANAGER', '实验管理员');
INSERT INTO `authority` VALUES ('6', 'EXTEACHDIRECTOR', '实验教学主任');
INSERT INTO `authority` VALUES ('7', 'EXPERIMENTALTEACHING', '实验教务');
INSERT INTO `authority` VALUES ('8', 'ADMINISTRATOR', '系统管理员');
INSERT INTO `authority` VALUES ('9', 'SOFTWAREADMIN', '软件管理员');
INSERT INTO `authority` VALUES ('10', 'WUGUANSTAFF', '物管员');
INSERT INTO `authority` VALUES ('11', 'SUPERADMIN', '超级管理员');


-- ----------------------------
-- Table structure for `resources`
-- ----------------------------
DROP TABLE IF EXISTS `resources`;
CREATE TABLE `resources` (
  `memo` varchar(255) DEFAULT NULL COMMENT '备注',
  `url` varchar(255) DEFAULT NULL COMMENT ' 权限所对应的url地址',
  `priority` int(11) DEFAULT NULL COMMENT '优先权',
  `type` int(11) DEFAULT NULL COMMENT '类型',
  `name` varchar(255) DEFAULT NULL COMMENT '权限所对应的编码,例201代表发表文章',
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


-- ----------------------------
-- Records of resources
-- ----------------------------
INSERT INTO `resources` VALUES (null, '/courseArrangeList', null, null, '课程列表', '1');
INSERT INTO `resources` VALUES (null, '/noCourseArrangeList', null, null, '查询课程列表', '2');
INSERT INTO `resources` VALUES (null, '/labList', null, null, '实验室信息', '3');


-- ----------------------------
-- Table structure for `resources_authority`
-- ----------------------------
DROP TABLE IF EXISTS `resources_authority`;
CREATE TABLE `resources_authority` (
  `authority_id` int(11) NOT NULL,
  `resources_id` int(11) NOT NULL,
  PRIMARY KEY (`authority_id`,`resources_id`),
  KEY `FK_auth_resources` (`resources_id`),
  CONSTRAINT `FK_auth_resources` FOREIGN KEY (`resources_id`) REFERENCES `resources` (`id`),
  CONSTRAINT `FK_resources_auth` FOREIGN KEY (`authority_id`) REFERENCES `authority` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- ----------------------------
-- Records of resources_authority
-- ----------------------------
INSERT INTO `resources_authority` VALUES ('1', '1');
INSERT INTO `resources_authority` VALUES ('1', '2');
INSERT INTO `resources_authority` VALUES ('1', '3');


-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) NOT NULL COMMENT '学号/工号',
  `cname` varchar(100) NOT NULL COMMENT '姓名',
  `password` varchar(120) NOT NULL COMMENT '密码',
  `user_sexy` varchar(10) DEFAULT NULL COMMENT '性别',
  `user_status` varchar(10) DEFAULT NULL COMMENT '用户状态',
  `teacher_number` int(11) DEFAULT NULL,
  `academy_number` varchar(40) DEFAULT NULL COMMENT '所属学院',
  `major_number` varchar(40) DEFAULT NULL COMMENT '专业代码',
  `user_role` varchar(40) DEFAULT NULL COMMENT '用户角色',
  `department_number` varchar(40) DEFAULT NULL COMMENT '所属部门',
  `classes_number` varchar(40) DEFAULT NULL COMMENT '所属班级',
  `last_login` datetime DEFAULT NULL COMMENT '最后登录时间',
  `created_at` date DEFAULT NULL COMMENT '创建时间',
  `updated_at` date DEFAULT NULL COMMENT '更新时间',
  `telephone` varchar(20) DEFAULT NULL COMMENT '联系电话',
  `email` varchar(20) DEFAULT NULL COMMENT '邮件',
  `changed_id` int(11) DEFAULT NULL COMMENT '变更的id',
  `enabled` bit(1) DEFAULT b'1' COMMENT '是否可用',
  `position` varchar(100) DEFAULT NULL COMMENT '职称',
  PRIMARY KEY (`id`),
  KEY `FK_user_depart` (`department_number`),
  KEY `FK_user_academy` (`academy_number`),
  KEY `FK_user_classes` (`classes_number`),
  KEY `FK_user_major` (`major_number`),
  KEY `FK_user_changed` (`changed_id`),
  KEY `FK_user_school` (`username`),
  CONSTRAINT `FK_user_academy` FOREIGN KEY (`academy_number`) REFERENCES `school_academy` (`academy_number`),
  CONSTRAINT `FK_user_classes` FOREIGN KEY (`classes_number`) REFERENCES `school_classes` (`class_number`),
  CONSTRAINT `FK_user_department` FOREIGN KEY (`department_number`) REFERENCES `school_department` (`department_number`),
  CONSTRAINT `FK_user_major` FOREIGN KEY (`major_number`) REFERENCES `school_major` (`major_number`)
) ENGINE=InnoDB AUTO_INCREMENT=50356 DEFAULT CHARSET=utf8;


-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '101010100', 'admin', 'admin123', null, null, null, '100', '110130', '', null, '11102', '2013-10-31 00:00:00', '2013-11-25', '2013-11-25', '2134325432', '3245@43.com', null, '', null);
INSERT INTO `user` VALUES ('2', '202020200', 'duhuijiang', 'duhuijiang', null, null, null, null, '110110', null, null, '11100', null, null, null, '67845678945', '9', null, '', null);
INSERT INTO `user` VALUES ('3', '0610731228', '0610731228', '0610731228', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('4', '0510411407', '0510411407', '0510411407', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('5', '0610731233', '0610731233', '0610731233', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('6', '0510121108', '0510121108', '0510121108', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('7', '0510121230', '0510121230', '0510121230', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('8', '0510121323', '0510121323', '0510121323', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('9', '0510121503', '0510121503', '0510121503', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('10', '0510215112', '0510215112', '0510215112', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);
INSERT INTO `user` VALUES ('11', '0510215135', '0510215135', '0510215135', null, null, null, null, null, null, null, null, null, null, null, null, null, null, '', null);

-- ----------------------------
-- Table structure for `user_authority`
-- ----------------------------
DROP TABLE IF EXISTS `user_authority`;
CREATE TABLE `user_authority` (
  `user_id` varchar(40) NOT NULL COMMENT '用户名',
  `authority_id` int(11) NOT NULL COMMENT '权限',
  PRIMARY KEY (`user_id`,`authority_id`),
  KEY `FK_user_authority` (`authority_id`),
  CONSTRAINT `FK_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`username`),
  CONSTRAINT `FK_user_authority` FOREIGN KEY (`authority_id`) REFERENCES `authority` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- ----------------------------
-- Records of user_authority
-- ----------------------------
INSERT INTO `user_authority` VALUES ('0510411407', '1');
INSERT INTO `user_authority` VALUES ('0610731228', '2');
INSERT INTO `user_authority` VALUES ('101010100', '2');
INSERT INTO `user_authority` VALUES ('202020200', '3');
INSERT INTO `user_authority` VALUES ('0510121108', '4');
INSERT INTO `user_authority` VALUES ('0510121230', '5');
INSERT INTO `user_authority` VALUES ('0510121323', '6');
INSERT INTO `user_authority` VALUES ('0510121503', '7');
INSERT INTO `user_authority` VALUES ('0510215112', '8');
INSERT INTO `user_authority` VALUES ('0510215135', '9');
INSERT INTO `user_authority` VALUES ('0510411330', '10');


现在所能做到的是101010100这个用户登录进去之后需要点击实验室信息这个url时会提示

我所需要的是当101010100登录之后实验室信息这个链接不显示该怎么做啊?


加载中
0
IT小香猪
IT小香猪

能否把你这个全部发给我呢?yuanxy008@gmail.com

0
xmut
xmut

你们老大没教你排版很重要吗?


0
了不起的盖茨比A
了不起的盖茨比A
能否把你这个全部发给我呢?137694377@qq.com
0
ddatsh
ddatsh
shiro 不解释
返回顶部
顶部