CAS5.0.X 使用经历

Posted yiyi_2

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAS5.0.X 使用经历相关的知识,希望对你有一定的参考价值。

公司要做统一SSO,以前都是自己做后来有同事推荐使用CAS,于是开始一起研究。

CAS网址:https://apereo.github.io/cas/5.0.x/index.html

目前稳定版本是5.0.5

CAS包含众多功能推荐server使用一体化版本的overlay,按官网说法按需导入依赖即可

下载 https://github.com/apereo/cas-overlay-template

修改POM,添加依赖

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.driver.version}</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-webapp</artifactId>
            <version>${cas.version}</version>
            <type>war</type>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jdbc</artifactId>
            <version>${cas.version}</version>
        </dependency>

        <!-- cas rest ticket start-->
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-rest</artifactId>
            <version>${cas.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-rest-services</artifactId>
            <version>${cas.version}</version>
            <scope>runtime</scope>
        </dependency>
        <!-- cas rest ticket end-->
        <!--
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-json-service-registry</artifactId>
            <version>${cas.version}</version>
        </dependency>
        -->
        
        <dependency>
             <groupId>org.apereo.cas</groupId>
             <artifactId>cas-server-support-ldap</artifactId>
             <version>${cas.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-oauth-webflow</artifactId>
            <version>${cas.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jpa-ticket-registry</artifactId>
            <version>${cas.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jpa-service-registry</artifactId>
            <version>${cas.version}</version>
        </dependency>

    </dependencies>

    <properties>
        <cas.version>5.0.5</cas.version>
        <springboot.version>1.4.2.RELEASE</springboot.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mysql.driver.version>6.0.6</mysql.driver.version>
    </properties>

 

打包cas.war,放到linux环境,我用的是centos7(安装jdk什么的不赘述了)

添加application.properties, 根据pom可知我需要db和ldap验证, ticket和service使用db保存

##
# CAS Server Context Configuration
#
server.context-path=/cas
server.port=8443

#####################https configuration############################
server.ssl.enabled=true
server.ssl.key-store=file:///home/admin/keystore/casssl.keystore
server.ssl.key-store-password=123456
server.ssl.key-password=123456
server.max-http-header-size=2097152
server.max-http-post-size=2097152
server.use-forward-headers=true

###very important#####
cas.serviceRegistry.config.location=file:///home/admin/cas/services

# server.ssl.ciphers=
# server.ssl.client-auth=
# server.ssl.enabled=
# server.ssl.key-alias=
# server.ssl.key-store-provider=
# server.ssl.key-store-type=
# server.ssl.protocol=
# server.ssl.trust-store=
# server.ssl.trust-store-password=
# server.ssl.trust-store-provider=
# server.ssl.trust-store-type=
        
server.tomcat.basedir=build/tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)
server.tomcat.accesslog.suffix=.log
server.tomcat.max-threads=5
server.tomcat.port-header=X-Forwarded-Port
server.tomcat.protocol-header=X-Forwarded-Proto
server.tomcat.protocol-header-https-value=https
server.tomcat.remote-ip-header=X-FORWARDED-FOR
server.tomcat.uri-encoding=UTF-8
server.error.include-stacktrace=ALWAYS
        
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true

##
# CAS Cloud Bus Configuration
#
spring.cloud.bus.enabled=false
# spring.cloud.bus.refresh.enabled=true
# spring.cloud.bus.env.enabled=true
# spring.cloud.bus.destination=CasCloudBus
# spring.cloud.bus.ack.enabled=true

endpoints.enabled=true
endpoints.sensitive=true
management.context-path=/status
endpoints.restart.enabled=false
endpoints.shutdown.enabled=false


##
# CAS Web Application Session Configuration
#
server.session.timeout=300
server.session.cookie.http-only=true
server.session.tracking-modes=COOKIE

##
# CAS Thymeleaf View Configuration
#
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML
##
# CAS Log4j Configuration
#
logging.config=file:///home/admin/cas/log4j2.xml
server.context-parameters.isLog4jAutoInitializationDisabled=true

##
# CAS AspectJ Configuration
#
spring.aop.auto=true
spring.aop.proxy-target-class=true

##
# CAS Authentication Credentials
#
#cas.authn.accept.users=casuser::123456

#############jdbc authentication##################
cas.authn.jdbc.query[0].sql=SELECT password FROM sys_user WHERE login_name=?
cas.authn.jdbc.query[0].healthQuery=SELECT 1
cas.authn.jdbc.query[0].isolateInternalQueries=false
cas.authn.jdbc.query[0].url=jdbc:mysql://172.16.37.64:3306/cas-local?useUnicode=true&useSSL=false&characterEncoding=utf-8&serverTimezone=GMT%2B8
cas.authn.jdbc.query[0].failFast=true
cas.authn.jdbc.query[0].isolationLevelName=ISOLATION_READ_COMMITTED
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
cas.authn.jdbc.query[0].leakThreshold=10
cas.authn.jdbc.query[0].propagationBehaviorName=PROPAGATION_REQUIRED
cas.authn.jdbc.query[0].batchSize=1
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].ddlAuto=update
cas.authn.jdbc.query[0].maxAgeDays=180
cas.authn.jdbc.query[0].password=123456
cas.authn.jdbc.query[0].autocommit=false
cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver
cas.authn.jdbc.query[0].idleTimeout=5000
cas.authn.jdbc.query[0].credentialCriteria=

cas.authn.jdbc.query[0].pool.minSize=10
cas.authn.jdbc.query[0].pool.maxSize=100

cas.authn.jdbc.query[0].passwordEncoder.type=NONE

##################jdbc attribute###########################
cas.authn.attributeRepository.jdbc.singleRow=false
cas.authn.attributeRepository.jdbc.requireAllAttributes=true
cas.authn.attributeRepository.jdbc.caseCanonicalization=NONE
cas.authn.attributeRepository.jdbc.queryType=OR
cas.authn.attributeRepository.jdbc.sql=SELECT * FROM user_info WHERE uid = ?
cas.authn.attributeRepository.jdbc.username=root
cas.authn.attributeRepository.jdbc.healthQuery=SELECT 1
cas.authn.attributeRepository.jdbc.url=jdbc:mysql://172.16.37.64:3306/cas-local?useUnicode=true&useSSL=false&characterEncoding=utf-8&serverTimezone=GMT%2B8
cas.authn.attributeRepository.jdbc.dialect=org.hibernate.dialect.MySQL5Dialect
cas.authn.attributeRepository.jdbc.driverClass=com.mysql.cj.jdbc.Driver
cas.authn.attributeRepository.jdbc.user=root
cas.authn.attributeRepository.jdbc.password=123456
cas.authn.attributeRepository.jdbc.ddlAuto=validate

cas.authn.attributeRepository.expireInMinutes=30
cas.authn.attributeRepository.maximumCacheSize=10000
cas.authn.attributeRepository.merger=REPLACE
cas.authn.attributeRepository.jdbc.columnMappings.key=key
cas.authn.attributeRepository.jdbc.columnMappings.value=value
cas.authn.attributeRepository.attributes.cname=cname
cas.authn.attributeRepository.attributes.mail=mail
cas.authn.attributeRepository.defaultAttributesToRelease=cname,mail

###################ldap authentication######################
cas.authn.ldap[0].type=AD
cas.authn.ldap[0].ldapUrl=配置ldapurl
cas.authn.ldap[0].useSsl=false
cas.authn.ldap[0].connectTimeout=5000
cas.authn.ldap[0].baseDn=配置dn
cas.authn.ldap[0].userFilter=sAMAccountName={user}
cas.authn.ldap[0].subtreeSearch=true
cas.authn.ldap[0].bindDn=配置dn
cas.authn.ldap[0].bindCredential=用户名


cas.authn.ldap[0].enhanceWithEntryResolver=true
cas.authn.ldap[0].dnFormat=配置format
cas.authn.ldap[0].principalAttributeId=sAMAccountName
cas.authn.ldap[0].allowMultiplePrincipalAttributeValues=true
cas.authn.ldap[0].additionalAttributes=
cas.authn.ldap[0].principalAttributeList=displayName,givenName,mail,sn,cn,commonName,department,title,description,telephoneNumber,physicalDeliveryOfficeName,memberOf


cas.authn.ldap[0].minPoolSize=3
cas.authn.ldap[0].maxPoolSize=50
cas.authn.ldap[0].validateOnCheckout=true
cas.authn.ldap[0].validatePeriodically=true
cas.authn.ldap[0].validatePeriod=600

cas.authn.ldap[0].failFast=true
cas.authn.ldap[0].idleTime=5000
cas.authn.ldap[0].prunePeriod=5000
cas.authn.ldap[0].blockWaitTime=5000

#############ticket registry##################
cas.ticket.registry.jpa.jpaLockingTimeout=3600
cas.ticket.registry.jpa.healthQuery=SELECT 1
cas.ticket.registry.jpa.isolateInternalQueries=false
cas.ticket.registry.jpa.url=jdbc:mysql://172.16.37.64:3306/cas-local?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
cas.ticket.registry.jpa.failFast=true
cas.ticket.registry.jpa.dialect=org.hibernate.dialect.MySQL5Dialect
# cas.ticket.registry.jpa.leakThreshold=10
cas.ticket.registry.jpa.jpaLockingTgtEnabled=false
cas.ticket.registry.jpa.batchSize=1
# cas.ticket.registry.jpa.defaultCatalog=
cas.ticket.registry.jpa.defaultSchema=cas-local
cas.ticket.registry.jpa.user=root

cas.ticket.registry.jpa.ddlAuto=validate
cas.ticket.registry.jpa.password=123456
cas.ticket.registry.jpa.autocommit=true
cas.ticket.registry.jpa.driverClass=com.mysql.cj.jdbc.Driver

######service registry##################
cas.serviceRegistry.jpa.healthQuery=SELECT 1
cas.serviceRegistry.jpa.isolateInternalQueries=false
cas.serviceRegistry.jpa.url=jdbc:mysql://172.16.37.64:3306/cas-local2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
cas.serviceRegistry.jpa.failFast=true
cas.serviceRegistry.jpa.dialect=org.hibernate.dialect.MySQL5Dialect
# cas.serviceRegistry.jpa.leakThreshold=10
# cas.serviceRegistry.jpa.batchSize=1
# cas.serviceRegistry.jpa.defaultCatalog=
# cas.serviceRegistry.jpa.defaultSchema=
cas.serviceRegistry.jpa.user=root
cas.serviceRegistry.jpa.ddlAuto=validate
cas.serviceRegistry.jpa.password=123456
cas.serviceRegistry.jpa.autocommit=true
cas.serviceRegistry.jpa.driverClass=com.mysql.cj.jdbc.Driver

 

配一个启动的shell

ID=`ps -ef|grep cas.war|grep -v "grep"|awk ‘{print $2}‘`
echo cas PROCESS ID $ID
kill -9 $ID
rm -rf *.log
rm -rf logs/*.log
java -jar cas.war

ok cas server 搞定

ticket保存到db需要4张表,ddlauto为create时会自动创建,不过建议是validate

CREATE TABLE `locks` (
  `application_id` varchar(255) NOT NULL,
  `expiration_date` datetime DEFAULT NULL,
  `unique_id` varchar(255) DEFAULT NULL,
  `lockVer` int(11) NOT NULL DEFAULT 0,
  PRIMARY KEY (`application_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


CREATE TABLE `oauth_tokens` (
  `TYPE` varchar(31) NOT NULL,
  `ID` varchar(255) NOT NULL,
  `NUMBER_OF_TIMES_USED` int(11) DEFAULT NULL,
  `CREATION_TIME` datetime DEFAULT NULL,
  `EXPIRATION_POLICY` longblob NOT NULL,
  `LAST_TIME_USED` datetime DEFAULT NULL,
  `PREVIOUS_LAST_TIME_USED` datetime DEFAULT NULL,
  `AUTHENTICATION` longblob NOT NULL,
  `SERVICE` longblob NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE `serviceticket` (
  `TYPE` varchar(31) NOT NULL,
  `ID` varchar(255) NOT NULL,
  `NUMBER_OF_TIMES_USED` int(11) DEFAULT NULL,
  `CREATION_TIME` datetime DEFAULT NULL,
  `EXPIRATION_POLICY` longblob NOT NULL,
  `LAST_TIME_USED` datetime DEFAULT NULL,
  `PREVIOUS_LAST_TIME_USED` datetime DEFAULT NULL,
  `FROM_NEW_LOGIN` bit(1) NOT NULL,
  `TICKET_ALREADY_GRANTED` bit(1) NOT NULL,
  `SERVICE` longblob NOT NULL,
  `ticketGrantingTicket_ID` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `FK60oigifivx01ts3n8vboyqs38` (`ticketGrantingTicket_ID`),
  CONSTRAINT `FK60oigifivx01ts3n8vboyqs38` FOREIGN KEY (`ticketGrantingTicket_ID`) REFERENCES `ticketgrantingticket` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE `ticketgrantingticket` (
  `TYPE` varchar(31) NOT NULL,
  `ID` varchar(255) NOT NULL,
  `NUMBER_OF_TIMES_USED` int(11) DEFAULT NULL,
  `CREATION_TIME` datetime DEFAULT NULL,
  `EXPIRATION_POLICY` longblob NOT NULL,
  `LAST_TIME_USED` datetime DEFAULT NULL,
  `PREVIOUS_LAST_TIME_USED` datetime DEFAULT NULL,
  `AUTHENTICATION` longblob NOT NULL,
  `EXPIRED` bit(1) NOT NULL,
  `PROXIED_BY` longblob,
  `SERVICES_GRANTED_ACCESS_TO` longblob NOT NULL,
  `ticketGrantingTicket_ID` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `FKiqyu3qw2fxf5qaqin02mox8r4` (`ticketGrantingTicket_ID`),
  CONSTRAINT `FKiqyu3qw2fxf5qaqin02mox8r4` FOREIGN KEY (`ticketGrantingTicket_ID`) REFERENCES `ticketgrantingticket` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

 

 

因为service 注册官网推荐使用Management Webapp来配置,所以还要配个这个

下载 https://github.com/Apereo/cas-services-management-overlay

修改pom添加依赖

<dependencies>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-management-webapp</artifactId>
            <version>${cas.version}</version>
            <type>war</type>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jdbc</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jpa-service-registry</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.driver.version}</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <properties>
        <cas.version>5.0.5</cas.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mysql.driver.version>6.0.6</mysql.driver.version>
    </properties>

注意其中mysql版本默认6.0.3有问题,升级成6.0.6

 

打包放到linux,添加application.properties

##
# CAS Thymeleaf Views
#
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML
##
# Embedded CAS Tomcat Container
#
server.context-path=/casmgr
server.port=443
server.ssl.key-store=file:///home/admin/keystore/casssl.keystore
server.ssl.key-store-password=123456
server.ssl.key-password=123456

##
# Log4J Configuration
#
server.context-parameters.isLog4jAutoInitializationDisabled=true
logging.config=file:///home/admin/casmgr/log4j2.xml

##
# CAS
#
cas.server.name=https://n2:8443
cas.server.prefix=${cas.server.name}/cas

cas.mgmt.adminRoles=ROLE_ADMIN
cas.mgmt.userPropertiesFile=file:///home/admin/casmgr/user-details.properties
cas.mgmt.serverName=https://n2


##
# CAS Authentication Attributes
#
cas.authn.attributeRepository.attributes.uid=uid
cas.authn.attributeRepository.attributes.displayName=displayName
cas.authn.attributeRepository.attributes.cn=commonName
cas.authn.attributeRepository.attributes.affiliation=groupMembership
cas.authn.attributeRepository.attributes.lastName=lastName
cas.authn.attributeRepository.attributes.firstName=firstName
cas.authn.attributeRepository.attributes.givenName=givenName
##
# CAS Web Application Config
#
server.session.timeout=1800
server.session.cookie.http-only=true
server.session.tracking-modes=COOKIE

##
# CAS Cloud Bus Configuration
# Please leave spring.cloud.bus.enabled set to false
#
spring.cloud.bus.enabled=false


cas.serviceRegistry.jpa.healthQuery=SELECT 1
cas.serviceRegistry.jpa.isolateInternalQueries=false
cas.serviceRegistry.jpa.url=jdbc:mysql://172.16.37.64:3306/cas-local2?useUnicode=true&useSSL=false&characterEncoding=utf-8&serverTimezone=GMT%2B8
cas.serviceRegistry.jpa.failFast=true
cas.serviceRegistry.jpa.dialect=org.hibernate.dialect.MySQL5Dialect
cas.serviceRegistry.jpa.user=root
cas.serviceRegistry.jpa.ddlAuto=validate
cas.serviceRegistry.jpa.password=123456
cas.serviceRegistry.jpa.autocommit=false
cas.serviceRegistry.jpa.driverClass=com.mysql.cj.jdbc.Driver

注意cas.serviceRegistry.jpa.ddlAuto如果值为create server和management都会创建4张表,但是其中regexregisteredservice表 Management会多创建几个字段,所以使用Management创建的表即可

表sql:

CREATE TABLE `hibernate_sequence` (
  `next_val` bigint(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `regexregisteredservice` (
  `expression_type` varchar(15) NOT NULL DEFAULT ‘ant‘,
  `id` bigint(20) NOT NULL,
  `access_strategy` longblob,
  `attribute_release` longblob,
  `description` varchar(255) NOT NULL,
  `evaluation_order` int(11) NOT NULL,
  `logo` varchar(255) DEFAULT NULL,
  `logout_type` int(11) DEFAULT NULL,
  `logout_url` varchar(255) DEFAULT NULL,
  `mfa_policy` longblob,
  `name` varchar(255) NOT NULL,
  `proxy_policy` longblob,
  `public_key` longblob,
  `required_handlers` longblob,
  `serviceId` varchar(255) NOT NULL,
  `theme` varchar(255) DEFAULT NULL,
  `username_attr` longblob,
  `bypassApprovalPrompt` bit(1) DEFAULT NULL,
  `clientId` varchar(255) DEFAULT NULL,
  `clientSecret` varchar(255) DEFAULT NULL,
  `generateRefreshToken` bit(1) DEFAULT NULL,
  `jsonFormat` bit(1) DEFAULT NULL,
  `jwks` varchar(255) DEFAULT NULL,
  `signIdToken` bit(1) DEFAULT NULL,
  `encryptAssertions` bit(1) DEFAULT NULL,
  `metadataCriteriaDirection` varchar(255) DEFAULT NULL,
  `metadataCriteriaPattern` varchar(255) DEFAULT NULL,
  `metadataCriteriaRemoveEmptyEntitiesDescriptors` bit(1) DEFAULT NULL,
  `metadataCriteriaRemoveRolelessEntityDescriptors` bit(1) DEFAULT NULL,
  `metadataCriteriaRoles` varchar(255) DEFAULT NULL,
  `metadataLocation` varchar(255) DEFAULT NULL,
  `metadataMaxValidity` bigint(20) DEFAULT NULL,
  `metadataSignatureLocation` varchar(255) DEFAULT NULL,
  `requiredAuthenticationContextClass` varchar(255) DEFAULT NULL,
  `requiredNameIdFormat` varchar(255) DEFAULT NULL,
  `signAssertions` bit(1) DEFAULT NULL,
  `signResponses` bit(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `regexregisteredserviceproperty` (
  `id` bigint(20) NOT NULL,
  `property_values` longblob,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `registeredserviceimpl_props` (
  `AbstractRegisteredService_id` bigint(20) NOT NULL,
  `properties_id` bigint(20) NOT NULL,
  `properties_KEY` varchar(255) NOT NULL,
  PRIMARY KEY (`AbstractRegisteredService_id`,`properties_KEY`),
  UNIQUE KEY `UK_i2mjaqjwxpvurc6aefjkx5x97` (`properties_id`),
  CONSTRAINT `FK1xan7uamsa94y2451jgksjkj4` FOREIGN KEY (`properties_id`) REFERENCES `regexregisteredserviceproperty` (`id`),
  CONSTRAINT `FK5ghaknoplphay7reury7n3vcm` FOREIGN KEY (`AbstractRegisteredService_id`) REFERENCES `regexregisteredservice` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

添加登录用户配置user-details.properties, 使用默认casuser或者dba登录可以成功获得权限,但是ldap还不行原因不明

casuser=notused,ROLE_ADMIN
testAD=notused,ROLE_ADMIN

 

启动Management后会自动跳到server的登录页面,登录后可以添加service。

如果配置都正确server的console会定时刷新service的配置

[localhost-startStop-1] INFO  org.apereo.cas.services.DefaultServicesManagerImpl - Loaded 4 services from JpaServiceRegistryDaoImpl.

 


 

以上是关于CAS5.0.X 使用经历的主要内容,如果未能解决你的问题,请参考以下文章

缺少 SQL SERVER 2014 代码片段

是否可以仅使用一个实例创建片段

回顾这些年的学习技术经历

从片段中的相机意图返回后屏幕变为白色

微信小程序代码片段

webstorm代码片段的创建