diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..ff4fdd1 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +security.buession.com diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..c8de553 --- /dev/null +++ b/about/index.html @@ -0,0 +1,4 @@ +

About

+

+ ... +

\ No newline at end of file diff --git a/docs/guide.md b/docs/guide.md new file mode 100644 index 0000000..7f6834d --- /dev/null +++ b/docs/guide.md @@ -0,0 +1 @@ +文档完善中 \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..436444a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,32 @@ +# 框架介绍 + + +### Buession Security 框架是什么? +基于 Pac4j、Shiro 二次封装,集成 Spring Security,提供极验、阿里云、腾讯云行为验证码 SDK,封装了数据脱敏和多种密码加密方式工具类库的一款安全框架。 + + +#### 数据加密封装 +1. 封装了 MD5、SHA1、SHA256、SHA512、HmacSHA1、HmacSHA256、HmacSHA512 等散列算法 +2. 封装了 AES、DES 等对称加密算法 +3. 封装了 Base64、原 Discuz 论坛中的加解密算法 +4. 封装了基于以上部分算法的密码生成器,未来将会一一实现 + +未来将会提供更多的常用加密算法,如:RSA 等等... ... + + +#### 行为验证码 +封装了阿里云、极验、腾讯云行为验证码,实现标准的 SDK。 +您只需,更改一下 `CaptchaValidator`、`CaptchaClient` 实现类的初始化,即可快速完成行为验证码的更换(当然您还需要修改前端 WEB 代码) + +未来,我们将会接入更多的行为验证码... ... + + +#### 数据脱敏 +为保证返回给前端的数据的安全性,我们提供了数据脱敏处理的工具类 + + +#### 整合三方安全框架 +1. 对 pac4j、io.buji:buji-pac4j 进行了整合 +2. 整合了 apache shiro,并实现了基于 redis 的 session、cache 的缓存;并提供了对 velocity 的支持,未来会支持更多的模板引擎 +3. 整合了 spring security,以及对 spring security 默认 Configurer 的修改,支持 servlet 和 webflux +4. 提供了 XSS 过滤器 `XssFilter`,支持 servlet 和 webflux \ No newline at end of file diff --git a/docs/installation.html b/docs/installation.html new file mode 100644 index 0000000..2b86284 --- /dev/null +++ b/docs/installation.html @@ -0,0 +1,37 @@ +安装及使用-快速上手

安装及使用

+

Maven 中央仓库搜索

+ +

手动编译

+
git clone https://github.com/buession/buession-security
+cd buession-security/buession-security-parent && mvn clean install
+
+

Maven

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-xxx</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

Gradle

+
compile group: 'com.buession.security', name: 'buession-security-xxx', version: 'x.x.x'
+
+

其中,artifactId 中的 xxx 表示对应的子模块;version 中的 x.x.x 代表版本号,根据需要使用特定版本,建议使用 maven 仓库中已构建好的最新版本Maven Central的包。

+
\ No newline at end of file diff --git a/docs/intro.html b/docs/intro.html new file mode 100644 index 0000000..833be33 --- /dev/null +++ b/docs/intro.html @@ -0,0 +1,40 @@ +框架介绍-快速上手

框架介绍

+

Buession Security 框架是什么?

+

基于 Pac4j、Shiro 二次封装,集成 Spring Security,提供极验、阿里云、腾讯云行为验证码 SDK,封装了数据脱敏和多种密码加密方式工具类库的一款安全框架。

+

数据加密封装

+
    +
  1. 封装了 MD5、SHA1、SHA256、SHA512、HmacSHA1、HmacSHA256、HmacSHA512 等散列算法
  2. +
  3. 封装了 AES、DES 等对称加密算法
  4. +
  5. 封装了 Base64、原 Discuz 论坛中的加解密算法
  6. +
  7. 封装了基于以上部分算法的密码生成器,未来将会一一实现
  8. +
+

未来将会提供更多的常用加密算法,如:RSA 等等... ...

+

行为验证码

+

封装了阿里云、极验、腾讯云行为验证码,实现标准的 SDK。 +您只需,更改一下 CaptchaValidatorCaptchaClient 实现类的初始化,即可快速完成行为验证码的更换(当然您还需要修改前端 WEB 代码)

+

未来,我们将会接入更多的行为验证码... ...

+

数据脱敏

+

为保证返回给前端的数据的安全性,我们提供了数据脱敏处理的工具类

+

整合三方安全框架

+
    +
  1. 对 pac4j、io.buji:buji-pac4j 进行了整合
  2. +
  3. 整合了 apache shiro,并实现了基于 redis 的 session、cache 的缓存;并提供了对 velocity 的支持,未来会支持更多的模板引擎
  4. +
  5. 整合了 spring security,以及对 spring security 默认 Configurer 的修改,支持 servlet 和 webflux
  6. +
  7. 提供了 XSS 过滤器 XssFilter,支持 servlet 和 webflux
  8. +
+
\ No newline at end of file diff --git a/docs/license.html b/docs/license.html new file mode 100644 index 0000000..69c51db --- /dev/null +++ b/docs/license.html @@ -0,0 +1,209 @@ +开源协议-快速上手

开源协议

+
                             Apache License
+                       Version 2.0, January 2004
+                    http://www.apache.org/licenses/
+
+

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

+
    +
  1. +

    Definitions.

    +

    "License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document.

    +

    "Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License.

    +

    "Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity.

    +

    "You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License.

    +

    "Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files.

    +

    "Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types.

    +

    "Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below).

    +

    "Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof.

    +

    "Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution."

    +

    "Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work.

    +
  2. +
  3. +

    Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form.

    +
  4. +
  5. +

    Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed.

    +
  6. +
  7. +

    Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions:

    +

    (a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and

    +

    (b) You must cause any modified files to carry prominent notices +stating that You changed the files; and

    +

    (c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and

    +

    (d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License.

    +

    You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License.

    +
  8. +
  9. +

    Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions.

    +
  10. +
  11. +

    Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file.

    +
  12. +
  13. +

    Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License.

    +
  14. +
  15. +

    Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages.

    +
  16. +
  17. +

    Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability.

    +
  18. +
+

END OF TERMS AND CONDITIONS

+

APPENDIX: How to apply the Apache License to your work.

+
  To apply the Apache License to your work, attach the following
+  boilerplate notice, with the fields enclosed by brackets "[]"
+  replaced with your own identifying information. (Don't include
+  the brackets!)  The text should be enclosed in the appropriate
+  comment syntax for the file format. We also recommend that a
+  file or class name and description of purpose be included on the
+  same "printed page" as the copyright notice for easier
+  identification within third-party archives.
+
+

Copyright [yyyy] [name of copyright owner]

+

Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at

+
   http://www.apache.org/licenses/LICENSE-2.0
+
+

Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.

+
\ No newline at end of file diff --git a/docs/module.html b/docs/module.html new file mode 100644 index 0000000..7672c41 --- /dev/null +++ b/docs/module.html @@ -0,0 +1,48 @@ +模块说明-快速上手

模块说明

+

buession-security-core

+
    +
  • 安全核心类库,安全相关实体类、枚举定义,数据脱敏工具。
  • +
+

buession-security-captcha

+
    +
  • 极验、阿里云、腾讯云行为验证 SDK
  • +
+

buession-security-mcrypt

+
    +
  • 数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。
  • +
+

buession-security-pac4j

+
    +
  • 对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j
  • +
+

buession-security-shiro

+
    +
  • apache shiro 二次封装
  • +
  • 增加 redis 对 session 和 cache 的管理
  • +
+

buession-security-spring

+
    +
  • 集成 spring security 框架
  • +
+

buession-security-web

+
    +
  • web 安全相关的功能封装,支持 servlet 和 reactive
  • +
  • Http 安全构建器,HttpSecurityBuilder
  • +
  • XSS filter
  • +
+
\ No newline at end of file diff --git a/docs/quickstart.html b/docs/quickstart.html new file mode 100644 index 0000000..faa11ff --- /dev/null +++ b/docs/quickstart.html @@ -0,0 +1,31 @@ +快速入门-快速上手

快速入门

+
+

TIP

+
+

官方指南假设您已了解"JAVA"方面的相关知识,并且对安全相关的知识有一定认知。

+
+
+

Buession Security 是一款安全方面的框架,实现了当前流行的行为验证码、加解密、数据脱敏、集成 pac4j 和 shiro、集成 spring security,提供浏览器相关的安全响应头。

+

下一步可做什么?

+

您对 Buession Security 大致了解后,您接下来可以做以下事情:

+
    +
  • 了解兼容性:了解 Buession Security 的兼容性
  • +
  • 安装:安装/引用 Buession Security
  • +
  • 使用:开始使用 Buession Security 功能
  • +
+
\ No newline at end of file diff --git a/docs/requirement.html b/docs/requirement.html new file mode 100644 index 0000000..9544c39 --- /dev/null +++ b/docs/requirement.html @@ -0,0 +1,40 @@ +环境要求-快速上手

环境要求

+

JDK

+

JDK 8+

+

构建工具

+ + + + + + + + + + + + + + + + + +
构建工具版本
Maven3.5+
Gradle6.x+,推荐 6.3 及以上版本
+

Servlet 容器

+

支持 servlet 3.1+,推荐使用 servlet 4.0 及以上版本。

+
\ No newline at end of file diff --git a/docs/version.html b/docs/version.html new file mode 100644 index 0000000..bf504b8 --- /dev/null +++ b/docs/version.html @@ -0,0 +1,26 @@ +版本说明-快速上手

版本说明

+

该项目基于 GNU 版风格定义项目版本,即:主版本号.子版本号.修正版本号。

+

管理策略

+
    +
  • 主版本号,发生变更时,不保证所有的 API 对上一个版本兼容,但保障大部分能兼容;主版本变更,可能涉及类、接口、枚举、方法的删除,或者包名的变更
  • +
  • 子版本号,发生变更时,完全兼容上一个版本,主要会增加一些小的功能或API,底层逻辑的调整调优
  • +
  • 修正版本号,主要用于修复 BUG、优化性能、安全漏洞修复,不会新增、变更、删除已有 API
  • +
+

三方包兼容性说明

+

当引用的三方包,我们保证尽大可能兼容。但对于 springframework、springboot、springcloud、springsecurity、springdata 等 spring 家族组件,以及 servlet 兼容对应的主版本。

+
\ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..0343c16 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ +Buession Security

一款集成和封装的 JAVA 安全框架

基于 Pac4j、Shiro 二次封装,集成 Spring Security,提供极验、阿里云、腾讯云行为验证码 SDK,封装了数据脱敏和多种密码加密方式工具类库的一款安全框架。

当前版本: v3.0.0

bg
优雅

经过精雕细琢,我们带给大家一个精心设计的、标准的、高内聚低耦合的通用类库

灵活

非重复造车轮,我们是整合市面上开源的类库,以标准的接口暴露给上层用户,用户可替换或自行封装同类组件。在此基础上,封装了大量的常用的类库。

易用

开箱即用,API 基本统一,学习成本低

统一

引入相同版本的三方库,类库 API 命名、参数、包路径等统一化、标准化

\ No newline at end of file diff --git a/js/stat.js b/js/stat.js new file mode 100644 index 0000000..ca00c75 --- /dev/null +++ b/js/stat.js @@ -0,0 +1,7 @@ +var _hmt = _hmt || []; +(function() { + var hm = document.createElement("script"); + hm.src = "https://hm.baidu.com/hm.js?d7cef67991538c4b3409c8e809918b92"; + var s = document.getElementsByTagName("script")[0]; + s.parentNode.insertBefore(hm, s); +})(); \ No newline at end of file diff --git a/manual/2.0/captcha/index.html b/manual/2.0/captcha/index.html new file mode 100644 index 0000000..c4694e4 --- /dev/null +++ b/manual/2.0/captcha/index.html @@ -0,0 +1,113 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。

+

buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-captcha</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。

+
    +
  • AliYunRequestData:阿里云验证码请求数据
  • +
  • GeetestV3RequestData:极验第三代行为验证码请求数据
  • +
  • GeetestV4RequestData:极验第四代行为验证码请求数据
  • +
  • TencentRequestData:腾讯云验证码请求数据
  • +
+

阿里云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunCaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new AliYunCaptchaClient("Your accessKeyId", "Your accessKeySecret", "Your appKey", httpClient);
+
+RequestData request = new AliYunRequestData();
+request.setToken("token");
+request.setSig("sig");
+request.setSessionId("session id");
+request.setScene("ecene");
+request.setRemoteIp("User client ip");
+captchaClient.validate(request);
+
+

极验

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.geetest.GeetestCaptchaClient;
+import com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new GeetestCaptchaClient("Your appId", "Your secretKey", "version", httpClient);
+
+RequestData request = new GeetestV4RequestData();
+request.setLotNumber("lot number");
+request.setCaptchaOutput("captcha Output");
+request.setPassToken("pass token");
+request.setGenTime("gen time");
+captchaClient.validate(request);
+
+

腾讯云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.tencent.TencentCaptchaClient;
+import com.buession.security.captcha.tencent.TencentRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new TencentCaptchaClient("Your secretId", "Your secretKey", httpClient);
+
+RequestData request = new TencentRequestData();
+request.setRandstr("rand str");
+request.setTicket("ticket");
+request.setUserIp("User client ip");
+captchaClient.validate(request);
+
+

当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。

+

在您的 controller 中您可以这么用。

+
import com.buession.lang.Status;
+import com.buession.web.mvc.Response;
+import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliyunParameter;
+import com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(path = "/captcha")
+public class CaptchamentController {
+
+    @Autowired
+    private CaptchaClient captchaClient;
+
+    @RequestMapping(path = "/validate", method = RequestMethod.GET)
+    public Status validate(HttpServletRequest request){
+        ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());
+        return captchaValidator.validate(request);
+    }
+
+}
+
+

以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClientcom.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.0/captcha/parameter.html b/manual/2.0/captcha/parameter.html new file mode 100644 index 0000000..22de5e0 --- /dev/null +++ b/manual/2.0/captcha/parameter.html @@ -0,0 +1,161 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。

+

阿里云

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
sessionIdSessionIdsessionId会话 ID
sigSigsig签名串
tokenTokentoken请求唯一标识
sceneScenescene场景标识
+

极验

+

第三代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
challengechallengechallenge流水号
seccodeseccodeseccode核心校验数据
validatevalidatevalidate核心校验数据
userIduser_iduser_iduser_id作为终端用户的唯一标识,确定用户的唯一性
clientTypeclient_typeclient_type客户端类型
+

第四代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
lotNumberlot_numberlot_number验证流水号
captchaOutputcaptcha_outputcaptcha_output验证输出信息
passTokenpass_tokenpass_token验证通过标识
genTimegen_timegen_time验证通过时间戳
+

腾讯云

+ + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
randStrRandstrRandstr客户端验证回调的随机串
ticketTicketTicket票据
+
\ No newline at end of file diff --git a/manual/2.0/core/index.html b/manual/2.0/core/index.html new file mode 100644 index 0000000..3eb70cc --- /dev/null +++ b/manual/2.0/core/index.html @@ -0,0 +1,32 @@ +buession-security-core 参考手册-参考手册

buession-security-core 参考手册

+

该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-core</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

数据脱敏:

+
import com.buession.security.core.Desensitization;
+
+String str = Desensitization.encode("13800138000", 3); // 1380***8000
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.0/index.html b/manual/2.0/index.html new file mode 100644 index 0000000..f77b209 --- /dev/null +++ b/manual/2.0/index.html @@ -0,0 +1,64 @@ +API 参考手册-参考手册

API 参考手册

+

Buession Security API 包含以下目录:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
模块使用帮助手册
buession-security-core使用帮助API 手册
buession-security-captcha使用帮助API 手册
buession-security-mcrypt使用帮助API 手册
buession-security-pac4j使用帮助API 手册
buession-security-shiro使用帮助API 手册
buession-security-spring使用帮助API 手册
buession-security-web使用帮助API 手册
+
\ No newline at end of file diff --git a/manual/2.0/mcrypt/index.html b/manual/2.0/mcrypt/index.html new file mode 100644 index 0000000..7fbf312 --- /dev/null +++ b/manual/2.0/mcrypt/index.html @@ -0,0 +1,53 @@ +buession-security-mcrypt 参考手册-参考手册

buession-security-mcrypt 参考手册

+

数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-mcrypt</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。

+
    +
  • encode:加密,对任意对象进行加密,如果参数为 char[]byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密
  • +
  • decode:解密,对 CharSequence 进行解密
  • +
+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt();
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();
+mcrypt.encode(new Integer(100));
+
+

您可以指定加密 key。

+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt("UTF-8", "key");
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt("UTF-8", "key");
+mcrypt.encode(new Integer(100));
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.0/pac4j/index.html b/manual/2.0/pac4j/index.html new file mode 100644 index 0000000..39cfd40 --- /dev/null +++ b/manual/2.0/pac4j/index.html @@ -0,0 +1,27 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-pac4j</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.0/shiro/index.html b/manual/2.0/shiro/index.html new file mode 100644 index 0000000..5d17765 --- /dev/null +++ b/manual/2.0/shiro/index.html @@ -0,0 +1,27 @@ +buession-security-shiro 参考手册-参考手册

buession-security-shiro 参考手册

+

apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-shiro</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.0/spring/index.html b/manual/2.0/spring/index.html new file mode 100644 index 0000000..06b0233 --- /dev/null +++ b/manual/2.0/spring/index.html @@ -0,0 +1,28 @@ +buession-security-spring 参考手册-参考手册

buession-security-spring 参考手册

+

集成 spring security 框架。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-spring</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

该模块无功能,仅仅整合把 spring security 的依赖整合进来了。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.0/web/index.html b/manual/2.0/web/index.html new file mode 100644 index 0000000..48fc01c --- /dev/null +++ b/manual/2.0/web/index.html @@ -0,0 +1,28 @@ +buession-security-web 参考手册-参考手册

buession-security-web 参考手册

+

web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-web</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

您可以通过 ServletWebSecurityConfigurerAdapterConfigurationReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.1/captcha/index.html b/manual/2.1/captcha/index.html new file mode 100644 index 0000000..e5dcd76 --- /dev/null +++ b/manual/2.1/captcha/index.html @@ -0,0 +1,113 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。

+

buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-captcha</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。

+
    +
  • AliYunRequestData:阿里云验证码请求数据
  • +
  • GeetestV3RequestData:极验第三代行为验证码请求数据
  • +
  • GeetestV4RequestData:极验第四代行为验证码请求数据
  • +
  • TencentRequestData:腾讯云验证码请求数据
  • +
+

阿里云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunCaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new AliYunCaptchaClient("Your accessKeyId", "Your accessKeySecret", "Your appKey", httpClient);
+
+RequestData request = new AliYunRequestData();
+request.setToken("token");
+request.setSig("sig");
+request.setSessionId("session id");
+request.setScene("ecene");
+request.setRemoteIp("User client ip");
+captchaClient.validate(request);
+
+

极验

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.geetest.GeetestCaptchaClient;
+import com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new GeetestCaptchaClient("Your appId", "Your secretKey", "version", httpClient);
+
+RequestData request = new GeetestV4RequestData();
+request.setLotNumber("lot number");
+request.setCaptchaOutput("captcha Output");
+request.setPassToken("pass token");
+request.setGenTime("gen time");
+captchaClient.validate(request);
+
+

腾讯云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.tencent.TencentCaptchaClient;
+import com.buession.security.captcha.tencent.TencentRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new TencentCaptchaClient("Your secretId", "Your secretKey", httpClient);
+
+RequestData request = new TencentRequestData();
+request.setRandstr("rand str");
+request.setTicket("ticket");
+request.setUserIp("User client ip");
+captchaClient.validate(request);
+
+

当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。

+

在您的 controller 中您可以这么用。

+
import com.buession.lang.Status;
+import com.buession.web.mvc.Response;
+import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliyunParameter;
+import com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(path = "/captcha")
+public class CaptchamentController {
+
+    @Autowired
+    private CaptchaClient captchaClient;
+
+    @RequestMapping(path = "/validate", method = RequestMethod.GET)
+    public Status validate(HttpServletRequest request){
+        ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());
+        return captchaValidator.validate(request);
+    }
+
+}
+
+

以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClientcom.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.1/captcha/parameter.html b/manual/2.1/captcha/parameter.html new file mode 100644 index 0000000..087e085 --- /dev/null +++ b/manual/2.1/captcha/parameter.html @@ -0,0 +1,161 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。

+

阿里云

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
sessionIdSessionIdsessionId会话 ID
sigSigsig签名串
tokenTokentoken请求唯一标识
sceneScenescene场景标识
+

极验

+

第三代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
challengechallengechallenge流水号
seccodeseccodeseccode核心校验数据
validatevalidatevalidate核心校验数据
userIduser_iduser_iduser_id作为终端用户的唯一标识,确定用户的唯一性
clientTypeclient_typeclient_type客户端类型
+

第四代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
lotNumberlot_numberlot_number验证流水号
captchaOutputcaptcha_outputcaptcha_output验证输出信息
passTokenpass_tokenpass_token验证通过标识
genTimegen_timegen_time验证通过时间戳
+

腾讯云

+ + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
randStrRandstrRandstr客户端验证回调的随机串
ticketTicketTicket票据
+
\ No newline at end of file diff --git a/manual/2.1/core/index.html b/manual/2.1/core/index.html new file mode 100644 index 0000000..5e66edd --- /dev/null +++ b/manual/2.1/core/index.html @@ -0,0 +1,32 @@ +buession-security-core 参考手册-参考手册

buession-security-core 参考手册

+

该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-core</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

数据脱敏:

+
import com.buession.security.core.Desensitization;
+
+String str = Desensitization.encode("13800138000", 3); // 1380***8000
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.1/index.html b/manual/2.1/index.html new file mode 100644 index 0000000..5404bca --- /dev/null +++ b/manual/2.1/index.html @@ -0,0 +1,64 @@ +API 参考手册-参考手册

API 参考手册

+

Buession Security API 包含以下目录:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
模块使用帮助手册
buession-security-core使用帮助API 手册
buession-security-captcha使用帮助API 手册
buession-security-mcrypt使用帮助API 手册
buession-security-pac4j使用帮助API 手册
buession-security-shiro使用帮助API 手册
buession-security-spring使用帮助API 手册
buession-security-web使用帮助API 手册
+
\ No newline at end of file diff --git a/manual/2.1/mcrypt/index.html b/manual/2.1/mcrypt/index.html new file mode 100644 index 0000000..f624aef --- /dev/null +++ b/manual/2.1/mcrypt/index.html @@ -0,0 +1,53 @@ +buession-security-mcrypt 参考手册-参考手册

buession-security-mcrypt 参考手册

+

数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-mcrypt</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。

+
    +
  • encode:加密,对任意对象进行加密,如果参数为 char[]byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密
  • +
  • decode:解密,对 CharSequence 进行解密
  • +
+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt();
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();
+mcrypt.encode(new Integer(100));
+
+

您可以指定加密 key。

+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt("UTF-8", "key");
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt("UTF-8", "key");
+mcrypt.encode(new Integer(100));
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.1/pac4j/ajaxrequestresolver.html b/manual/2.1/pac4j/ajaxrequestresolver.html new file mode 100644 index 0000000..98cf51b --- /dev/null +++ b/manual/2.1/pac4j/ajaxrequestresolver.html @@ -0,0 +1,29 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

AjaxRequestResolver

+

pac4j 原生的 AJAX 请求解析器 AjaxRequestResolver 的实现 DefaultAjaxRequestResolver,以 XML 的形式响应 pac4j 重定向。

+
<?xml version='1.0' encoding='UTF-8'?>
+<partial-response>
+    <redirect url="redirect_url"></redirect>
+</partial-response>
+
+

此种,场景增加了响应数据的大小和前端 Ajax 解析的成本和难度。为此,我们扩展了 JsonAjaxRequestResolverTextAjaxRequestResolver 以 JSON 和文本的形式响应重定向地址,以简化前端 JavaScript 的解析成本和难度。

+
{"redirect": {"url": "redirect_url"}}
+
+
redirect_url
+
+
\ No newline at end of file diff --git a/manual/2.1/pac4j/annotation.html b/manual/2.1/pac4j/annotation.html new file mode 100644 index 0000000..2708363 --- /dev/null +++ b/manual/2.1/pac4j/annotation.html @@ -0,0 +1,55 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

注解

+

我们通过注解的形式封装了获取当前登录用户信息的 API。

+

注解

+ + + + + + + + + + + + + + + +
注解作用域说明
@Principal方法参数获取当前登录用户信息,并可以以任何实体类、Map 对象的形式返回
+

获取当前登录用户

+
@Controller
+@RequestMapping(path = "/test")
+public class TestController {
+
+    @RequestMapping(path = "/principal1")
+    @ResponseBody
+    public User principal1(@Principal User user, ServerHttpResponse response){
+        return user;
+    }
+
+    @RequestMapping(path = "/principal2")
+    @ResponseBody
+    public Map<String, Object> principal2(@Principal Map<String, Object> user, ServerHttpResponse response){
+        return user;
+    }
+
+}
+
+
\ No newline at end of file diff --git a/manual/2.1/pac4j/index.html b/manual/2.1/pac4j/index.html new file mode 100644 index 0000000..bee48db --- /dev/null +++ b/manual/2.1/pac4j/index.html @@ -0,0 +1,27 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-pac4j</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.1/shiro/index.html b/manual/2.1/shiro/index.html new file mode 100644 index 0000000..b04791e --- /dev/null +++ b/manual/2.1/shiro/index.html @@ -0,0 +1,27 @@ +buession-security-shiro 参考手册-参考手册

buession-security-shiro 参考手册

+

apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-shiro</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.1/spring/index.html b/manual/2.1/spring/index.html new file mode 100644 index 0000000..9f7d9cf --- /dev/null +++ b/manual/2.1/spring/index.html @@ -0,0 +1,28 @@ +buession-security-spring 参考手册-参考手册

buession-security-spring 参考手册

+

集成 spring security 框架。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-spring</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

该模块无功能,仅仅整合把 spring security 的依赖整合进来了。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.1/web/index.html b/manual/2.1/web/index.html new file mode 100644 index 0000000..f9d1954 --- /dev/null +++ b/manual/2.1/web/index.html @@ -0,0 +1,28 @@ +buession-security-web 参考手册-参考手册

buession-security-web 参考手册

+

web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-web</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

您可以通过 ServletWebSecurityConfigurerAdapterConfigurationReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.2/captcha/index.html b/manual/2.2/captcha/index.html new file mode 100644 index 0000000..933c429 --- /dev/null +++ b/manual/2.2/captcha/index.html @@ -0,0 +1,113 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。

+

buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-captcha</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。

+
    +
  • AliYunRequestData:阿里云验证码请求数据
  • +
  • GeetestV3RequestData:极验第三代行为验证码请求数据
  • +
  • GeetestV4RequestData:极验第四代行为验证码请求数据
  • +
  • TencentRequestData:腾讯云验证码请求数据
  • +
+

阿里云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunCaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new AliYunCaptchaClient("Your accessKeyId", "Your accessKeySecret", "Your appKey", httpClient);
+
+RequestData request = new AliYunRequestData();
+request.setToken("token");
+request.setSig("sig");
+request.setSessionId("session id");
+request.setScene("ecene");
+request.setRemoteIp("User client ip");
+captchaClient.validate(request);
+
+

极验

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.geetest.GeetestCaptchaClient;
+import com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new GeetestCaptchaClient("Your appId", "Your secretKey", "version", httpClient);
+
+RequestData request = new GeetestV4RequestData();
+request.setLotNumber("lot number");
+request.setCaptchaOutput("captcha Output");
+request.setPassToken("pass token");
+request.setGenTime("gen time");
+captchaClient.validate(request);
+
+

腾讯云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.tencent.TencentCaptchaClient;
+import com.buession.security.captcha.tencent.TencentRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new TencentCaptchaClient("Your secretId", "Your secretKey", httpClient);
+
+RequestData request = new TencentRequestData();
+request.setRandstr("rand str");
+request.setTicket("ticket");
+request.setUserIp("User client ip");
+captchaClient.validate(request);
+
+

当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。

+

在您的 controller 中您可以这么用。

+
import com.buession.lang.Status;
+import com.buession.web.mvc.Response;
+import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliyunParameter;
+import com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(path = "/captcha")
+public class CaptchamentController {
+
+    @Autowired
+    private CaptchaClient captchaClient;
+
+    @RequestMapping(path = "/validate", method = RequestMethod.GET)
+    public Status validate(HttpServletRequest request){
+        ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());
+        return captchaValidator.validate(request);
+    }
+
+}
+
+

以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClientcom.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.2/captcha/parameter.html b/manual/2.2/captcha/parameter.html new file mode 100644 index 0000000..eb65a20 --- /dev/null +++ b/manual/2.2/captcha/parameter.html @@ -0,0 +1,161 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。

+

阿里云

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
sessionIdSessionIdsessionId会话 ID
sigSigsig签名串
tokenTokentoken请求唯一标识
sceneScenescene场景标识
+

极验

+

第三代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
challengechallengechallenge流水号
seccodeseccodeseccode核心校验数据
validatevalidatevalidate核心校验数据
userIduser_iduser_iduser_id作为终端用户的唯一标识,确定用户的唯一性
clientTypeclient_typeclient_type客户端类型
+

第四代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
lotNumberlot_numberlot_number验证流水号
captchaOutputcaptcha_outputcaptcha_output验证输出信息
passTokenpass_tokenpass_token验证通过标识
genTimegen_timegen_time验证通过时间戳
+

腾讯云

+ + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
randStrRandstrRandstr客户端验证回调的随机串
ticketTicketTicket票据
+
\ No newline at end of file diff --git a/manual/2.2/core/index.html b/manual/2.2/core/index.html new file mode 100644 index 0000000..13aba1b --- /dev/null +++ b/manual/2.2/core/index.html @@ -0,0 +1,32 @@ +buession-security-core 参考手册-参考手册

buession-security-core 参考手册

+

该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-core</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

数据脱敏:

+
import com.buession.security.core.Desensitization;
+
+String str = Desensitization.encode("13800138000", 3); // 1380***8000
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.2/index.html b/manual/2.2/index.html new file mode 100644 index 0000000..4bc0b26 --- /dev/null +++ b/manual/2.2/index.html @@ -0,0 +1,64 @@ +API 参考手册-参考手册

API 参考手册

+

Buession Security API 包含以下目录:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
模块使用帮助手册
buession-security-core使用帮助API 手册
buession-security-captcha使用帮助API 手册
buession-security-mcrypt使用帮助API 手册
buession-security-pac4j使用帮助API 手册
buession-security-shiro使用帮助API 手册
buession-security-spring使用帮助API 手册
buession-security-web使用帮助API 手册
+
\ No newline at end of file diff --git a/manual/2.2/mcrypt/index.html b/manual/2.2/mcrypt/index.html new file mode 100644 index 0000000..94c6091 --- /dev/null +++ b/manual/2.2/mcrypt/index.html @@ -0,0 +1,53 @@ +buession-security-mcrypt 参考手册-参考手册

buession-security-mcrypt 参考手册

+

数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-mcrypt</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。

+
    +
  • encode:加密,对任意对象进行加密,如果参数为 char[]byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密
  • +
  • decode:解密,对 CharSequence 进行解密
  • +
+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt();
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();
+mcrypt.encode(new Integer(100));
+
+

您可以指定加密 key。

+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt("UTF-8", "key");
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt("UTF-8", "key");
+mcrypt.encode(new Integer(100));
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.2/pac4j/ajaxrequestresolver.html b/manual/2.2/pac4j/ajaxrequestresolver.html new file mode 100644 index 0000000..0b2652a --- /dev/null +++ b/manual/2.2/pac4j/ajaxrequestresolver.html @@ -0,0 +1,29 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

AjaxRequestResolver

+

pac4j 原生的 AJAX 请求解析器 AjaxRequestResolver 的实现 DefaultAjaxRequestResolver,以 XML 的形式响应 pac4j 重定向。

+
<?xml version='1.0' encoding='UTF-8'?>
+<partial-response>
+    <redirect url="redirect_url"></redirect>
+</partial-response>
+
+

此种,场景增加了响应数据的大小和前端 Ajax 解析的成本和难度。为此,我们扩展了 JsonAjaxRequestResolverTextAjaxRequestResolver 以 JSON 和文本的形式响应重定向地址,以简化前端 JavaScript 的解析成本和难度。

+
{"redirect": {"url": "redirect_url"}}
+
+
redirect_url
+
+
\ No newline at end of file diff --git a/manual/2.2/pac4j/annotation.html b/manual/2.2/pac4j/annotation.html new file mode 100644 index 0000000..0f91fbf --- /dev/null +++ b/manual/2.2/pac4j/annotation.html @@ -0,0 +1,55 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

注解

+

我们通过注解的形式封装了获取当前登录用户信息的 API。

+

注解

+ + + + + + + + + + + + + + + +
注解作用域说明
@Principal方法参数获取当前登录用户信息,并可以以任何实体类、Map 对象的形式返回
+

获取当前登录用户

+
@Controller
+@RequestMapping(path = "/test")
+public class TestController {
+
+    @RequestMapping(path = "/principal1")
+    @ResponseBody
+    public User principal1(@Principal User user, ServerHttpResponse response){
+        return user;
+    }
+
+    @RequestMapping(path = "/principal2")
+    @ResponseBody
+    public Map<String, Object> principal2(@Principal Map<String, Object> user, ServerHttpResponse response){
+        return user;
+    }
+
+}
+
+
\ No newline at end of file diff --git a/manual/2.2/pac4j/index.html b/manual/2.2/pac4j/index.html new file mode 100644 index 0000000..a2d9f65 --- /dev/null +++ b/manual/2.2/pac4j/index.html @@ -0,0 +1,27 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-pac4j</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.2/shiro/index.html b/manual/2.2/shiro/index.html new file mode 100644 index 0000000..188f665 --- /dev/null +++ b/manual/2.2/shiro/index.html @@ -0,0 +1,27 @@ +buession-security-shiro 参考手册-参考手册

buession-security-shiro 参考手册

+

apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-shiro</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.2/spring/index.html b/manual/2.2/spring/index.html new file mode 100644 index 0000000..a4d22e8 --- /dev/null +++ b/manual/2.2/spring/index.html @@ -0,0 +1,28 @@ +buession-security-spring 参考手册-参考手册

buession-security-spring 参考手册

+

集成 spring security 框架。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-spring</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

该模块无功能,仅仅整合把 spring security 的依赖整合进来了。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.2/web/index.html b/manual/2.2/web/index.html new file mode 100644 index 0000000..2ddd3b6 --- /dev/null +++ b/manual/2.2/web/index.html @@ -0,0 +1,28 @@ +buession-security-web 参考手册-参考手册

buession-security-web 参考手册

+

web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-web</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

您可以通过 ServletWebSecurityConfigurerAdapterConfigurationReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/captcha/index.html b/manual/2.3/captcha/index.html new file mode 100644 index 0000000..f5dc72e --- /dev/null +++ b/manual/2.3/captcha/index.html @@ -0,0 +1,113 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。

+

buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-captcha</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。

+
    +
  • AliYunRequestData:阿里云验证码请求数据
  • +
  • GeetestV3RequestData:极验第三代行为验证码请求数据
  • +
  • GeetestV4RequestData:极验第四代行为验证码请求数据
  • +
  • TencentRequestData:腾讯云验证码请求数据
  • +
+

阿里云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunCaptchaClient;
+import com.buession.security.captcha.aliyun.AliYunRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new AliYunCaptchaClient("Your accessKeyId", "Your accessKeySecret", "Your appKey", httpClient);
+
+RequestData request = new AliYunRequestData();
+request.setToken("token");
+request.setSig("sig");
+request.setSessionId("session id");
+request.setScene("ecene");
+request.setRemoteIp("User client ip");
+captchaClient.validate(request);
+
+

极验

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.geetest.GeetestCaptchaClient;
+import com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new GeetestCaptchaClient("Your appId", "Your secretKey", "version", httpClient);
+
+RequestData request = new GeetestV4RequestData();
+request.setLotNumber("lot number");
+request.setCaptchaOutput("captcha Output");
+request.setPassToken("pass token");
+request.setGenTime("gen time");
+captchaClient.validate(request);
+
+

腾讯云

+
import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.tencent.TencentCaptchaClient;
+import com.buession.security.captcha.tencent.TencentRequestData;
+import com.buession.security.captcha.core.RequestData;
+import com.buession.httpclient.HttpClient;
+
+HttpClient httpClient;
+CaptchaClient captchaClient = new TencentCaptchaClient("Your secretId", "Your secretKey", httpClient);
+
+RequestData request = new TencentRequestData();
+request.setRandstr("rand str");
+request.setTicket("ticket");
+request.setUserIp("User client ip");
+captchaClient.validate(request);
+
+

当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。

+

在您的 controller 中您可以这么用。

+
import com.buession.lang.Status;
+import com.buession.web.mvc.Response;
+import com.buession.security.captcha.CaptchaClient;
+import com.buession.security.captcha.aliyun.AliyunParameter;
+import com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(path = "/captcha")
+public class CaptchamentController {
+
+    @Autowired
+    private CaptchaClient captchaClient;
+
+    @RequestMapping(path = "/validate", method = RequestMethod.GET)
+    public Status validate(HttpServletRequest request){
+        ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());
+        return captchaValidator.validate(request);
+    }
+
+}
+
+

以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClientcom.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/captcha/parameter.html b/manual/2.3/captcha/parameter.html new file mode 100644 index 0000000..6f5781d --- /dev/null +++ b/manual/2.3/captcha/parameter.html @@ -0,0 +1,161 @@ +buession-security-captcha 参考手册-参考手册

buession-security-captcha 参考手册

+

本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。

+

阿里云

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
sessionIdSessionIdsessionId会话 ID
sigSigsig签名串
tokenTokentoken请求唯一标识
sceneScenescene场景标识
+

极验

+

第三代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
challengechallengechallenge流水号
seccodeseccodeseccode核心校验数据
validatevalidatevalidate核心校验数据
userIduser_iduser_iduser_id作为终端用户的唯一标识,确定用户的唯一性
clientTypeclient_typeclient_type客户端类型
+

第四代

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
lotNumberlot_numberlot_number验证流水号
captchaOutputcaptcha_outputcaptcha_output验证输出信息
passTokenpass_tokenpass_token验证通过标识
genTimegen_timegen_time验证通过时间戳
+

腾讯云

+ + + + + + + + + + + + + + + + + + + + + + + +
参数名官方参数名默认值说明
randStrRandstrRandstr客户端验证回调的随机串
ticketTicketTicket票据
+
\ No newline at end of file diff --git a/manual/2.3/core/index.html b/manual/2.3/core/index.html new file mode 100644 index 0000000..1b48883 --- /dev/null +++ b/manual/2.3/core/index.html @@ -0,0 +1,32 @@ +buession-security-core 参考手册-参考手册

buession-security-core 参考手册

+

该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-core</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

数据脱敏:

+
import com.buession.security.core.Desensitization;
+
+String str = Desensitization.encode("13800138000", 3); // 1380***8000
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/crypto/index.html b/manual/2.3/crypto/index.html new file mode 100644 index 0000000..53b864c --- /dev/null +++ b/manual/2.3/crypto/index.html @@ -0,0 +1,28 @@ +buession-security-crypto 参考手册-参考手册

buession-security-crypto 参考手册

+

数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等接口。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-crypto</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-crypto 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/index.html b/manual/2.3/index.html new file mode 100644 index 0000000..742ec3d --- /dev/null +++ b/manual/2.3/index.html @@ -0,0 +1,69 @@ +API 参考手册-参考手册

API 参考手册

+

Buession Security API 包含以下目录:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
模块使用帮助手册
buession-security-core使用帮助API 手册
buession-security-captcha使用帮助API 手册
buession-security-crypto使用帮助API 手册
buession-security-mcrypt使用帮助API 手册
buession-security-pac4j使用帮助API 手册
buession-security-shiro使用帮助API 手册
buession-security-spring使用帮助API 手册
buession-security-web使用帮助API 手册
+
\ No newline at end of file diff --git a/manual/2.3/mcrypt/index.html b/manual/2.3/mcrypt/index.html new file mode 100644 index 0000000..b1930c3 --- /dev/null +++ b/manual/2.3/mcrypt/index.html @@ -0,0 +1,53 @@ +buession-security-mcrypt 参考手册-参考手册

buession-security-mcrypt 参考手册

+

数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-mcrypt</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。

+
    +
  • encode:加密,对任意对象进行加密,如果参数为 char[]byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密
  • +
  • decode:解密,对 CharSequence 进行解密
  • +
+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt();
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();
+mcrypt.encode(new Integer(100));
+
+

您可以指定加密 key。

+
import com.buession.security.mcrypt.Sha1Mcrypt;
+
+Sha1Mcrypt mcrypt = new Sha1Mcrypt("UTF-8", "key");
+mcrypt.encode("Abc");
+
+
import com.buession.security.mcrypt.HmacSha512Mcrypt;
+
+HmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt("UTF-8", "key");
+mcrypt.encode(new Integer(100));
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/pac4j/ajaxrequestresolver.html b/manual/2.3/pac4j/ajaxrequestresolver.html new file mode 100644 index 0000000..addfe99 --- /dev/null +++ b/manual/2.3/pac4j/ajaxrequestresolver.html @@ -0,0 +1,29 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

AjaxRequestResolver

+

pac4j 原生的 AJAX 请求解析器 AjaxRequestResolver 的实现 DefaultAjaxRequestResolver,以 XML 的形式响应 pac4j 重定向。

+
<?xml version='1.0' encoding='UTF-8'?>
+<partial-response>
+    <redirect url="redirect_url"></redirect>
+</partial-response>
+
+

此种,场景增加了响应数据的大小和前端 Ajax 解析的成本和难度。为此,我们扩展了 JsonAjaxRequestResolverTextAjaxRequestResolver 以 JSON 和文本的形式响应重定向地址,以简化前端 JavaScript 的解析成本和难度。

+
{"redirect": {"url": "redirect_url"}}
+
+
redirect_url
+
+
\ No newline at end of file diff --git a/manual/2.3/pac4j/annotation.html b/manual/2.3/pac4j/annotation.html new file mode 100644 index 0000000..6e6eb4d --- /dev/null +++ b/manual/2.3/pac4j/annotation.html @@ -0,0 +1,55 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

注解

+

我们通过注解的形式封装了获取当前登录用户信息的 API。

+

注解

+ + + + + + + + + + + + + + + +
注解作用域说明
@Principal方法参数获取当前登录用户信息,并可以以任何实体类、Map 对象的形式返回
+

获取当前登录用户

+
@Controller
+@RequestMapping(path = "/test")
+public class TestController {
+
+    @RequestMapping(path = "/principal1")
+    @ResponseBody
+    public User principal1(@Principal User user, ServerHttpResponse response){
+        return user;
+    }
+
+    @RequestMapping(path = "/principal2")
+    @ResponseBody
+    public Map<String, Object> principal2(@Principal Map<String, Object> user, ServerHttpResponse response){
+        return user;
+    }
+
+}
+
+
\ No newline at end of file diff --git a/manual/2.3/pac4j/index.html b/manual/2.3/pac4j/index.html new file mode 100644 index 0000000..d01bad1 --- /dev/null +++ b/manual/2.3/pac4j/index.html @@ -0,0 +1,27 @@ +buession-security-pac4j 参考手册-参考手册

buession-security-pac4j 参考手册

+

对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-pac4j</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/shiro/index.html b/manual/2.3/shiro/index.html new file mode 100644 index 0000000..3d56bed --- /dev/null +++ b/manual/2.3/shiro/index.html @@ -0,0 +1,27 @@ +buession-security-shiro 参考手册-参考手册

buession-security-shiro 参考手册

+

apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-shiro</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/spring/index.html b/manual/2.3/spring/index.html new file mode 100644 index 0000000..f35ac27 --- /dev/null +++ b/manual/2.3/spring/index.html @@ -0,0 +1,28 @@ +buession-security-spring 参考手册-参考手册

buession-security-spring 参考手册

+

集成 spring security 框架。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-spring</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

该模块无功能,仅仅整合把 spring security 的依赖整合进来了。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/2.3/web/index.html b/manual/2.3/web/index.html new file mode 100644 index 0000000..f4a5dee --- /dev/null +++ b/manual/2.3/web/index.html @@ -0,0 +1,28 @@ +buession-security-web 参考手册-参考手册

buession-security-web 参考手册

+

web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。

+
+

安装

+
<dependency>
+    <groupId>com.buession.security</groupId>
+    <artifactId>buession-security-web</artifactId>
+    <version>x.x.x</version>
+</dependency>
+
+

您可以通过 ServletWebSecurityConfigurerAdapterConfigurationReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。

+

API 参考手册>>

+
\ No newline at end of file diff --git a/manual/3.0/captcha/index.md b/manual/3.0/captcha/index.md new file mode 100644 index 0000000..f243edd --- /dev/null +++ b/manual/3.0/captcha/index.md @@ -0,0 +1,127 @@ +# buession-security-captcha 参考手册 + + +随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。 + +`buession-security-captcha` 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。 + + +--- + + +### 安装 + +```xml + + com.buession.security + buession-security-captcha + x.x.x + +``` + + +我们通过实现接口 `com.buession.security.captcha.core.RequestData` 定义不同厂商行为验证码需要的请求参数。 + +* AliYunRequestData:阿里云验证码请求数据 +* GeetestV3RequestData:极验第三代行为验证码请求数据 +* GeetestV4RequestData:极验第四代行为验证码请求数据 +* TencentRequestData:腾讯云验证码请求数据 + + +### 阿里云 + +```java +import com.buession.security.captcha.CaptchaClient; +import com.buession.security.captcha.aliyun.AliYunCaptchaClient; +import com.buession.security.captcha.aliyun.AliYunRequestData; +import com.buession.security.captcha.core.RequestData; +import com.buession.httpclient.HttpClient; + +HttpClient httpClient; +CaptchaClient captchaClient = new AliYunCaptchaClient("Your accessKeyId", "Your accessKeySecret", "Your appKey", httpClient); + +RequestData request = new AliYunRequestData(); +request.setToken("token"); +request.setSig("sig"); +request.setSessionId("session id"); +request.setScene("ecene"); +request.setRemoteIp("User client ip"); +captchaClient.validate(request); +``` + + +### 极验 + +```java +import com.buession.security.captcha.CaptchaClient; +import com.buession.security.captcha.geetest.GeetestCaptchaClient; +import com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData; +import com.buession.security.captcha.core.RequestData; +import com.buession.httpclient.HttpClient; + +HttpClient httpClient; +CaptchaClient captchaClient = new GeetestCaptchaClient("Your appId", "Your secretKey", "version", httpClient); + +RequestData request = new GeetestV4RequestData(); +request.setLotNumber("lot number"); +request.setCaptchaOutput("captcha Output"); +request.setPassToken("pass token"); +request.setGenTime("gen time"); +captchaClient.validate(request); +``` + + +### 腾讯云 + +```java +import com.buession.security.captcha.CaptchaClient; +import com.buession.security.captcha.tencent.TencentCaptchaClient; +import com.buession.security.captcha.tencent.TencentRequestData; +import com.buession.security.captcha.core.RequestData; +import com.buession.httpclient.HttpClient; + +HttpClient httpClient; +CaptchaClient captchaClient = new TencentCaptchaClient("Your secretId", "Your secretKey", httpClient); + +RequestData request = new TencentRequestData(); +request.setRandstr("rand str"); +request.setTicket("ticket"); +request.setUserIp("User client ip"); +captchaClient.validate(request); +``` + +当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 `RequestData` 的转换,您可不必这么麻烦的一个一个的去设置参数值。 + +在您的 controller 中您可以这么用。 + + +```java +import com.buession.lang.Status; +import com.buession.web.mvc.Response; +import com.buession.security.captcha.CaptchaClient; +import com.buession.security.captcha.aliyun.AliyunParameter; +import com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(path = "/captcha") +public class CaptchamentController { + + @Autowired + private CaptchaClient captchaClient; + + @RequestMapping(path = "/validate", method = RequestMethod.GET) + public Status validate(HttpServletRequest request){ + ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter()); + return captchaValidator.validate(request); + } + +} +``` + +以上是基于 servlet 的一个简单实例,`buession-security-captcha` 基于上述模式也可以用于 webflux 环境。`CaptchaValidator` 的每个最终实现,均通过构造函数设置 `com.buession.security.captcha.CaptchaClient` 和 `com.buession.security.captcha.core.Parameter`。通过 `com.buession.security.captcha.core.Parameter` 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 `com.buession.security.captcha.core.Parameter` 均设置了默认值。 + + +### [API 参考手册>>](https://javadoc.io/doc/com.buession.security/buession-security-captcha/3.0.0/index.html) \ No newline at end of file diff --git a/manual/3.0/captcha/parameter.md b/manual/3.0/captcha/parameter.md new file mode 100644 index 0000000..48bec98 --- /dev/null +++ b/manual/3.0/captcha/parameter.md @@ -0,0 +1,44 @@ +# buession-security-captcha 参考手册 + + +本文档用于说明 `com.buession.security.captcha.core.Parameter` 和官方参数的对应关系。 + + +### 阿里云 + +| 参数名 | 官方参数名 | 默认值 | 说明 | +| ---- | ---- | ---- | ---- | +| sessionId | SessionId | sessionId | 会话 ID | +| sig | Sig | sig | 签名串 | +| token | Token | token | 请求唯一标识 | +| scene | Scene | scene | 场景标识 | + + +### 极验 + +第三代 + +| 参数名 | 官方参数名 | 默认值 | 说明 | +| ---- | ---- | ---- | ---- | +| challenge | challenge | challenge | 流水号 | +| seccode | seccode | seccode | 核心校验数据 | +| validate | validate | validate | 核心校验数据 | +| userId | user_id | user_id | user_id作为终端用户的唯一标识,确定用户的唯一性 | +| clientType | client_type | client_type | 客户端类型 | + +第四代 + +| 参数名 | 官方参数名 | 默认值 | 说明 | +| ---- | ---- | ---- | ---- | +| lotNumber | lot_number | lot_number | 验证流水号 | +| captchaOutput | captcha_output | captcha_output | 验证输出信息 | +| passToken | pass_token | pass_token | 验证通过标识 | +| genTime | gen_time | gen_time | 验证通过时间戳 | + + +### 腾讯云 + +| 参数名 | 官方参数名 | 默认值 | 说明 | +| ---- | ---- | ---- | ---- | +| randStr | Randstr | Randstr | 客户端验证回调的随机串 | +| ticket | Ticket | Ticket | 票据 | \ No newline at end of file diff --git a/manual/3.0/core/index.md b/manual/3.0/core/index.md new file mode 100644 index 0000000..1355516 --- /dev/null +++ b/manual/3.0/core/index.md @@ -0,0 +1,30 @@ +# buession-security-core 参考手册 + + +该类库为核心包,目前仅实现了 `SameSite` 枚举的定义和数据脱敏工具 `Desensitization`。 + + +--- + + +### 安装 + +```xml + + com.buession.security + buession-security-core + x.x.x + +``` + + +数据脱敏: + +```java +import com.buession.security.core.Desensitization; + +String str = Desensitization.encode("13800138000", 3); // 1380***8000 +``` + + +### [API 参考手册>>](https://javadoc.io/doc/com.buession.security/buession-security-core/3.0.0/index.html) \ No newline at end of file diff --git a/manual/3.0/crypto/index.md b/manual/3.0/crypto/index.md new file mode 100644 index 0000000..bc2fe0e --- /dev/null +++ b/manual/3.0/crypto/index.md @@ -0,0 +1,24 @@ +# buession-security-crypto 参考手册 + + +数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等接口。 + + +--- + + +### 安装 + +```xml + + com.buession.security + buession-security-crypto + x.x.x + +``` + + +随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。`buession-security-crypto` 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。 + + +### [API 参考手册>>](https://javadoc.io/doc/com.buession.security/buession-security-crypto/3.0.0/index.html) \ No newline at end of file diff --git a/manual/3.0/index.md b/manual/3.0/index.md new file mode 100644 index 0000000..a1a0ba7 --- /dev/null +++ b/manual/3.0/index.md @@ -0,0 +1,16 @@ +# API 参考手册 + + +Buession Security API 包含以下目录: + + +| 模块 | 使用帮助 | 手册 | +| ---- | ---- | ---- | +| buession-security-core | [使用帮助](core/index.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-core/3.0.0/) | +| buession-security-captcha | [使用帮助](beans/captcha.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-captcha/3.0.0/) | +| buession-security-crypto | [使用帮助](crypto/index.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-crypto/3.0.0/) | +| buession-security-mcrypt | [使用帮助](mcrypt/index.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-mcrypt/3.0.0/) | +| buession-security-pac4j | [使用帮助](pac4j/index.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-pac4j/3.0.0/) | +| buession-security-shiro | [使用帮助](shiro/index.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-shiro/3.0.0/) | +| buession-security-spring | [使用帮助](spring/index.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-spring/3.0.0/) | +| buession-security-web | [使用帮助](web/index.md) | [API 手册](https://javadoc.io/doc/com.buession.security/buession-security-web/3.0.0/) | \ No newline at end of file diff --git a/manual/3.0/pac4j/ajaxrequestresolver.md b/manual/3.0/pac4j/ajaxrequestresolver.md new file mode 100644 index 0000000..f2da176 --- /dev/null +++ b/manual/3.0/pac4j/ajaxrequestresolver.md @@ -0,0 +1,24 @@ +# buession-security-pac4j 参考手册 + + +## AjaxRequestResolver + + +pac4j 原生的 AJAX 请求解析器 `AjaxRequestResolver` 的实现 `DefaultAjaxRequestResolver`,以 XML 的形式响应 pac4j 重定向。 + +```xml + + + + +``` + +此种,场景增加了响应数据的大小和前端 Ajax 解析的成本和难度。为此,我们扩展了 `JsonAjaxRequestResolver` 和 `TextAjaxRequestResolver` 以 JSON 和文本的形式响应重定向地址,以简化前端 JavaScript 的解析成本和难度。 + +```json +{"redirect": {"url": "redirect_url"}} +``` + +```text +redirect_url +``` \ No newline at end of file diff --git a/manual/3.0/pac4j/annotation.md b/manual/3.0/pac4j/annotation.md new file mode 100644 index 0000000..b5ba4f8 --- /dev/null +++ b/manual/3.0/pac4j/annotation.md @@ -0,0 +1,37 @@ +# buession-security-pac4j 参考手册 + + +## 注解 + + +我们通过注解的形式封装了获取当前登录用户信息的 API。 + + +### 注解 + +| 注解 | 作用域 | 说明 | +| ---- | ---- | ---- | +| @Principal | 方法参数 | 获取当前登录用户信息,并可以以任何实体类、Map 对象的形式返回 | + + +#### 获取当前登录用户 + +```java +@Controller +@RequestMapping(path = "/test") +public class TestController { + + @RequestMapping(path = "/principal1") + @ResponseBody + public User principal1(@Principal User user, ServerHttpResponse response){ + return user; + } + + @RequestMapping(path = "/principal2") + @ResponseBody + public Map principal2(@Principal Map user, ServerHttpResponse response){ + return user; + } + +} +``` \ No newline at end of file diff --git a/manual/3.0/pac4j/index.md b/manual/3.0/pac4j/index.md new file mode 100644 index 0000000..3c47fa1 --- /dev/null +++ b/manual/3.0/pac4j/index.md @@ -0,0 +1,21 @@ +# buession-security-pac4j 参考手册 + + +对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。 + + +--- + + +### 安装 + +```xml + + com.buession.security + buession-security-pac4j + x.x.x + +``` + + +### [API 参考手册>>](https://javadoc.io/doc/com.buession.security/buession-security-pac4j/3.0.0/index.html) \ No newline at end of file diff --git a/manual/3.0/shiro/index.md b/manual/3.0/shiro/index.md new file mode 100644 index 0000000..5ed3cc4 --- /dev/null +++ b/manual/3.0/shiro/index.md @@ -0,0 +1,21 @@ +# buession-security-shiro 参考手册 + + +apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。 + + +--- + + +### 安装 + +```xml + + com.buession.security + buession-security-shiro + x.x.x + +``` + + +### [API 参考手册>>](https://javadoc.io/doc/com.buession.security/buession-security-shiro/3.0.0/index.html) \ No newline at end of file diff --git a/manual/3.0/spring/index.md b/manual/3.0/spring/index.md new file mode 100644 index 0000000..9b244be --- /dev/null +++ b/manual/3.0/spring/index.md @@ -0,0 +1,23 @@ +# buession-security-spring 参考手册 + + +集成 spring security 框架。 + + +--- + + +### 安装 + +```xml + + com.buession.security + buession-security-spring + x.x.x + +``` + +该模块无功能,仅仅整合把 spring security 的依赖整合进来了。 + + +### [API 参考手册>>](https://javadoc.io/doc/com.buession.security/buession-security-spring/3.0.0/index.html) \ No newline at end of file diff --git a/manual/3.0/web/index.md b/manual/3.0/web/index.md new file mode 100644 index 0000000..1051867 --- /dev/null +++ b/manual/3.0/web/index.md @@ -0,0 +1,23 @@ +# buession-security-web 参考手册 + + +web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。 + + +--- + + +### 安装 + +```xml + + com.buession.security + buession-security-web + x.x.x + +``` + +您可以通过 `ServletWebSecurityConfigurerAdapterConfiguration`、`ReactiveWebSecurityConfigurerAdapterConfiguration` 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。 + + +### [API 参考手册>>](https://javadoc.io/doc/com.buession.security/buession-security-web/3.0.0/index.html) \ No newline at end of file diff --git a/manual/index.html b/manual/index.html new file mode 100644 index 0000000..c09f19c --- /dev/null +++ b/manual/index.html @@ -0,0 +1,19 @@ +参考手册简介-参考手册

参考手册简介

+

Buession Security 是一款安全方面的框架,实现了当前流行的行为验证、加密、数据脱敏、集成 pac4j 和 shiro、集成 spring security,提供浏览器相关的安全响应头。更多介绍开源查阅框架介绍

+

本章节将想您讲解,如何使用 Buession Security,为您提供 Java 应用的最佳实践。

+
\ No newline at end of file diff --git a/manual/overview.html b/manual/overview.html new file mode 100644 index 0000000..32fba9c --- /dev/null +++ b/manual/overview.html @@ -0,0 +1,48 @@ +参考指南-参考手册

参考指南

+

本文档包含了完整的 Buession Security 的参考文档。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
版本手册
3.0.xAPI 手册
2.3.xAPI 手册
2.2.xAPI 手册
2.1.xAPI 手册
2.0.xAPI 手册
+
\ No newline at end of file diff --git a/search_json.js b/search_json.js new file mode 100644 index 0000000..79a8101 --- /dev/null +++ b/search_json.js @@ -0,0 +1,881 @@ +window.ydoc_plugin_search_json = { + "快速上手": [ + { + "title": "快速入门", + "content": "TIP\n\n官方指南假设您已了解\"JAVA\"方面的相关知识,并且对安全相关的知识有一定认知。\n\nBuession Security 是一款安全方面的框架,实现了当前流行的行为验证码、加解密、数据脱敏、集成 pac4j 和 shiro、集成 spring security,提供浏览器相关的安全响应头。", + "url": "/docs/quickstart.html", + "children": [ + { + "title": "下一步可做什么?", + "url": "/docs/quickstart.html#下一步可做什么?", + "content": "下一步可做什么?您对 Buession Security 大致了解后,您接下来可以做以下事情:了解兼容性:了解 Buession Security 的兼容性\n安装:安装/引用 Buession Security\n使用:开始使用 Buession Security 功能\n" + } + ] + }, + { + "title": "框架介绍", + "content": "", + "url": "/docs/intro.html", + "children": [ + { + "title": "Buession Security 框架是什么?", + "url": "/docs/intro.html#buession-security-框架是什么?", + "content": "Buession Security 框架是什么?基于 Pac4j、Shiro 二次封装,集成 Spring Security,提供极验、阿里云、腾讯云行为验证码 SDK,封装了数据脱敏和多种密码加密方式工具类库的一款安全框架。数据加密封装封装了 MD5、SHA1、SHA256、SHA512、HmacSHA1、HmacSHA256、HmacSHA512 等散列算法\n封装了 AES、DES 等对称加密算法\n封装了 Base64、原 Discuz 论坛中的加解密算法\n封装了基于以上部分算法的密码生成器,未来将会一一实现\n未来将会提供更多的常用加密算法,如:RSA 等等... ...行为验证码封装了阿里云、极验、腾讯云行为验证码,实现标准的 SDK。您只需,更改一下 CaptchaValidator、CaptchaClient 实现类的初始化,即可快速完成行为验证码的更换(当然您还需要修改前端 WEB 代码)未来,我们将会接入更多的行为验证码... ...数据脱敏为保证返回给前端的数据的安全性,我们提供了数据脱敏处理的工具类整合三方安全框架对 pac4j、io.buji:buji-pac4j 进行了整合\n整合了 apache shiro,并实现了基于 redis 的 session、cache 的缓存;并提供了对 velocity 的支持,未来会支持更多的模板引擎\n整合了 spring security,以及对 spring security 默认 Configurer 的修改,支持 servlet 和 webflux\n提供了 XSS 过滤器 XssFilter,支持 servlet 和 webflux\n" + } + ] + }, + { + "title": "开源协议", + "content": " Apache License Version 2.0, January 2004\n http://www.apache.org/licenses/\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\nDefinitions.\n\"License\" shall mean the terms and conditions for use, reproduction,\nand distribution as defined by Sections 1 through 9 of this document.\n\"Licensor\" shall mean the copyright owner or entity authorized by\nthe copyright owner that is granting the License.\n\"Legal Entity\" shall mean the union of the acting entity and all\nother entities that control, are controlled by, or are under common\ncontrol with that entity. For the purposes of this definition,\n\"control\" means (i) the power, direct or indirect, to cause the\ndirection or management of such entity, whether by contract or\notherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\"You\" (or \"Your\") shall mean an individual or Legal Entity\nexercising permissions granted by this License.\n\"Source\" form shall mean the preferred form for making modifications,\nincluding but not limited to software source code, documentation\nsource, and configuration files.\n\"Object\" form shall mean any form resulting from mechanical\ntransformation or translation of a Source form, including but\nnot limited to compiled object code, generated documentation,\nand conversions to other media types.\n\"Work\" shall mean the work of authorship, whether in Source or\nObject form, made available under the License, as indicated by a\ncopyright notice that is included in or attached to the work\n(an example is provided in the Appendix below).\n\"Derivative Works\" shall mean any work, whether in Source or Object\nform, that is based on (or derived from) the Work and for which the\neditorial revisions, annotations, elaborations, or other modifications\nrepresent, as a whole, an original work of authorship. For the purposes\nof this License, Derivative Works shall not include works that remain\nseparable from, or merely link (or bind by name) to the interfaces of,\nthe Work and Derivative Works thereof.\n\"Contribution\" shall mean any work of authorship, including\nthe original version of the Work and any modifications or additions\nto that Work or Derivative Works thereof, that is intentionally\nsubmitted to Licensor for inclusion in the Work by the copyright owner\nor by an individual or Legal Entity authorized to submit on behalf of\nthe copyright owner. For the purposes of this definition, \"submitted\"\nmeans any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems,\nand issue tracking systems that are managed by, or on behalf of, the\nLicensor for the purpose of discussing and improving the Work, but\nexcluding communication that is conspicuously marked or otherwise\ndesignated in writing by the copyright owner as \"Not a Contribution.\"\n\"Contributor\" shall mean Licensor and any individual or Legal Entity\non behalf of whom a Contribution has been received by Licensor and\nsubsequently incorporated within the Work.\n\n\nGrant of Copyright License. Subject to the terms and conditions of\nthis License, each Contributor hereby grants to You a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable\ncopyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the\nWork and such Derivative Works in Source or Object form.\n\n\nGrant of Patent License. Subject to the terms and conditions of\nthis License, each Contributor hereby grants to You a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable\n(except as stated in this section) patent license to make, have made,\nuse, offer to sell, sell, import, and otherwise transfer the Work,\nwhere such license applies only to those patent claims licensable\nby such Contributor that are necessarily infringed by their\nContribution(s) alone or by combination of their Contribution(s)\nwith the Work to which such Contribution(s) was submitted. If You\ninstitute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work\nor a Contribution incorporated within the Work constitutes direct\nor contributory patent infringement, then any patent licenses\ngranted to You under this License for that Work shall terminate\nas of the date such litigation is filed.\n\n\nRedistribution. You may reproduce and distribute copies of the\nWork or Derivative Works thereof in any medium, with or without\nmodifications, and in Source or Object form, provided that You\nmeet the following conditions:\n(a) You must give any other recipients of the Work or\nDerivative Works a copy of this License; and\n(b) You must cause any modified files to carry prominent notices\nstating that You changed the files; and\n(c) You must retain, in the Source form of any Derivative Works\nthat You distribute, all copyright, patent, trademark, and\nattribution notices from the Source form of the Work,\nexcluding those notices that do not pertain to any part of\nthe Derivative Works; and\n(d) If the Work includes a \"NOTICE\" text file as part of its\ndistribution, then any Derivative Works that You distribute must\ninclude a readable copy of the attribution notices contained\nwithin such NOTICE file, excluding those notices that do not\npertain to any part of the Derivative Works, in at least one\nof the following places: within a NOTICE text file distributed\nas part of the Derivative Works; within the Source form or\ndocumentation, if provided along with the Derivative Works; or,\nwithin a display generated by the Derivative Works, if and\nwherever such third-party notices normally appear. The contents\nof the NOTICE file are for informational purposes only and\ndo not modify the License. You may add Your own attribution\nnotices within Derivative Works that You distribute, alongside\nor as an addendum to the NOTICE text from the Work, provided\nthat such additional attribution notices cannot be construed\nas modifying the License.\nYou may add Your own copyright statement to Your modifications and\nmay provide additional or different license terms and conditions\nfor use, reproduction, or distribution of Your modifications, or\nfor any such Derivative Works as a whole, provided Your use,\nreproduction, and distribution of the Work otherwise complies with\nthe conditions stated in this License.\n\n\nSubmission of Contributions. Unless You explicitly state otherwise,\nany Contribution intentionally submitted for inclusion in the Work\nby You to the Licensor shall be under the terms and conditions of\nthis License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify\nthe terms of any separate license agreement you may have executed\nwith Licensor regarding such Contributions.\n\n\nTrademarks. This License does not grant permission to use the trade\nnames, trademarks, service marks, or product names of the Licensor,\nexcept as required for reasonable and customary use in describing the\norigin of the Work and reproducing the content of the NOTICE file.\n\n\nDisclaimer of Warranty. Unless required by applicable law or\nagreed to in writing, Licensor provides the Work (and each\nContributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\nimplied, including, without limitation, any warranties or conditions\nof TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\nPARTICULAR PURPOSE. You are solely responsible for determining the\nappropriateness of using or redistributing the Work and assume any\nrisks associated with Your exercise of permissions under this License.\n\n\nLimitation of Liability. In no event and under no legal theory,\nwhether in tort (including negligence), contract, or otherwise,\nunless required by applicable law (such as deliberate and grossly\nnegligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special,\nincidental, or consequential damages of any character arising as a\nresult of this License or out of the use or inability to use the\nWork (including but not limited to damages for loss of goodwill,\nwork stoppage, computer failure or malfunction, or any and all\nother commercial damages or losses), even if such Contributor\nhas been advised of the possibility of such damages.\n\n\nAccepting Warranty or Additional Liability. While redistributing\nthe Work or Derivative Works thereof, You may choose to offer,\nand charge a fee for, acceptance of support, warranty, indemnity,\nor other liability obligations and/or rights consistent with this\nLicense. However, in accepting such obligations, You may act only\non Your own behalf and on Your sole responsibility, not on behalf\nof any other Contributor, and only if You agree to indemnify,\ndefend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason\nof your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONSAPPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\nCopyright [yyyy] [name of copyright owner]Licensed under the Apache License, Version 2.0 (the \"License\");you may not use this file except in compliance with the License.\nYou may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.", + "url": "/docs/license.html", + "children": [] + }, + { + "title": "模块说明", + "content": "", + "url": "/docs/module.html", + "children": [ + { + "title": "buession-security-core", + "url": "/docs/module.html#buession-security-core", + "content": "buession-security-core安全核心类库,安全相关实体类、枚举定义,数据脱敏工具。\n" + }, + { + "title": "buession-security-captcha", + "url": "/docs/module.html#buession-security-captcha", + "content": "buession-security-captcha极验、阿里云、腾讯云行为验证 SDK\n" + }, + { + "title": "buession-security-mcrypt", + "url": "/docs/module.html#buession-security-mcrypt", + "content": "buession-security-mcrypt数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。\n" + }, + { + "title": "buession-security-pac4j", + "url": "/docs/module.html#buession-security-pac4j", + "content": "buession-security-pac4j对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j\n" + }, + { + "title": "buession-security-shiro", + "url": "/docs/module.html#buession-security-shiro", + "content": "buession-security-shiroapache shiro 二次封装\n增加 redis 对 session 和 cache 的管理\n" + }, + { + "title": "buession-security-spring", + "url": "/docs/module.html#buession-security-spring", + "content": "buession-security-spring集成 spring security 框架\n" + }, + { + "title": "buession-security-web", + "url": "/docs/module.html#buession-security-web", + "content": "buession-security-webweb 安全相关的功能封装,支持 servlet 和 reactive\nHttp 安全构建器,HttpSecurityBuilder\nXSS filter\n" + } + ] + }, + { + "title": "版本说明", + "content": "该项目基于 GNU 版风格定义项目版本,即:主版本号.子版本号.修正版本号。管理策略主版本号,发生变更时,不保证所有的 API 对上一个版本兼容,但保障大部分能兼容;主版本变更,可能涉及类、接口、枚举、方法的删除,或者包名的变更\n子版本号,发生变更时,完全兼容上一个版本,主要会增加一些小的功能或API,底层逻辑的调整调优\n修正版本号,主要用于修复 BUG、优化性能、安全漏洞修复,不会新增、变更、删除已有 API\n三方包兼容性说明当引用的三方包,我们保证尽大可能兼容。但对于 springframework、springboot、springcloud、springsecurity、springdata 等 spring 家族组件,以及 servlet 兼容对应的主版本。", + "url": "/docs/version.html", + "children": [] + }, + { + "title": "安装及使用", + "content": "", + "url": "/docs/installation.html", + "children": [ + { + "title": "Maven 中央仓库搜索", + "url": "/docs/installation.html#maven-中央仓库搜索", + "content": "Maven 中央仓库搜索https://mvnrepository.com/search?q=com.buession.security\nhttps://search.maven.org/search?q=g:com.buession.security\n" + }, + { + "title": "手动编译", + "url": "/docs/installation.html#手动编译", + "content": "手动编译git clone https://github.com/buession/buession-securitycd buession-security/buession-security-parent && mvn clean install\n" + }, + { + "title": "Maven", + "url": "/docs/installation.html#maven", + "content": "Maven com.buession.security\n buession-security-xxx\n x.x.x\n\n" + }, + { + "title": "Gradle", + "url": "/docs/installation.html#gradle", + "content": "Gradlecompile group: 'com.buession.security', name: 'buession-security-xxx', version: 'x.x.x'其中,artifactId 中的 xxx 表示对应的子模块;version 中的 x.x.x 代表版本号,根据需要使用特定版本,建议使用 maven 仓库中已构建好的最新版本的包。" + } + ] + }, + { + "title": "环境要求", + "content": "JDKJDK 8+构建工具\n\n构建工具\n版本\n\n\n\n\nMaven\n3.5+\n\n\nGradle\n6.x+,推荐 6.3 及以上版本\n\n\nServlet 容器支持 servlet 3.1+,推荐使用 servlet 4.0 及以上版本。", + "url": "/docs/requirement.html", + "children": [] + } + ], + "参考手册": [ + { + "title": "参考手册简介", + "content": "Buession Security 是一款安全方面的框架,实现了当前流行的行为验证、加密、数据脱敏、集成 pac4j 和 shiro、集成 spring security,提供浏览器相关的安全响应头。更多介绍开源查阅框架介绍。本章节将想您讲解,如何使用 Buession Security,为您提供 Java 应用的最佳实践。", + "url": "/manual/index.html", + "children": [] + }, + { + "title": "参考指南", + "content": "本文档包含了完整的 Buession Security 的参考文档。\n\n版本\n手册\n\n\n\n\n3.0.x\nAPI 手册\n\n\n2.3.x\nAPI 手册\n\n\n2.2.x\nAPI 手册\n\n\n2.1.x\nAPI 手册\n\n\n2.0.x\nAPI 手册\n\n\n", + "url": "/manual/overview.html", + "children": [] + }, + { + "title": "API 参考手册", + "content": "Buession Security API 包含以下目录:\n\n模块\n使用帮助\n手册\n\n\n\n\nbuession-security-core\n使用帮助\nAPI 手册\n\n\nbuession-security-captcha\n使用帮助\nAPI 手册\n\n\nbuession-security-crypto\n使用帮助\nAPI 手册\n\n\nbuession-security-mcrypt\n使用帮助\nAPI 手册\n\n\nbuession-security-pac4j\n使用帮助\nAPI 手册\n\n\nbuession-security-shiro\n使用帮助\nAPI 手册\n\n\nbuession-security-spring\n使用帮助\nAPI 手册\n\n\nbuession-security-web\n使用帮助\nAPI 手册\n\n\n", + "url": "/manual/2.3/index.html", + "children": [] + }, + { + "title": "buession-security-core 参考手册", + "content": "该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization。", + "url": "/manual/2.3/core/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/core/index.html#安装", + "content": "安装 com.buession.security\n buession-security-core\n x.x.x\n\n数据脱敏:import com.buession.security.core.Desensitization;\nString str = Desensitization.encode(\"13800138000\", 3); // 1380***8000\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/core/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。", + "url": "/manual/2.3/captcha/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/captcha/index.html#安装", + "content": "安装 com.buession.security\n buession-security-captcha\n x.x.x\n\n我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。AliYunRequestData:阿里云验证码请求数据\nGeetestV3RequestData:极验第三代行为验证码请求数据\nGeetestV4RequestData:极验第四代行为验证码请求数据\nTencentRequestData:腾讯云验证码请求数据\n" + }, + { + "title": "阿里云", + "url": "/manual/2.3/captcha/index.html#阿里云", + "content": "阿里云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.aliyun.AliYunCaptchaClient;\nimport com.buession.security.captcha.aliyun.AliYunRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new AliYunCaptchaClient(\"Your accessKeyId\", \"Your accessKeySecret\", \"Your appKey\", httpClient);\n\nRequestData request = new AliYunRequestData();\nrequest.setToken(\"token\");\nrequest.setSig(\"sig\");\nrequest.setSessionId(\"session id\");\nrequest.setScene(\"ecene\");\nrequest.setRemoteIp(\"User client ip\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "极验", + "url": "/manual/2.3/captcha/index.html#极验", + "content": "极验import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.geetest.GeetestCaptchaClient;\nimport com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new GeetestCaptchaClient(\"Your appId\", \"Your secretKey\", \"version\", httpClient);\n\nRequestData request = new GeetestV4RequestData();\nrequest.setLotNumber(\"lot number\");\nrequest.setCaptchaOutput(\"captcha Output\");\nrequest.setPassToken(\"pass token\");\nrequest.setGenTime(\"gen time\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.3/captcha/index.html#腾讯云", + "content": "腾讯云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.tencent.TencentCaptchaClient;\nimport com.buession.security.captcha.tencent.TencentRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new TencentCaptchaClient(\"Your secretId\", \"Your secretKey\", httpClient);\n\nRequestData request = new TencentRequestData();\nrequest.setRandstr(\"rand str\");\nrequest.setTicket(\"ticket\");\nrequest.setUserIp(\"User client ip\");\ncaptchaClient.validate(request);\n当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。在您的 controller 中您可以这么用。import com.buession.lang.Status;import com.buession.web.mvc.Response;\nimport com.buession.security.captcha.CaptchaClient;\nimport com.buession.security.captcha.aliyun.AliyunParameter;\nimport com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(path = \"/captcha\")\npublic class CaptchamentController {\n\n @Autowired\n private CaptchaClient captchaClient;\n\n @RequestMapping(path = \"/validate\", method = RequestMethod.GET)\n public Status validate(HttpServletRequest request){\n ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());\n return captchaValidator.validate(request);\n }\n\n}\n以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClient 和 com.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/captcha/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。", + "url": "/manual/2.3/captcha/parameter.html", + "children": [ + { + "title": "阿里云", + "url": "/manual/2.3/captcha/parameter.html#阿里云", + "content": "阿里云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nsessionId\nSessionId\nsessionId\n会话 ID\n\n\nsig\nSig\nsig\n签名串\n\n\ntoken\nToken\ntoken\n请求唯一标识\n\n\nscene\nScene\nscene\n场景标识\n\n\n" + }, + { + "title": "极验", + "url": "/manual/2.3/captcha/parameter.html#极验", + "content": "极验第三代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nchallenge\nchallenge\nchallenge\n流水号\n\n\nseccode\nseccode\nseccode\n核心校验数据\n\n\nvalidate\nvalidate\nvalidate\n核心校验数据\n\n\nuserId\nuser_id\nuser_id\nuser_id作为终端用户的唯一标识,确定用户的唯一性\n\n\nclientType\nclient_type\nclient_type\n客户端类型\n\n\n第四代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nlotNumber\nlot_number\nlot_number\n验证流水号\n\n\ncaptchaOutput\ncaptcha_output\ncaptcha_output\n验证输出信息\n\n\npassToken\npass_token\npass_token\n验证通过标识\n\n\ngenTime\ngen_time\ngen_time\n验证通过时间戳\n\n\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.3/captcha/parameter.html#腾讯云", + "content": "腾讯云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nrandStr\nRandstr\nRandstr\n客户端验证回调的随机串\n\n\nticket\nTicket\nTicket\n票据\n\n\n" + } + ] + }, + { + "title": "buession-security-crypto 参考手册", + "content": "数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等接口。", + "url": "/manual/2.3/crypto/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/crypto/index.html#安装", + "content": "安装 com.buession.security\n buession-security-crypto\n x.x.x\n\n随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-crypto 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/crypto/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-mcrypt 参考手册", + "content": "数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。", + "url": "/manual/2.3/mcrypt/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/mcrypt/index.html#安装", + "content": "安装 com.buession.security\n buession-security-mcrypt\n x.x.x\n\n随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。encode:加密,对任意对象进行加密,如果参数为 char[]、byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密\ndecode:解密,对 CharSequence 进行解密\nimport com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt();\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();\nmcrypt.encode(new Integer(100));\n您可以指定加密 key。import com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(new Integer(100));\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/mcrypt/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。", + "url": "/manual/2.3/pac4j/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/pac4j/index.html#安装", + "content": "安装 com.buession.security\n buession-security-pac4j\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/pac4j/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "", + "url": "/manual/2.3/pac4j/annotation.html", + "children": [ + { + "title": "注解", + "url": "/manual/2.3/pac4j/annotation.html#注解", + "content": "注解我们通过注解的形式封装了获取当前登录用户信息的 API。" + }, + { + "title": "注解", + "url": "/manual/2.3/pac4j/annotation.html#注解-注解", + "content": "注解\n\n注解\n作用域\n说明\n\n\n\n\n@Principal\n方法参数\n获取当前登录用户信息,并可以以任何实体类、Map 对象的形式返回\n\n\n获取当前登录用户@Controller@RequestMapping(path = \"/test\")\npublic class TestController {\n\n @RequestMapping(path = \"/principal1\")\n @ResponseBody\n public User principal1(@Principal User user, ServerHttpResponse response){\n return user;\n }\n\n @RequestMapping(path = \"/principal2\")\n @ResponseBody\n public Map principal2(@Principal Map user, ServerHttpResponse response){\n return user;\n }\n\n}\n" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "", + "url": "/manual/2.3/pac4j/ajaxrequestresolver.html", + "children": [ + { + "title": "AjaxRequestResolver", + "url": "/manual/2.3/pac4j/ajaxrequestresolver.html#ajaxrequestresolver", + "content": "AjaxRequestResolverpac4j 原生的 AJAX 请求解析器 AjaxRequestResolver 的实现 DefaultAjaxRequestResolver,以 XML 的形式响应 pac4j 重定向。\n \n\n此种,场景增加了响应数据的大小和前端 Ajax 解析的成本和难度。为此,我们扩展了 JsonAjaxRequestResolver 和 TextAjaxRequestResolver 以 JSON 和文本的形式响应重定向地址,以简化前端 JavaScript 的解析成本和难度。{\"redirect\": {\"url\": \"redirect_url\"}}redirect_url" + } + ] + }, + { + "title": "buession-security-shiro 参考手册", + "content": "apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。", + "url": "/manual/2.3/shiro/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/shiro/index.html#安装", + "content": "安装 com.buession.security\n buession-security-shiro\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/shiro/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-spring 参考手册", + "content": "集成 spring security 框架。", + "url": "/manual/2.3/spring/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/spring/index.html#安装", + "content": "安装 com.buession.security\n buession-security-spring\n x.x.x\n\n该模块无功能,仅仅整合把 spring security 的依赖整合进来了。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/spring/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-web 参考手册", + "content": "web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。", + "url": "/manual/2.3/web/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.3/web/index.html#安装", + "content": "安装 com.buession.security\n buession-security-web\n x.x.x\n\n您可以通过 ServletWebSecurityConfigurerAdapterConfiguration、ReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.3/web/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "API 参考手册", + "content": "Buession Security API 包含以下目录:\n\n模块\n使用帮助\n手册\n\n\n\n\nbuession-security-core\n使用帮助\nAPI 手册\n\n\nbuession-security-captcha\n使用帮助\nAPI 手册\n\n\nbuession-security-mcrypt\n使用帮助\nAPI 手册\n\n\nbuession-security-pac4j\n使用帮助\nAPI 手册\n\n\nbuession-security-shiro\n使用帮助\nAPI 手册\n\n\nbuession-security-spring\n使用帮助\nAPI 手册\n\n\nbuession-security-web\n使用帮助\nAPI 手册\n\n\n", + "url": "/manual/2.2/index.html", + "children": [] + }, + { + "title": "buession-security-core 参考手册", + "content": "该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization。", + "url": "/manual/2.2/core/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.2/core/index.html#安装", + "content": "安装 com.buession.security\n buession-security-core\n x.x.x\n\n数据脱敏:import com.buession.security.core.Desensitization;\nString str = Desensitization.encode(\"13800138000\", 3); // 1380***8000\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.2/core/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。", + "url": "/manual/2.2/captcha/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.2/captcha/index.html#安装", + "content": "安装 com.buession.security\n buession-security-captcha\n x.x.x\n\n我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。AliYunRequestData:阿里云验证码请求数据\nGeetestV3RequestData:极验第三代行为验证码请求数据\nGeetestV4RequestData:极验第四代行为验证码请求数据\nTencentRequestData:腾讯云验证码请求数据\n" + }, + { + "title": "阿里云", + "url": "/manual/2.2/captcha/index.html#阿里云", + "content": "阿里云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.aliyun.AliYunCaptchaClient;\nimport com.buession.security.captcha.aliyun.AliYunRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new AliYunCaptchaClient(\"Your accessKeyId\", \"Your accessKeySecret\", \"Your appKey\", httpClient);\n\nRequestData request = new AliYunRequestData();\nrequest.setToken(\"token\");\nrequest.setSig(\"sig\");\nrequest.setSessionId(\"session id\");\nrequest.setScene(\"ecene\");\nrequest.setRemoteIp(\"User client ip\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "极验", + "url": "/manual/2.2/captcha/index.html#极验", + "content": "极验import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.geetest.GeetestCaptchaClient;\nimport com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new GeetestCaptchaClient(\"Your appId\", \"Your secretKey\", \"version\", httpClient);\n\nRequestData request = new GeetestV4RequestData();\nrequest.setLotNumber(\"lot number\");\nrequest.setCaptchaOutput(\"captcha Output\");\nrequest.setPassToken(\"pass token\");\nrequest.setGenTime(\"gen time\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.2/captcha/index.html#腾讯云", + "content": "腾讯云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.tencent.TencentCaptchaClient;\nimport com.buession.security.captcha.tencent.TencentRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new TencentCaptchaClient(\"Your secretId\", \"Your secretKey\", httpClient);\n\nRequestData request = new TencentRequestData();\nrequest.setRandstr(\"rand str\");\nrequest.setTicket(\"ticket\");\nrequest.setUserIp(\"User client ip\");\ncaptchaClient.validate(request);\n当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。在您的 controller 中您可以这么用。import com.buession.lang.Status;import com.buession.web.mvc.Response;\nimport com.buession.security.captcha.CaptchaClient;\nimport com.buession.security.captcha.aliyun.AliyunParameter;\nimport com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(path = \"/captcha\")\npublic class CaptchamentController {\n\n @Autowired\n private CaptchaClient captchaClient;\n\n @RequestMapping(path = \"/validate\", method = RequestMethod.GET)\n public Status validate(HttpServletRequest request){\n ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());\n return captchaValidator.validate(request);\n }\n\n}\n以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClient 和 com.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.2/captcha/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。", + "url": "/manual/2.2/captcha/parameter.html", + "children": [ + { + "title": "阿里云", + "url": "/manual/2.2/captcha/parameter.html#阿里云", + "content": "阿里云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nsessionId\nSessionId\nsessionId\n会话 ID\n\n\nsig\nSig\nsig\n签名串\n\n\ntoken\nToken\ntoken\n请求唯一标识\n\n\nscene\nScene\nscene\n场景标识\n\n\n" + }, + { + "title": "极验", + "url": "/manual/2.2/captcha/parameter.html#极验", + "content": "极验第三代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nchallenge\nchallenge\nchallenge\n流水号\n\n\nseccode\nseccode\nseccode\n核心校验数据\n\n\nvalidate\nvalidate\nvalidate\n核心校验数据\n\n\nuserId\nuser_id\nuser_id\nuser_id作为终端用户的唯一标识,确定用户的唯一性\n\n\nclientType\nclient_type\nclient_type\n客户端类型\n\n\n第四代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nlotNumber\nlot_number\nlot_number\n验证流水号\n\n\ncaptchaOutput\ncaptcha_output\ncaptcha_output\n验证输出信息\n\n\npassToken\npass_token\npass_token\n验证通过标识\n\n\ngenTime\ngen_time\ngen_time\n验证通过时间戳\n\n\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.2/captcha/parameter.html#腾讯云", + "content": "腾讯云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nrandStr\nRandstr\nRandstr\n客户端验证回调的随机串\n\n\nticket\nTicket\nTicket\n票据\n\n\n" + } + ] + }, + { + "title": "buession-security-mcrypt 参考手册", + "content": "数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。", + "url": "/manual/2.2/mcrypt/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.2/mcrypt/index.html#安装", + "content": "安装 com.buession.security\n buession-security-mcrypt\n x.x.x\n\n随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。encode:加密,对任意对象进行加密,如果参数为 char[]、byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密\ndecode:解密,对 CharSequence 进行解密\nimport com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt();\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();\nmcrypt.encode(new Integer(100));\n您可以指定加密 key。import com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(new Integer(100));\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.2/mcrypt/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。", + "url": "/manual/2.2/pac4j/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.2/pac4j/index.html#安装", + "content": "安装 com.buession.security\n buession-security-pac4j\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.2/pac4j/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "", + "url": "/manual/2.2/pac4j/annotation.html", + "children": [ + { + "title": "注解", + "url": "/manual/2.2/pac4j/annotation.html#注解", + "content": "注解我们通过注解的形式封装了获取当前登录用户信息的 API。" + }, + { + "title": "注解", + "url": "/manual/2.2/pac4j/annotation.html#注解-注解", + "content": "注解\n\n注解\n作用域\n说明\n\n\n\n\n@Principal\n方法参数\n获取当前登录用户信息,并可以以任何实体类、Map 对象的形式返回\n\n\n获取当前登录用户@Controller@RequestMapping(path = \"/test\")\npublic class TestController {\n\n @RequestMapping(path = \"/principal1\")\n @ResponseBody\n public User principal1(@Principal User user, ServerHttpResponse response){\n return user;\n }\n\n @RequestMapping(path = \"/principal2\")\n @ResponseBody\n public Map principal2(@Principal Map user, ServerHttpResponse response){\n return user;\n }\n\n}\n" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "", + "url": "/manual/2.2/pac4j/ajaxrequestresolver.html", + "children": [ + { + "title": "AjaxRequestResolver", + "url": "/manual/2.2/pac4j/ajaxrequestresolver.html#ajaxrequestresolver", + "content": "AjaxRequestResolverpac4j 原生的 AJAX 请求解析器 AjaxRequestResolver 的实现 DefaultAjaxRequestResolver,以 XML 的形式响应 pac4j 重定向。\n \n\n此种,场景增加了响应数据的大小和前端 Ajax 解析的成本和难度。为此,我们扩展了 JsonAjaxRequestResolver 和 TextAjaxRequestResolver 以 JSON 和文本的形式响应重定向地址,以简化前端 JavaScript 的解析成本和难度。{\"redirect\": {\"url\": \"redirect_url\"}}redirect_url" + } + ] + }, + { + "title": "buession-security-shiro 参考手册", + "content": "apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。", + "url": "/manual/2.2/shiro/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.2/shiro/index.html#安装", + "content": "安装 com.buession.security\n buession-security-shiro\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.2/shiro/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-spring 参考手册", + "content": "集成 spring security 框架。", + "url": "/manual/2.2/spring/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.2/spring/index.html#安装", + "content": "安装 com.buession.security\n buession-security-spring\n x.x.x\n\n该模块无功能,仅仅整合把 spring security 的依赖整合进来了。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.2/spring/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-web 参考手册", + "content": "web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。", + "url": "/manual/2.2/web/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.2/web/index.html#安装", + "content": "安装 com.buession.security\n buession-security-web\n x.x.x\n\n您可以通过 ServletWebSecurityConfigurerAdapterConfiguration、ReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.2/web/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "API 参考手册", + "content": "Buession Security API 包含以下目录:\n\n模块\n使用帮助\n手册\n\n\n\n\nbuession-security-core\n使用帮助\nAPI 手册\n\n\nbuession-security-captcha\n使用帮助\nAPI 手册\n\n\nbuession-security-mcrypt\n使用帮助\nAPI 手册\n\n\nbuession-security-pac4j\n使用帮助\nAPI 手册\n\n\nbuession-security-shiro\n使用帮助\nAPI 手册\n\n\nbuession-security-spring\n使用帮助\nAPI 手册\n\n\nbuession-security-web\n使用帮助\nAPI 手册\n\n\n", + "url": "/manual/2.0/index.html", + "children": [] + }, + { + "title": "buession-security-core 参考手册", + "content": "该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization。", + "url": "/manual/2.0/core/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.0/core/index.html#安装", + "content": "安装 com.buession.security\n buession-security-core\n x.x.x\n\n数据脱敏:import com.buession.security.core.Desensitization;\nString str = Desensitization.encode(\"13800138000\", 3); // 1380***8000\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.0/core/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。", + "url": "/manual/2.0/captcha/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.0/captcha/index.html#安装", + "content": "安装 com.buession.security\n buession-security-captcha\n x.x.x\n\n我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。AliYunRequestData:阿里云验证码请求数据\nGeetestV3RequestData:极验第三代行为验证码请求数据\nGeetestV4RequestData:极验第四代行为验证码请求数据\nTencentRequestData:腾讯云验证码请求数据\n" + }, + { + "title": "阿里云", + "url": "/manual/2.0/captcha/index.html#阿里云", + "content": "阿里云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.aliyun.AliYunCaptchaClient;\nimport com.buession.security.captcha.aliyun.AliYunRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new AliYunCaptchaClient(\"Your accessKeyId\", \"Your accessKeySecret\", \"Your appKey\", httpClient);\n\nRequestData request = new AliYunRequestData();\nrequest.setToken(\"token\");\nrequest.setSig(\"sig\");\nrequest.setSessionId(\"session id\");\nrequest.setScene(\"ecene\");\nrequest.setRemoteIp(\"User client ip\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "极验", + "url": "/manual/2.0/captcha/index.html#极验", + "content": "极验import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.geetest.GeetestCaptchaClient;\nimport com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new GeetestCaptchaClient(\"Your appId\", \"Your secretKey\", \"version\", httpClient);\n\nRequestData request = new GeetestV4RequestData();\nrequest.setLotNumber(\"lot number\");\nrequest.setCaptchaOutput(\"captcha Output\");\nrequest.setPassToken(\"pass token\");\nrequest.setGenTime(\"gen time\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.0/captcha/index.html#腾讯云", + "content": "腾讯云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.tencent.TencentCaptchaClient;\nimport com.buession.security.captcha.tencent.TencentRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new TencentCaptchaClient(\"Your secretId\", \"Your secretKey\", httpClient);\n\nRequestData request = new TencentRequestData();\nrequest.setRandstr(\"rand str\");\nrequest.setTicket(\"ticket\");\nrequest.setUserIp(\"User client ip\");\ncaptchaClient.validate(request);\n当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。在您的 controller 中您可以这么用。import com.buession.lang.Status;import com.buession.web.mvc.Response;\nimport com.buession.security.captcha.CaptchaClient;\nimport com.buession.security.captcha.aliyun.AliyunParameter;\nimport com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(path = \"/captcha\")\npublic class CaptchamentController {\n\n @Autowired\n private CaptchaClient captchaClient;\n\n @RequestMapping(path = \"/validate\", method = RequestMethod.GET)\n public Status validate(HttpServletRequest request){\n ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());\n return captchaValidator.validate(request);\n }\n\n}\n以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClient 和 com.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.0/captcha/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。", + "url": "/manual/2.0/captcha/parameter.html", + "children": [ + { + "title": "阿里云", + "url": "/manual/2.0/captcha/parameter.html#阿里云", + "content": "阿里云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nsessionId\nSessionId\nsessionId\n会话 ID\n\n\nsig\nSig\nsig\n签名串\n\n\ntoken\nToken\ntoken\n请求唯一标识\n\n\nscene\nScene\nscene\n场景标识\n\n\n" + }, + { + "title": "极验", + "url": "/manual/2.0/captcha/parameter.html#极验", + "content": "极验第三代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nchallenge\nchallenge\nchallenge\n流水号\n\n\nseccode\nseccode\nseccode\n核心校验数据\n\n\nvalidate\nvalidate\nvalidate\n核心校验数据\n\n\nuserId\nuser_id\nuser_id\nuser_id作为终端用户的唯一标识,确定用户的唯一性\n\n\nclientType\nclient_type\nclient_type\n客户端类型\n\n\n第四代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nlotNumber\nlot_number\nlot_number\n验证流水号\n\n\ncaptchaOutput\ncaptcha_output\ncaptcha_output\n验证输出信息\n\n\npassToken\npass_token\npass_token\n验证通过标识\n\n\ngenTime\ngen_time\ngen_time\n验证通过时间戳\n\n\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.0/captcha/parameter.html#腾讯云", + "content": "腾讯云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nrandStr\nRandstr\nRandstr\n客户端验证回调的随机串\n\n\nticket\nTicket\nTicket\n票据\n\n\n" + } + ] + }, + { + "title": "buession-security-mcrypt 参考手册", + "content": "数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。", + "url": "/manual/2.0/mcrypt/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.0/mcrypt/index.html#安装", + "content": "安装 com.buession.security\n buession-security-mcrypt\n x.x.x\n\n随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。encode:加密,对任意对象进行加密,如果参数为 char[]、byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密\ndecode:解密,对 CharSequence 进行解密\nimport com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt();\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();\nmcrypt.encode(new Integer(100));\n您可以指定加密 key。import com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(new Integer(100));\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.0/mcrypt/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。", + "url": "/manual/2.0/pac4j/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.0/pac4j/index.html#安装", + "content": "安装 com.buession.security\n buession-security-pac4j\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.0/pac4j/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-shiro 参考手册", + "content": "apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。", + "url": "/manual/2.0/shiro/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.0/shiro/index.html#安装", + "content": "安装 com.buession.security\n buession-security-shiro\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.0/shiro/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-spring 参考手册", + "content": "集成 spring security 框架。", + "url": "/manual/2.0/spring/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.0/spring/index.html#安装", + "content": "安装 com.buession.security\n buession-security-spring\n x.x.x\n\n该模块无功能,仅仅整合把 spring security 的依赖整合进来了。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.0/spring/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-web 参考手册", + "content": "web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。", + "url": "/manual/2.0/web/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.0/web/index.html#安装", + "content": "安装 com.buession.security\n buession-security-web\n x.x.x\n\n您可以通过 ServletWebSecurityConfigurerAdapterConfiguration、ReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.0/web/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "API 参考手册", + "content": "Buession Security API 包含以下目录:\n\n模块\n使用帮助\n手册\n\n\n\n\nbuession-security-core\n使用帮助\nAPI 手册\n\n\nbuession-security-captcha\n使用帮助\nAPI 手册\n\n\nbuession-security-mcrypt\n使用帮助\nAPI 手册\n\n\nbuession-security-pac4j\n使用帮助\nAPI 手册\n\n\nbuession-security-shiro\n使用帮助\nAPI 手册\n\n\nbuession-security-spring\n使用帮助\nAPI 手册\n\n\nbuession-security-web\n使用帮助\nAPI 手册\n\n\n", + "url": "/manual/2.1/index.html", + "children": [] + }, + { + "title": "buession-security-core 参考手册", + "content": "该类库为核心包,目前仅实现了 SameSite 枚举的定义和数据脱敏工具 Desensitization。", + "url": "/manual/2.1/core/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.1/core/index.html#安装", + "content": "安装 com.buession.security\n buession-security-core\n x.x.x\n\n数据脱敏:import com.buession.security.core.Desensitization;\nString str = Desensitization.encode(\"13800138000\", 3); // 1380***8000\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.1/core/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "随着互联网的发展,对应用的安全要求越来越高,在安全的前提下,也需要更加注重用户体验。行为式验证码的诞生,避免了用户去读懂扭曲的图片文字,且行为式验证码背景图片采用多种图像加密技术,采用多种字体,且添加了很多随机效果,能有效防止 OCR 文字识别和暴力破解。buession-security-captcha 目前集成了极验行为验证第三代和第四代、阿里云验证码、腾讯云验证码,屏蔽了各行为验证厂商的调用细节。后续会根据实际情况,接入更多厂商的行为验证码,欢迎各位大神可以提供其它厂商的 key 用于开发测试。", + "url": "/manual/2.1/captcha/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.1/captcha/index.html#安装", + "content": "安装 com.buession.security\n buession-security-captcha\n x.x.x\n\n我们通过实现接口 com.buession.security.captcha.core.RequestData 定义不同厂商行为验证码需要的请求参数。AliYunRequestData:阿里云验证码请求数据\nGeetestV3RequestData:极验第三代行为验证码请求数据\nGeetestV4RequestData:极验第四代行为验证码请求数据\nTencentRequestData:腾讯云验证码请求数据\n" + }, + { + "title": "阿里云", + "url": "/manual/2.1/captcha/index.html#阿里云", + "content": "阿里云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.aliyun.AliYunCaptchaClient;\nimport com.buession.security.captcha.aliyun.AliYunRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new AliYunCaptchaClient(\"Your accessKeyId\", \"Your accessKeySecret\", \"Your appKey\", httpClient);\n\nRequestData request = new AliYunRequestData();\nrequest.setToken(\"token\");\nrequest.setSig(\"sig\");\nrequest.setSessionId(\"session id\");\nrequest.setScene(\"ecene\");\nrequest.setRemoteIp(\"User client ip\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "极验", + "url": "/manual/2.1/captcha/index.html#极验", + "content": "极验import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.geetest.GeetestCaptchaClient;\nimport com.buession.security.captcha.geetest.api.v4.GeetestV4RequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new GeetestCaptchaClient(\"Your appId\", \"Your secretKey\", \"version\", httpClient);\n\nRequestData request = new GeetestV4RequestData();\nrequest.setLotNumber(\"lot number\");\nrequest.setCaptchaOutput(\"captcha Output\");\nrequest.setPassToken(\"pass token\");\nrequest.setGenTime(\"gen time\");\ncaptchaClient.validate(request);\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.1/captcha/index.html#腾讯云", + "content": "腾讯云import com.buession.security.captcha.CaptchaClient;import com.buession.security.captcha.tencent.TencentCaptchaClient;\nimport com.buession.security.captcha.tencent.TencentRequestData;\nimport com.buession.security.captcha.core.RequestData;\nimport com.buession.httpclient.HttpClient;\n\nHttpClient httpClient;\nCaptchaClient captchaClient = new TencentCaptchaClient(\"Your secretId\", \"Your secretKey\", httpClient);\n\nRequestData request = new TencentRequestData();\nrequest.setRandstr(\"rand str\");\nrequest.setTicket(\"ticket\");\nrequest.setUserIp(\"User client ip\");\ncaptchaClient.validate(request);\n当然,在您的应用中您可不必这么麻烦的使用,我们已经为您封装好了前端提交参数到 RequestData 的转换,您可不必这么麻烦的一个一个的去设置参数值。在您的 controller 中您可以这么用。import com.buession.lang.Status;import com.buession.web.mvc.Response;\nimport com.buession.security.captcha.CaptchaClient;\nimport com.buession.security.captcha.aliyun.AliyunParameter;\nimport com.buession.security.captcha.validator.servlet.ServletAliYunCaptchaValidator;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(path = \"/captcha\")\npublic class CaptchamentController {\n\n @Autowired\n private CaptchaClient captchaClient;\n\n @RequestMapping(path = \"/validate\", method = RequestMethod.GET)\n public Status validate(HttpServletRequest request){\n ServletAliYunCaptchaValidator captchaValidator = new ServletAliYunCaptchaValidator(captchaClient, new AliyunParameter());\n return captchaValidator.validate(request);\n }\n\n}\n以上是基于 servlet 的一个简单实例,buession-security-captcha 基于上述模式也可以用于 webflux 环境。CaptchaValidator 的每个最终实现,均通过构造函数设置 com.buession.security.captcha.CaptchaClient 和 com.buession.security.captcha.core.Parameter。通过 com.buession.security.captcha.core.Parameter 的实现配置,用户提交的参数名称,也就是说,您可以自定义行为验证码前端提交到后端的参数名称,每一个 com.buession.security.captcha.core.Parameter 均设置了默认值。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.1/captcha/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-captcha 参考手册", + "content": "本文档用于说明 com.buession.security.captcha.core.Parameter 和官方参数的对应关系。", + "url": "/manual/2.1/captcha/parameter.html", + "children": [ + { + "title": "阿里云", + "url": "/manual/2.1/captcha/parameter.html#阿里云", + "content": "阿里云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nsessionId\nSessionId\nsessionId\n会话 ID\n\n\nsig\nSig\nsig\n签名串\n\n\ntoken\nToken\ntoken\n请求唯一标识\n\n\nscene\nScene\nscene\n场景标识\n\n\n" + }, + { + "title": "极验", + "url": "/manual/2.1/captcha/parameter.html#极验", + "content": "极验第三代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nchallenge\nchallenge\nchallenge\n流水号\n\n\nseccode\nseccode\nseccode\n核心校验数据\n\n\nvalidate\nvalidate\nvalidate\n核心校验数据\n\n\nuserId\nuser_id\nuser_id\nuser_id作为终端用户的唯一标识,确定用户的唯一性\n\n\nclientType\nclient_type\nclient_type\n客户端类型\n\n\n第四代\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nlotNumber\nlot_number\nlot_number\n验证流水号\n\n\ncaptchaOutput\ncaptcha_output\ncaptcha_output\n验证输出信息\n\n\npassToken\npass_token\npass_token\n验证通过标识\n\n\ngenTime\ngen_time\ngen_time\n验证通过时间戳\n\n\n" + }, + { + "title": "腾讯云", + "url": "/manual/2.1/captcha/parameter.html#腾讯云", + "content": "腾讯云\n\n参数名\n官方参数名\n默认值\n说明\n\n\n\n\nrandStr\nRandstr\nRandstr\n客户端验证回调的随机串\n\n\nticket\nTicket\nTicket\n票据\n\n\n" + } + ] + }, + { + "title": "buession-security-mcrypt 参考手册", + "content": "数据加密、解密类库,支持:MD5、SHA1、SHA256、SHA512、BASE64 以及 Discuz 加密算法等等。", + "url": "/manual/2.1/mcrypt/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.1/mcrypt/index.html#安装", + "content": "安装 com.buession.security\n buession-security-mcrypt\n x.x.x\n\n随着互联网的发展,对应用的安全要求越来越高,需要通过各种不同的加密算法,对铭感数据加密,包括可逆的(如:手机号码、身份证号码)和不可逆的(如:密码)。buession-security-mcrypt 基于此背景封装了大量的加解密、散列/哈希等算法,尚未囊括市面上主流的加密算法,会在后续的版本中继续添加。encode:加密,对任意对象进行加密,如果参数为 char[]、byte[] 时,将会 new String 创建一个 String 对象,其它对象会调用 toString() 方法转换为字符串后,再进行加密\ndecode:解密,对 CharSequence 进行解密\nimport com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt();\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt();\nmcrypt.encode(new Integer(100));\n您可以指定加密 key。import com.buession.security.mcrypt.Sha1Mcrypt;\nSha1Mcrypt mcrypt = new Sha1Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(\"Abc\");\nimport com.buession.security.mcrypt.HmacSha512Mcrypt;\nHmacSha512Mcrypt mcrypt = new HmacSha512Mcrypt(\"UTF-8\", \"key\");\nmcrypt.encode(new Integer(100));\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.1/mcrypt/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "对 pac4j 二次封装,集成了 pac4j 和 buji-pac4j。", + "url": "/manual/2.1/pac4j/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.1/pac4j/index.html#安装", + "content": "安装 com.buession.security\n buession-security-pac4j\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.1/pac4j/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "", + "url": "/manual/2.1/pac4j/annotation.html", + "children": [ + { + "title": "注解", + "url": "/manual/2.1/pac4j/annotation.html#注解", + "content": "注解我们通过注解的形式封装了获取当前登录用户信息的 API。" + }, + { + "title": "注解", + "url": "/manual/2.1/pac4j/annotation.html#注解-注解", + "content": "注解\n\n注解\n作用域\n说明\n\n\n\n\n@Principal\n方法参数\n获取当前登录用户信息,并可以以任何实体类、Map 对象的形式返回\n\n\n获取当前登录用户@Controller@RequestMapping(path = \"/test\")\npublic class TestController {\n\n @RequestMapping(path = \"/principal1\")\n @ResponseBody\n public User principal1(@Principal User user, ServerHttpResponse response){\n return user;\n }\n\n @RequestMapping(path = \"/principal2\")\n @ResponseBody\n public Map principal2(@Principal Map user, ServerHttpResponse response){\n return user;\n }\n\n}\n" + } + ] + }, + { + "title": "buession-security-pac4j 参考手册", + "content": "", + "url": "/manual/2.1/pac4j/ajaxrequestresolver.html", + "children": [ + { + "title": "AjaxRequestResolver", + "url": "/manual/2.1/pac4j/ajaxrequestresolver.html#ajaxrequestresolver", + "content": "AjaxRequestResolverpac4j 原生的 AJAX 请求解析器 AjaxRequestResolver 的实现 DefaultAjaxRequestResolver,以 XML 的形式响应 pac4j 重定向。\n \n\n此种,场景增加了响应数据的大小和前端 Ajax 解析的成本和难度。为此,我们扩展了 JsonAjaxRequestResolver 和 TextAjaxRequestResolver 以 JSON 和文本的形式响应重定向地址,以简化前端 JavaScript 的解析成本和难度。{\"redirect\": {\"url\": \"redirect_url\"}}redirect_url" + } + ] + }, + { + "title": "buession-security-shiro 参考手册", + "content": "apache shiro 二次封装,增加 redis 对 session 和 cache 的管理。", + "url": "/manual/2.1/shiro/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.1/shiro/index.html#安装", + "content": "安装 com.buession.security\n buession-security-shiro\n x.x.x\n\n" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.1/shiro/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-spring 参考手册", + "content": "集成 spring security 框架。", + "url": "/manual/2.1/spring/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.1/spring/index.html#安装", + "content": "安装 com.buession.security\n buession-security-spring\n x.x.x\n\n该模块无功能,仅仅整合把 spring security 的依赖整合进来了。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.1/spring/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + }, + { + "title": "buession-security-web 参考手册", + "content": "web 安全相关的功能封装,支持 servlet 和 reactive,增加 XSS 过滤器。", + "url": "/manual/2.1/web/index.html", + "children": [ + { + "title": "安装", + "url": "/manual/2.1/web/index.html#安装", + "content": "安装 com.buession.security\n buession-security-web\n x.x.x\n\n您可以通过 ServletWebSecurityConfigurerAdapterConfiguration、ReactiveWebSecurityConfigurerAdapterConfiguration 来控制 HTTP 安全相关响应头,是 spring security 默认参配置的修改。" + }, + { + "title": "API 参考手册>>", + "url": "/manual/2.1/web/index.html#api-参考手册>>", + "content": "API 参考手册>>" + } + ] + } + ] +} \ No newline at end of file diff --git a/support.html b/support.html new file mode 100644 index 0000000..9cddd3b --- /dev/null +++ b/support.html @@ -0,0 +1,16 @@ +技术支持
\ No newline at end of file diff --git a/ydoc/images/android-chrome-192x192.png b/ydoc/images/android-chrome-192x192.png new file mode 100644 index 0000000..aede5e3 Binary files /dev/null and b/ydoc/images/android-chrome-192x192.png differ diff --git a/ydoc/images/android-chrome-512x512.png b/ydoc/images/android-chrome-512x512.png new file mode 100644 index 0000000..3f028af Binary files /dev/null and b/ydoc/images/android-chrome-512x512.png differ diff --git a/ydoc/images/apple-touch-icon.png b/ydoc/images/apple-touch-icon.png new file mode 100644 index 0000000..25d12f8 Binary files /dev/null and b/ydoc/images/apple-touch-icon.png differ diff --git a/ydoc/images/browserconfig.xml b/ydoc/images/browserconfig.xml new file mode 100644 index 0000000..b3930d0 --- /dev/null +++ b/ydoc/images/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/ydoc/images/dog@1x.png b/ydoc/images/dog@1x.png new file mode 100644 index 0000000..0eb0f99 Binary files /dev/null and b/ydoc/images/dog@1x.png differ diff --git a/ydoc/images/dog@2x.png b/ydoc/images/dog@2x.png new file mode 100644 index 0000000..7026917 Binary files /dev/null and b/ydoc/images/dog@2x.png differ diff --git a/ydoc/images/dogbg@1x.png b/ydoc/images/dogbg@1x.png new file mode 100644 index 0000000..6e6761e Binary files /dev/null and b/ydoc/images/dogbg@1x.png differ diff --git a/ydoc/images/dogbg@2x.png b/ydoc/images/dogbg@2x.png new file mode 100644 index 0000000..7c7a0d4 Binary files /dev/null and b/ydoc/images/dogbg@2x.png differ diff --git a/ydoc/images/favicon-16x16.png b/ydoc/images/favicon-16x16.png new file mode 100644 index 0000000..9b9e70a Binary files /dev/null and b/ydoc/images/favicon-16x16.png differ diff --git a/ydoc/images/favicon-32x32.png b/ydoc/images/favicon-32x32.png new file mode 100644 index 0000000..5dec632 Binary files /dev/null and b/ydoc/images/favicon-32x32.png differ diff --git a/ydoc/images/favicon.ico b/ydoc/images/favicon.ico new file mode 100644 index 0000000..f7f45db Binary files /dev/null and b/ydoc/images/favicon.ico differ diff --git a/ydoc/images/logo.png b/ydoc/images/logo.png new file mode 100644 index 0000000..caca95f Binary files /dev/null and b/ydoc/images/logo.png differ diff --git a/ydoc/images/manifest.json b/ydoc/images/manifest.json new file mode 100644 index 0000000..b20abb7 --- /dev/null +++ b/ydoc/images/manifest.json @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/ydoc/images/mstile-150x150.png b/ydoc/images/mstile-150x150.png new file mode 100644 index 0000000..87b9960 Binary files /dev/null and b/ydoc/images/mstile-150x150.png differ diff --git a/ydoc/images/safari-pinned-tab.svg b/ydoc/images/safari-pinned-tab.svg new file mode 100644 index 0000000..fed47b7 --- /dev/null +++ b/ydoc/images/safari-pinned-tab.svg @@ -0,0 +1,70 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + diff --git a/ydoc/scripts/app.js b/ydoc/scripts/app.js new file mode 100644 index 0000000..e1e4fa1 --- /dev/null +++ b/ydoc/scripts/app.js @@ -0,0 +1,87 @@ +var $panel = document.getElementById('js-panel'), + $header = document.getElementById('js-header'), + $content = document.getElementById('js-content'), + $navIcon = document.getElementById('js-nav-btn'), + $summaryItems = Array.prototype.slice.call(document.querySelectorAll('#js-menu .href')), + $menu = document.getElementById('js-menu'), + $menuContent = document.getElementById('js-menu-content'), + $menuBar = document.getElementById('js-summary-switch'), + navigation; + +var utils = { + debounce: function(func, wait) { + var timeout; + return function () { + clearTimeout(timeout); + timeout = setTimeout(func, wait); + }; + } +}; + +// Add 'active' to summary item +function itemAddActive() { + var locationHref = window.location.href; + $summaryItems.map(function (item, index) { + if (item.href === locationHref) { + // add 'active' for present summary item. + item.parentElement.classList.add('active'); + } else { + item.parentElement.classList.remove('active'); + } + }); +} + +// Add EventListener +function addEvents() { + $panel.addEventListener('click', function (e) { + itemAddActive(); + if (e.target.scrollTop > 0) { + $header.classList.add('moved'); + } else { + $header.classList.remove('moved'); + } + }); + if ($menuContent) { + $menuContent.addEventListener('click', function (e) { + $menu.classList.remove('active'); + setTimeout(itemAddActive, 0); + }); + } + if ($menuBar) { + $menuBar.addEventListener('click', function () { + $menu.classList.toggle('active'); + // 侧栏菜单点击时收起 nav 导航 + if ($navIcon.classList.value.indexOf('active') !== -1) { + navigation.toggle(); + } + }); + } + if ($menu) { + $menu.addEventListener('scroll', function(e) { + sessionStorage.setItem('menuScrollTop', e.target.scrollTop); + }); + } + if ($content) { + $content.addEventListener('scroll', function (e) { + sessionStorage.setItem('contentScrollTop', e.target.scrollTop); + }); + } +} + +// initial components +function initComponents() { + // nav + navigation = responsiveNav('.js-nav', { + customToggle: '#js-nav-btn', + open: function() { + if ($menu) $menu.classList.remove('active'); + setTimeout(itemAddActive, 0); + } + }); +} + + +initComponents(); +addEvents(); +itemAddActive(); + diff --git a/ydoc/scripts/plugins/dollar.min.js b/ydoc/scripts/plugins/dollar.min.js new file mode 100644 index 0000000..9edc48c --- /dev/null +++ b/ydoc/scripts/plugins/dollar.min.js @@ -0,0 +1,6 @@ +/*! + * DollarJS 2.0.0 -- a light, fast, modular, jQuery replacement + * Github: https://github.com/seebigs/dollar-js + * Released under the MIT license: https://opensource.org/licenses/MIT + */ +(function () { function t() { var t, n, e, i = M(); return B.each(W.call(arguments), function (r) { if (r) for (t = 0, n = r.length; t < n; t++)e = r[t], -1 === i.indexOf(e) && i.push(e) }), i } function n(t, n) { if (n) { var e = u(n), i = []; return B.each(e, function (n) { B.each(t, function (t) { typeof n.contains === $ && n.contains(t) && i.push(t) }) }), i } return t } function e(t, e) { function r() { var t = R.readyState; return "interactive" === t || "complete" === t } if (typeof t === H) return i(t, e); if (t.isDollar) return n(t.get(), e); if (t.nodeType) return n([t], e); if (t === t.window) return [t]; if (t.length) { for (var o, f = [], s = 0, u = t.length; s < u; s++)o = t[s], B.isElement(o) && f.push(o); return n(f, e) } return typeof t === $ && (r() ? t() : I.addEventListener ? R.addEventListener("DOMContentLoaded", t, !1) : R.attachEvent("onreadystatechange", function () { r() && t() })), [] } function i(t, n) { if (n) { var e = []; if (n = u(n), n.length > 1) { for (var r = 0, o = n.length; r < o; r++)B.merge(e, i(t, n[r])); return e } n = n[0] } else n = U; if (!n) return []; var f = /^\s*(?:#([\w-]+)|(\w+)|\.([\w-]+)|(<[\w\W]+>)[^>]*)\s*$/.exec(t); if (f) { if (t = f[1]) { var a = R.getElementById(t); return a && n !== a && n.contains(a) ? [a] : [] } if (t = f[2]) return n.getElementsByTagName(t); if (t = f[3]) return n.getElementsByClassName(t); if (t = f[4]) return [s(t)] } else { var l = /(.*)\:(.+)/.exec(t); if (l) { var c = l[1] || "*", h = l[2].split("("), p = _[h[0]]; if (p) return p(c, n, h) } } return W.call(n.querySelectorAll(t)) } function r(t) { var n = i(t); return -1 !== P.indexOf.call(n, this) } function o(t, n, e) { return !(!n || !t || 1 !== t.nodeType) && (typeof n !== H ? n.nodeType ? t === n : typeof n === $ ? !!n.call(t, t, e) : !!n.length && -1 !== n.indexOf(t) : G.call(t, n)) } function f(t, n) { for (var e = [], i = 0, r = t.length; i < r; i++)o(t[i], n, i) && e.push(t[i]); return e } function s(t) { var n = /^<(\w+)\s*\/?>(?:<\/\1>|)$/.exec(t); if (n) return R.createElement(n[1]); var e = R.createElement("div"); return e.innerHTML = t, e.childNodes[0] } function u(t) { return typeof t === H ? i(t) : t.isDollar ? t.get() : 1 === t.nodeType || 9 === t.nodeType ? [t] : Array.isArray(t) ? t : [U] } function a(t) { var n = t && t.nodeType; return n && 3 !== n && 8 !== n && 2 !== n } function l(t) { return 9 === t.nodeType && (t = t.documentElement), a(t) ? t : q } function c(t, n) { if (t) return t === t.window ? t[n] : (t = l(t), t && t.hasAttribute(n) ? t.getAttribute(n) : q) } function h(t, n, e) { return t === t.window && (t[n] = e), (t = l(t)) && t.setAttribute(n, e) } function p(t, n) { return t === t.window && (t[n] = q), (t = l(t)) && t.removeAttribute(n) } function d(t) { return Number(c(t, J)) || q } function v(t, n) { return h(t, J, n) } function y(t, n, e) { var i = d(n); if (i) return e ? t[i] && t[i][e] : t[i] } function m(t, n, e, i) { var r = d(n); r || (r = K, v(n, r), K++), t[r] || (t[r] = {}), t[r][e] = i } function g(t, n, e, i) { var r = y(t, n, e) || []; r.push(i), m(t, n, e, r) } function b(t, n, e) { var i = n + " " + C(e), r = t.style.transition ? t.style.transition.split(/,\s?/) : [], o = !1; return r.forEach(function (t, e) { 0 === t.indexOf(n + " ") && (r[e] = i, o = !0) }), o || r.push(i), r.join(", ") } function E(t, n) { var e = t.style.transition.split(/,\s?/), i = []; return e.forEach(function (t) { 0 !== t.indexOf(n + " ") && i.push(t) }), i.join(", ") } function C(t) { var n = []; return n.push(typeof t.duration === H ? t.duration : (parseInt(t.duration) || 400) + "ms"), n.push(t.easing || "ease"), n.push(typeof t.delay === H ? t.delay : (parseInt(t.delay) || 0) + "ms"), n.join(" ") } function T(t, n) { function e(t) { u.appendChild(t), o = !0 } t = [].concat.apply([], t); var i, r, o, f, u, a, l = t.length, c = this.length; for (r = 0; r < c; r++) { for (o = !1, u = R.createDocumentFragment(), i = 0; i < l; i++)(f = t[i]) && (typeof f === H ? (a = s(f)) && e(a) : 1 === f.nodeType ? e(f) : f.isDollar ? f.each(e) : typeof f === $ && (a = f(this[r], r), typeof a === H && (a = s(a)), a && e(a))); o && n(this[r], u) } return this } function w(t) { return t && t.dataset || function () { var n, e = {}, i = t.attributes, r = /^data-[a-z_\-\d]*$/i; for (var o in i) i.hasOwnProperty(o) && (n = i[o].name, r.test(n) && (n = B.format.dashToCamel(n.substr(5)), e[n] = i[o].value)); return e }() } function N(t, n) { return a(t) ? t[n] : q } function x(t, n) { return t && typeof n === H ? t.ownerDocument.defaultView.opener ? t.ownerDocument.defaultView.getComputedStyle(t, null)[n] || "" : k.getComputedStyle(t, null)[n] || t.style[n] || "" : "" } function O(t, n) { var e; return n = typeof n === H ? n : "", t ? (e = "float" === n ? "styleFloat" : B.format.dashToCamel(n.replace(/^-ms-/, "ms-")), t.currentStyle[e]) : "" } function S(t) { var n = t.style.display; if (n && "none" !== n || (n = y(X, t, "nonHiddenDisplayValue") || ""), !n && t.parentNode) { var e = R.createElement(t.nodeName); t.parentNode.appendChild(e), n = Y(e, "display"), t.parentNode.removeChild(e), m(X, t, "nonHiddenDisplayValue", n) } return n } function A(t, n) { if (typeof t !== H || typeof n !== $) return this; t = t.split(" "); var e, i, r; return this.each(function () { for (e = this.addEventListener || this.attachEvent, i = 0, r = t.length; i < r; i++)e.call(this, t[i], n, !1), g(X, this, tt, n) }), this } function D(t, n) { if (typeof t !== H) return this; t = t.split(" "); var e, i, r, o, f; return this.each(function () { for (e = 0, i = t.length; e < i; e++)for (r = typeof n === $ ? [n] : y(X, this, tt) || [], o = 0, f = r.length; o < f; o++)this.removeEventListener ? this.removeEventListener(t[e], r[o], !1) : this.detachEvent(t[e], r[o], !1) }), this } function j(t, n, e) { var i, r = { bubbles: !0, cancelable: !0 }; e && e.length && (r.detail = e), B.each(n, function (n) { B.each(t, function (t) { "click" === n ? t.click() : "focus" === n ? t.focus() : "blur" === n ? t.blur() : (i = new k.CustomEvent(n, r), t.dispatchEvent(i)) }) }) } function L(t, n) { return typeof n === $ ? A.call(this, t, n) : (j(this, t.split(" ")), this) } var q, B, M = function (t, n) { return new M.fn.init(t, n) }, H = "string", $ = "function", k = window, I = k.Element.prototype, z = Object.prototype, V = z.toString, F = z.hasOwnProperty, P = Array.prototype, W = P.slice, R = document, U = document.documentElement, Z = /[\s\t\r\n\f]+/g; M.isDollar = !0, M.fn = { isDollar: !0, indexOf: P.indexOf, push: P.push, pop: P.pop, shift: P.shift, unshift: P.unshift, slice: P.slice, splice: P.splice }, M.fn.init = function (t, n) { if (this.length = 0, !t) return this; if (!n) { if (/^#[\w-]+$/.test(t)) { var i = R.getElementById(t.substr(1)); return i && (this[0] = i, this.length = 1), this } if (/^[a-z]+$/.test(t)) { for (var r = R.getElementsByTagName(t), o = r.length, f = 0; f < o; f++)this[f] = r[f]; return this.length = o, this } } return B.merge(this, e(t, n)) }, M.fn.init.prototype = M.fn; var _ = { contains: function (t, n, e) { var r = e[1] && e[1].replace(/[\"\'\)]/g, ""); return r ? f(i(t, n), function (t) { return -1 !== (t.textContent || t.innerText).indexOf(r) }) : [] }, hidden: function (t, n) { return f(i(t, n), function (t) { return 1 === t.nodeType && !(t.offsetWidth || t.offsetHeight || t.getClientRects().length) }) }, visible: function (t, n) { return f(i(t, n), function (t) { return 1 === t.nodeType && !!(t.offsetWidth || t.offsetHeight || t.getClientRects().length) }) }, even: function (t, n) { return W.call(n.querySelectorAll(t + ":nth-child(even)")) }, odd: function (t, n) { return W.call(n.querySelectorAll(t + ":nth-child(odd)")) }, has: function (t, n, i) { var r = typeof i[1] === H && i[1].replace(")", ""); return r ? f(e(t, n), function (t) { return 1 === t.nodeType && !!e(r, t).length }) : [] }, not: function (t, n, e) { n === U && (n = R); var r = t; "*" !== t && " " === t[t.length - 1] && (r = t + "*"); var o = e[1].split(")"), f = o[1], s = (o[0] || "").split(","); f && (r += f); var u = [], a = i(r, n); return B.each(a, function (t) { var n = !0; B.each(s, function (e) { if (G.call(t, e)) return n = !1, !1 }), n && u.push(t) }), u } }, G = I.matches || I.webkitMatchesSelector || I.mozMatchesSelector || I.msMatchesSelector || I.oMatchesSelector || r, J = "dollar-node-id", K = 1, Q = {}, X = {}; M.utils = B = function () { function t(t) { return !!t && (1 === t.nodeType || 9 === t.nodeType) } function n(t) { return V.call(t) === o + "Object]" } function e(t, n) { if (t) if (t.length !== q) { for (var e = 0, i = t.length; e < i; e++)if (!1 === n.call(t[e], t[e], e, t)) return } else for (var r in t) if (F.call(t, r) && !1 === n.call(t[r], t[r], r, t)) return } function i() { var t = arguments[0], n = function (n, e) { n !== q && (t[e] = n) }; return e(W.call(arguments, 1), function (t) { e(t, n) }), t } function r() { var t, n, i = arguments[0]; return e(W.call(arguments, 1), function (e) { if (e) for (t = 0, n = e.length; t < n; t++)-1 === i.indexOf(e[t]) && i.push(e[t]) }), i } var o = "[object "; return { isElement: t, isObject: n, each: e, extend: i, merge: r, format: { camelToDash: function (t) { return t.replace(/([A-Z])/g, "-$1").toLowerCase() }, dashToCamel: function (t) { return t.replace(/\-(.)/g, function (t, n) { return n.charAt(0).toUpperCase() }) } } } }(), M.fn.closest = function (n, i) { if (!n) return M(); for (var r, o = e(n, i), f = [], s = 0, u = this.length; s < u; s++)for (r = this[s]; r && r !== i;) { if (-1 !== P.indexOf.call(o, r)) { f.push(r); break } r = r.parentNode } return t(f) }, M.fn.each = M.fn.forEach = function (t) { return B.each(this, t), this }, M.fn.eq = function (t) { return t = Array.isArray(t) ? NaN : parseInt(t, 10), M(t >= 0 ? this[t] : this[this.length + t]) }, M.fn.filter = function (n) { return this.length && n ? t(f(this, n)) : M() }, M.fn.find = function (n) { return n && this.length ? t(e(n, this)) : M() }, M.fn.get = function (t) { return t === q ? W.call(this, 0) : t < 0 ? this[t + this.length] : this[t] }, M.fn.reverse = P.reverse, M.fn.animate = function (t, n, e) { B.isObject(n) || (n = { duration: n }); var i = "transitionend"; return this.each(function (r, o) { B.each(t, function (t, f) { r.style.transition = b(r, f, n), r.style[f] = t; var s = function (t) { r.removeEventListener(i, s, !0), r.style.transition = E(r, t), typeof e === $ && e.call(r, r, o) }; r.addEventListener(i, s, !0) }) }), this }, M.fn.fadeIn = function (t, n) { return this.animate({ opacity: 1 }, t, n) }, M.fn.fadeOut = function (t, n) { return this.animate({ opacity: 0 }, t, n) }, M.fn.add = function (n, e) { return n ? t(this, M(n, e)) : this }, M.fn.concat = function () { var n = W.call(arguments); return n.unshift(this), t.apply(this, n) }, M.fn.has = function (t) { return t ? this.filter(function () { return !!e(t, this).length }) : M() }, M.fn.is = function (t) { return !(!t || !this.filter(t).length) }, M.fn.map = function (n) { if (typeof n !== $) return this; for (var e, i = [], r = 0, o = this.length; r < o; r++) { if (e = n.call(this[r], this[r], r, this), !B.isElement(e)) throw new Error(".map fn should return an Element, not " + typeof e); i.push(e) } return t.call(this, i) }, M.fn.not = function (n) { if (!n) return this; var e; return e = typeof n === $ ? function (t, e) { return !n.call(t, e, t) } : function (t, e) { return !o(t, n, e) }, t(this.filter(e)) }, M.fn.after = function () { return T.call(this, arguments, function (t, n) { var e = t.parentNode; e && (t.nextSibling ? e.insertBefore(n, t.nextSibling) : e.appendChild(n)) }) }, M.fn.append = function () { return T.call(this, arguments, function (t, n) { t.appendChild(n) }) }, M.fn.before = function () { return T.call(this, arguments, function (t, n) { t.parentNode && t.parentNode.insertBefore(n, t) }) }, M.fn.clone = function () { var t, n, e = this.length; for (n = 0; n < e; n++)t = this[n], 1 !== t.nodeType && 11 !== t.nodeType || (this[n] = t.cloneNode(!0)); return this }, M.fn.empty = function () { var t, n, e = this.length; for (n = 0; n < e; n++)t = this[n], 1 === t.nodeType && (t.textContent = ""); return this }, M.fn.html = function (t) { var n, e, i = this.length, r = this[0]; if (t === q) return r && 1 === r.nodeType ? r.innerHTML : q; try { for (e = 0; e < i; e++)n = this[e], 1 === n.nodeType && (n.innerHTML = t) } catch (n) { this.empty().append(t) } return this }, M.fn.prepend = function () { return T.call(this, arguments, function (t, n) { t.firstChild ? t.insertBefore(n, t.firstChild) : t.appendChild(n) }) }, M.fn.remove = function (t) { var n, e, i = this.length; if (t === q) for (e = 0; e < i; e++)n = this[e], n.parentNode && n.parentNode.removeChild(n); else for (e = 0; e < i; e++)n = this[e], o(n, t, e) && n.parentNode && n.parentNode.removeChild(n); return this }, M.fn.attr = function (t, n) { return n === q ? c(this[0], t) : (this.each(function (e, i) { a(e) && (typeof n === $ && (n = n(c(e, t), i)), e.setAttribute(t, n)) }), this) }, M.fn.data = function (t, n) { function e(t, n) { m(Q, i, n, t) } if (!this.length) return q; var i, r = {}; if (!t) return B.extend({}, w(this[0]), y(Q, this[0])); if (typeof t === H) { if (n === q) { var o = y(Q, this[0], t); return o === q ? w(this[0])[t] : o } r[t] = n } else B.isObject(t) && (r = t); for (var f = 0, s = this.length; f < s; f++)i = this[f], B.each(r, e); return this }, M.fn.prop = function (t, n) { return n === q ? N(this[0], t) : (this.each(function (e, i) { a(e) && (typeof n === $ && (n = n(N(e, t), i)), e[t] = n) }), this) }, M.fn.removeAttr = function (t) { return this.each(function () { this.removeAttribute(t) }), this }, M.fn.removeData = function (t) { for (var n, e, i = 0, r = this.length; i < r; i++)n = this[i], e = d(n), t ? (e && delete Q[e][t], n && (n.dataset ? n.dataset[t] && delete n.dataset[t] : p(n, "data-" + B.format.camelToDash(t)))) : Q[e] = {}; return this }, M.fn.removeProp = function (t) { return this.each(function () { a(this) && delete this[t] }), this }, M.fn.text = function (t) { if (t !== q) return this.each(function (n, e) { 1 !== n.nodeType && 11 !== n.nodeType && 9 !== n.nodeType || (n.textContent = typeof t === $ ? t(n.textContent, e) : t) }), this; var n = ""; return this.each(function (t) { var e = t.nodeType; 1 === e || 9 === e || 11 === e ? typeof t.textContent === H && (n += t.textContent) : 3 !== e && 4 !== e || (n += t.nodeValue) }), n }, M.fn.val = function (t) { if (t === q) return this[0] ? this[0].value : q; for (var n = 0; n < this.length && 1 === this[n].nodeType; n++)typeof t === $ && (t = t.call(this[n], this[n].value, n)), this[n].value = t; return this }; var Y = k.getComputedStyle !== q ? x : O; M.fn.addClass = function (t) { if (!t) return this; var n, e, i, r = this.length; if (typeof t === H) for (n = t.trim().split(" "), i = 0; i < r; i++)e = this[i].className.trim().replace(Z, " ").split(" "), this[i].className = B.merge([], e, n).join(" "); else if (typeof t === $) for (i = 0; i < r; i++)n = t.call(this[i], this[i].className, i).split(" "), e = this[i].className.trim().replace(Z, " ").split(" "), this[i].className = B.merge([], e, n).join(" "); return this }, M.fn.css = function (t, n) { function e(t, n) { r.style[n] = typeof t === $ ? t.call(r, Y(r, n), i) : t } if (!t) return this; var i, r, o = this.length, f = {}; if (typeof t === H) { if (n === q) return Y(this[0], t); f[t] = n } else B.isObject(t) && (f = t); for (i = 0; i < o; i++)r = this[i], B.each(f, e); return this }, M.fn.hasClass = function (t) { if (!t) return !1; t = " " + t.trim() + " "; for (var n = 0, e = this.length; n < e; n++)if (1 === this[n].nodeType && -1 !== (" " + this[n].className + " ").replace(Z, " ").indexOf(t)) return !0; return !1 }, M.fn.height = function () { return parseFloat(this.eq(0).css("height")) || 0 }, M.fn.hide = function () { return this.each(function () { this.style.display = "none" }), this }, M.fn.removeClass = function (t) { function n(t) { -1 === o.indexOf(t) && i.push(t) } for (var e, i, r, o, f = 0, s = this.length; f < s; f++)e = this[f], i = [], t ? (o = typeof t === $ ? t.call(e, e.className, f) : t, o.length && (o = typeof o === H ? o.trim().split(" ") : o, r = e.className.replace(Z, " ").split(" "), B.each(r, n), e.className = i.join(" "))) : e.className = ""; return this }, M.fn.show = function () { return this.each(function () { this.style.display = S(this), this.style.visibility = "visible", this.style.opacity = 1 }), this }, M.fn.width = function () { return parseFloat(this.eq(0).css("width")) || 0 }, M.fn.children = function (n) { for (var e = [], i = 0, r = this.length; i < r; i++)B.merge(e, this[i].children); return t(n ? M.fn.filter.call(e, n) : e) }, M.fn.first = function () { return this.eq(0) }, M.fn.last = function () { return this.eq(-1) }, M.fn.next = function (n) { for (var e, i = [], r = 0, o = this.length; r < o; r++)(e = this[r].nextElementSibling) && i.push(e); return t(n ? M.fn.filter.call(i, n) : i) }, M.fn.parent = function (n) { for (var e, i = [], r = 0, o = this.length; r < o; r++)(e = this[r].parentNode) && i.push(e); return t(n ? M.fn.filter.call(i, n) : i) }, M.fn.prev = function (n) { for (var e, i = [], r = 0, o = this.length; r < o; r++)(e = this[r].previousElementSibling) && i.push(e); return t(n ? M.fn.filter.call(i, n) : i) }, M.fn.siblings = function (n) { for (var e, i = [], r = 0, o = this.length; r < o; r++)for (this[r].parentNode && (e = this[r].parentNode.firstChild); e;)e !== this[r] && 1 === e.nodeType && i.push(e), e = e.nextSibling; return t(n ? M.fn.filter.call(i, n) : i) }; var tt = "activeEventListeners"; M.fn.click = function (t) { return L.call(this, "click", t) }, M.fn.focus = function (t) { return L.call(this, "focus", t) }, M.fn.blur = function (t) { return L.call(this, "blur", t) }, M.fn.change = function (t) { return L.call(this, "change", t) }, M.fn.resize = function (t) { return L.call(this, "resize", t) }, M.fn.off = M.fn.unbind = D, M.fn.on = M.fn.bind = A, M.fn.trigger = function (t) { return typeof t !== H ? this : (t = t.split(" "), j(this, t, W.call(arguments, 1)), this) }, function (t) { function n(t, n) { n = n || { bubbles: !1, cancelable: !1 }; var e = document.createEvent("CustomEvent"); return e.initCustomEvent(t, n.bubbles, n.cancelable, n.detail), e } typeof t.CustomEvent !== $ && (n.prototype = t.Event.prototype, t.CustomEvent = n) }(k), function () { var t = window; "undefined" != typeof module && module.exports ? module.exports = M : typeof t.define === $ && t.define.amd ? t.define(function () { return M }) : t.$ = M }.call(this) }).call(this); \ No newline at end of file diff --git a/ydoc/scripts/plugins/responsive-nav.min.js b/ydoc/scripts/plugins/responsive-nav.min.js new file mode 100644 index 0000000..add03c1 --- /dev/null +++ b/ydoc/scripts/plugins/responsive-nav.min.js @@ -0,0 +1 @@ +!function (a, b, c) { "use strict"; var d = function (d, e) { var f = !!b.getComputedStyle; f || (b.getComputedStyle = function (a) { return this.el = a, this.getPropertyValue = function (b) { var c = /(\-([a-z]){1})/g; return "float" === b && (b = "styleFloat"), c.test(b) && (b = b.replace(c, function () { return arguments[2].toUpperCase() })), a.currentStyle[b] ? a.currentStyle[b] : null }, this }); var g, h, i, j, k, l, m = function (a, b, c, d) { if ("addEventListener" in a) try { a.addEventListener(b, c, d) } catch (e) { if ("object" != typeof c || !c.handleEvent) throw e; a.addEventListener(b, function (a) { c.handleEvent.call(c, a) }, d) } else "attachEvent" in a && ("object" == typeof c && c.handleEvent ? a.attachEvent("on" + b, function () { c.handleEvent.call(c) }) : a.attachEvent("on" + b, c)) }, n = function (a, b, c, d) { if ("removeEventListener" in a) try { a.removeEventListener(b, c, d) } catch (e) { if ("object" != typeof c || !c.handleEvent) throw e; a.removeEventListener(b, function (a) { c.handleEvent.call(c, a) }, d) } else "detachEvent" in a && ("object" == typeof c && c.handleEvent ? a.detachEvent("on" + b, function () { c.handleEvent.call(c) }) : a.detachEvent("on" + b, c)) }, o = function (a) { if (a.children.length < 1) throw new Error("The Nav container has no containing elements"); for (var b = [], c = 0; c < a.children.length; c++)1 === a.children[c].nodeType && b.push(a.children[c]); return b }, p = function (a, b) { for (var c in b) a.setAttribute(c, b[c]) }, q = function (a, b) { 0 !== a.className.indexOf(b) && (a.className += " " + b, a.className = a.className.replace(/(^\s*)|(\s*$)/g, "")) }, r = function (a, b) { var c = new RegExp("(\\s|^)" + b + "(\\s|$)"); a.className = a.className.replace(c, " ").replace(/(^\s*)|(\s*$)/g, "") }, s = function (a, b, c) { for (var d = 0; d < a.length; d++)b.call(c, d, a[d]) }, t = a.createElement("style"), u = a.documentElement, v = function (b, c) { var d; this.options = { animate: !0, transition: 284, label: "Menu", insert: "before", customToggle: "", closeOnNavClick: !1, openPos: "relative", navClass: "js-nav", navActiveClass: "js-nav-active", jsClass: "js", init: function () { }, open: function () { }, close: function () { } }; for (d in c) this.options[d] = c[d]; if (q(u, this.options.jsClass), this.wrapperEl = b.replace("#", ""), a.getElementById(this.wrapperEl)) this.wrapper = a.getElementById(this.wrapperEl); else { if (!a.querySelector(this.wrapperEl)) throw new Error("The nav element you are trying to select doesn't exist"); this.wrapper = a.querySelector(this.wrapperEl) } this.wrapper.inner = o(this.wrapper), h = this.options, g = this.wrapper, this._init(this) }; return v.prototype = { destroy: function () { this._removeStyles(), r(g, "closed"), r(g, "opened"), r(g, h.navClass), r(g, h.navClass + "-" + this.index), r(u, h.navActiveClass), g.removeAttribute("style"), g.removeAttribute("aria-hidden"), n(b, "resize", this, !1), n(b, "focus", this, !1), n(a.body, "touchmove", this, !1), n(i, "touchstart", this, !1), n(i, "touchend", this, !1), n(i, "mouseup", this, !1), n(i, "keyup", this, !1), n(i, "click", this, !1), h.customToggle ? i.removeAttribute("aria-hidden") : i.parentNode.removeChild(i) }, toggle: function () { j === !0 && (l ? this.close() : this.open()) }, open: function () { l || (r(g, "closed"), q(g, "opened"), q(u, h.navActiveClass), q(i, "active"), g.style.position = h.openPos, p(g, { "aria-hidden": "false" }), l = !0, h.open()) }, close: function () { l && (q(g, "closed"), r(g, "opened"), r(u, h.navActiveClass), r(i, "active"), p(g, { "aria-hidden": "true" }), h.animate ? (j = !1, setTimeout(function () { g.style.position = "absolute", j = !0 }, h.transition + 10)) : g.style.position = "absolute", l = !1, h.close()) }, resize: function () { "none" !== b.getComputedStyle(i, null).getPropertyValue("display") ? (k = !0, p(i, { "aria-hidden": "false" }), g.className.match(/(^|\s)closed(\s|$)/) && (p(g, { "aria-hidden": "true" }), g.style.position = "absolute"), this._createStyles(), this._calcHeight()) : (k = !1, p(i, { "aria-hidden": "true" }), p(g, { "aria-hidden": "false" }), g.style.position = h.openPos, this._removeStyles()) }, handleEvent: function (a) { var c = a || b.event; switch (c.type) { case "touchstart": this._onTouchStart(c); break; case "touchmove": this._onTouchMove(c); break; case "touchend": case "mouseup": this._onTouchEnd(c); break; case "click": this._preventDefault(c); break; case "keyup": this._onKeyUp(c); break; case "focus": case "resize": this.resize(c) } }, _init: function () { this.index = c++ , q(g, h.navClass), q(g, h.navClass + "-" + this.index), q(g, "closed"), j = !0, l = !1, this._closeOnNavClick(), this._createToggle(), this._transitions(), this.resize(); var d = this; setTimeout(function () { d.resize() }, 20), m(b, "resize", this, !1), m(b, "focus", this, !1), m(a.body, "touchmove", this, !1), m(i, "touchstart", this, !1), m(i, "touchend", this, !1), m(i, "mouseup", this, !1), m(i, "keyup", this, !1), m(i, "click", this, !1), h.init() }, _createStyles: function () { t.parentNode || (t.type = "text/css", a.getElementsByTagName("head")[0].appendChild(t)) }, _removeStyles: function () { t.parentNode && t.parentNode.removeChild(t) }, _createToggle: function () { if (h.customToggle) { var b = h.customToggle.replace("#", ""); if (a.getElementById(b)) i = a.getElementById(b); else { if (!a.querySelector(b)) throw new Error("The custom nav toggle you are trying to select doesn't exist"); i = a.querySelector(b) } } else { var c = a.createElement("a"); c.innerHTML = h.label, p(c, { href: "#", "class": "nav-toggle" }), "after" === h.insert ? g.parentNode.insertBefore(c, g.nextSibling) : g.parentNode.insertBefore(c, g), i = c } }, _closeOnNavClick: function () { if (h.closeOnNavClick) { var a = g.getElementsByTagName("a"), b = this; s(a, function (c) { m(a[c], "click", function () { k && b.toggle() }, !1) }) } }, _preventDefault: function (a) { return a.preventDefault ? (a.stopImmediatePropagation && a.stopImmediatePropagation(), a.preventDefault(), a.stopPropagation(), !1) : void (a.returnValue = !1) }, _onTouchStart: function (a) { Event.prototype.stopImmediatePropagation || this._preventDefault(a), this.startX = a.touches[0].clientX, this.startY = a.touches[0].clientY, this.touchHasMoved = !1, n(i, "mouseup", this, !1) }, _onTouchMove: function (a) { (Math.abs(a.touches[0].clientX - this.startX) > 10 || Math.abs(a.touches[0].clientY - this.startY) > 10) && (this.touchHasMoved = !0) }, _onTouchEnd: function (a) { if (this._preventDefault(a), k && !this.touchHasMoved) { if ("touchend" === a.type) return void this.toggle(); var c = a || b.event; 3 !== c.which && 2 !== c.button && this.toggle() } }, _onKeyUp: function (a) { var c = a || b.event; 13 === c.keyCode && this.toggle() }, _transitions: function () { if (h.animate) { var a = g.style, b = "max-height " + h.transition + "ms"; a.WebkitTransition = a.MozTransition = a.OTransition = a.transition = b } }, _calcHeight: function () { for (var a = 0, b = 0; b < g.inner.length; b++)a += g.inner[b].offsetHeight; var c = "." + h.jsClass + " ." + h.navClass + "-" + this.index + ".opened{max-height:" + a + "px !important} ." + h.jsClass + " ." + h.navClass + "-" + this.index + ".opened.dropdown-active {max-height:9999px !important}"; t.styleSheet ? t.styleSheet.cssText = c : t.innerHTML = c, c = "" } }, new v(d, e) }; "undefined" != typeof module && module.exports ? module.exports = d : b.responsiveNav = d }(document, window, 0); \ No newline at end of file diff --git a/ydoc/scripts/plugins/slideout.min.js b/ydoc/scripts/plugins/slideout.min.js new file mode 100644 index 0000000..81c06b8 --- /dev/null +++ b/ydoc/scripts/plugins/slideout.min.js @@ -0,0 +1 @@ +!function (t) { if ("object" == typeof exports && "undefined" != typeof module) module.exports = t(); else if ("function" == typeof define && define.amd) define([], t); else { var e; "undefined" != typeof window ? e = window : "undefined" != typeof global ? e = global : "undefined" != typeof self && (e = self), e.Slideout = t() } }(function () { var t, e, n; return function i(t, e, n) { function o(r, a) { if (!e[r]) { if (!t[r]) { var u = typeof require == "function" && require; if (!a && u) return u(r, !0); if (s) return s(r, !0); var l = new Error("Cannot find module '" + r + "'"); throw l.code = "MODULE_NOT_FOUND", l } var f = e[r] = { exports: {} }; t[r][0].call(f.exports, function (e) { var n = t[r][1][e]; return o(n ? n : e) }, f, f.exports, i, t, e, n) } return e[r].exports } var s = typeof require == "function" && require; for (var r = 0; r < n.length; r++)o(n[r]); return o }({ 1: [function (t, e, n) { "use strict"; var i = t("decouple"); var o = t("emitter"); var s; var r = false; var a = window.document; var u = a.documentElement; var l = window.navigator.msPointerEnabled; var f = { start: l ? "MSPointerDown" : "touchstart", move: l ? "MSPointerMove" : "touchmove", end: l ? "MSPointerUp" : "touchend" }; var h = function v() { var t = /^(Webkit|Khtml|Moz|ms|O)(?=[A-Z])/; var e = a.getElementsByTagName("script")[0].style; for (var n in e) { if (t.test(n)) { return "-" + n.match(t)[0].toLowerCase() + "-" } } if ("WebkitOpacity" in e) { return "-webkit-" } if ("KhtmlOpacity" in e) { return "-khtml-" } return "" }(); function c(t, e) { for (var n in e) { if (e[n]) { t[n] = e[n] } } return t } function p(t, e) { t.prototype = c(t.prototype || {}, e.prototype) } function d(t) { while (t.parentNode) { if (t.getAttribute("data-slideout-ignore") !== null) { return t } t = t.parentNode } return null } function _(t) { t = t || {}; this._startOffsetX = 0; this._currentOffsetX = 0; this._opening = false; this._moved = false; this._opened = false; this._preventOpen = false; this._touch = t.touch === undefined ? true : t.touch && true; this._side = t.side || "left"; this.panel = t.panel; this.menu = t.menu; if (!this.panel.classList.contains("slideout-panel")) { this.panel.classList.add("slideout-panel") } if (!this.panel.classList.contains("slideout-panel-" + this._side)) { this.panel.classList.add("slideout-panel-" + this._side) } if (!this.menu.classList.contains("slideout-menu")) { this.menu.classList.add("slideout-menu") } if (!this.menu.classList.contains("slideout-menu-" + this._side)) { this.menu.classList.add("slideout-menu-" + this._side) } this._fx = t.fx || "ease"; this._duration = parseInt(t.duration, 10) || 300; this._tolerance = parseInt(t.tolerance, 10) || 70; this._padding = this._translateTo = parseInt(t.padding, 10) || 256; this._orientation = this._side === "right" ? -1 : 1; this._translateTo *= this._orientation; if (this._touch) { this._initTouchEvents() } } p(_, o); _.prototype.open = function () { var t = this; this.emit("beforeopen"); if (!u.classList.contains("slideout-open")) { u.classList.add("slideout-open") } this._setTransition(); this._translateXTo(this._translateTo); this._opened = true; setTimeout(function () { t.panel.style.transition = t.panel.style["-webkit-transition"] = ""; t.emit("open") }, this._duration + 50); return this }; _.prototype.close = function () { var t = this; if (!this.isOpen() && !this._opening) { return this } this.emit("beforeclose"); this._setTransition(); this._translateXTo(0); this._opened = false; setTimeout(function () { u.classList.remove("slideout-open"); t.panel.style.transition = t.panel.style["-webkit-transition"] = t.panel.style[h + "transform"] = t.panel.style.transform = ""; t.emit("close") }, this._duration + 50); return this }; _.prototype.toggle = function () { return this.isOpen() ? this.close() : this.open() }; _.prototype.isOpen = function () { return this._opened }; _.prototype._translateXTo = function (t) { this._currentOffsetX = t; this.panel.style[h + "transform"] = this.panel.style.transform = "translateX(" + t + "px)"; return this }; _.prototype._setTransition = function () { this.panel.style[h + "transition"] = this.panel.style.transition = h + "transform " + this._duration + "ms " + this._fx; return this }; _.prototype._initTouchEvents = function () { var t = this; this._onScrollFn = i(a, "scroll", function () { if (!t._moved) { clearTimeout(s); r = true; s = setTimeout(function () { r = false }, 250) } }); this._preventMove = function (e) { if (t._moved) { e.preventDefault() } }; a.addEventListener(f.move, this._preventMove); this._resetTouchFn = function (e) { if (typeof e.touches === "undefined") { return } t._moved = false; t._opening = false; t._startOffsetX = e.touches[0].pageX; t._preventOpen = !t._touch || !t.isOpen() && t.menu.clientWidth !== 0 }; this.panel.addEventListener(f.start, this._resetTouchFn); this._onTouchCancelFn = function () { t._moved = false; t._opening = false }; this.panel.addEventListener("touchcancel", this._onTouchCancelFn); this._onTouchEndFn = function () { if (t._moved) { t.emit("translateend"); t._opening && Math.abs(t._currentOffsetX) > t._tolerance ? t.open() : t.close() } t._moved = false }; this.panel.addEventListener(f.end, this._onTouchEndFn); this._onTouchMoveFn = function (e) { if (r || t._preventOpen || typeof e.touches === "undefined" || d(e.target)) { return } var n = e.touches[0].clientX - t._startOffsetX; var i = t._currentOffsetX = n; if (Math.abs(i) > t._padding) { return } if (Math.abs(n) > 20) { t._opening = true; var o = n * t._orientation; if (t._opened && o > 0 || !t._opened && o < 0) { return } if (!t._moved) { t.emit("translatestart") } if (o <= 0) { i = n + t._padding * t._orientation; t._opening = false } if (!(t._moved && u.classList.contains("slideout-open"))) { u.classList.add("slideout-open") } t.panel.style[h + "transform"] = t.panel.style.transform = "translateX(" + i + "px)"; t.emit("translate", i); t._moved = true } }; this.panel.addEventListener(f.move, this._onTouchMoveFn); return this }; _.prototype.enableTouch = function () { this._touch = true; return this }; _.prototype.disableTouch = function () { this._touch = false; return this }; _.prototype.destroy = function () { this.close(); a.removeEventListener(f.move, this._preventMove); this.panel.removeEventListener(f.start, this._resetTouchFn); this.panel.removeEventListener("touchcancel", this._onTouchCancelFn); this.panel.removeEventListener(f.end, this._onTouchEndFn); this.panel.removeEventListener(f.move, this._onTouchMoveFn); a.removeEventListener("scroll", this._onScrollFn); this.open = this.close = function () { }; return this }; e.exports = _ }, { decouple: 2, emitter: 3 }], 2: [function (t, e, n) { "use strict"; var i = function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || function (t) { window.setTimeout(t, 1e3 / 60) } }(); function o(t, e, n) { var o, s = false; function r(t) { o = t; a() } function a() { if (!s) { i(u); s = true } } function u() { n.call(t, o); s = false } t.addEventListener(e, r, false); return r } e.exports = o }, {}], 3: [function (t, e, n) { "use strict"; var i = function (t, e) { if (!(t instanceof e)) { throw new TypeError("Cannot call a class as a function") } }; n.__esModule = true; var o = function () { function t() { i(this, t) } t.prototype.on = function e(t, n) { this._eventCollection = this._eventCollection || {}; this._eventCollection[t] = this._eventCollection[t] || []; this._eventCollection[t].push(n); return this }; t.prototype.once = function n(t, e) { var n = this; function i() { n.off(t, i); e.apply(this, arguments) } i.listener = e; this.on(t, i); return this }; t.prototype.off = function o(t, e) { var n = undefined; if (!this._eventCollection || !(n = this._eventCollection[t])) { return this } n.forEach(function (t, i) { if (t === e || t.listener === e) { n.splice(i, 1) } }); if (n.length === 0) { delete this._eventCollection[t] } return this }; t.prototype.emit = function s(t) { var e = this; for (var n = arguments.length, i = Array(n > 1 ? n - 1 : 0), o = 1; o < n; o++) { i[o - 1] = arguments[o] } var s = undefined; if (!this._eventCollection || !(s = this._eventCollection[t])) { return this } s = s.slice(0); s.forEach(function (t) { return t.apply(e, i) }); return this }; return t }(); n["default"] = o; e.exports = n["default"] }, {}] }, {}, [1])(1) }); \ No newline at end of file diff --git a/ydoc/styles/style.css b/ydoc/styles/style.css new file mode 100644 index 0000000..8a1e1f9 --- /dev/null +++ b/ydoc/styles/style.css @@ -0,0 +1,2273 @@ +@charset "UTF-8"; +/* + * by wenbo.dong@qunar.com + * method color + * param $color: color HEX + * param $index: color number +*/ + +@font-face { + font-family: 'ydoc'; + src: url("https://s.qunarzz.com/ydoc/fonts/0.0.4/ydoc.eot"); + /* IE9*/ + src: url("https://s.qunarzz.com/ydoc/fonts/0.0.4/ydoc.woff") format("woff"), url("https://s.qunarzz.com/ydoc/fonts/0.0.4/ymfe.ttf") format("truetype"), url("https://s.qunarzz.com/ydoc/fonts/0.0.4/ydoc.svg#iconfont") format("svg"); + /* iOS 4.1- */ +} + +html { + height: 100%; + font-size: 100px; +} + +body { + width: 100%; + height: 100%; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif; + font-size: 14px; + color: rgba(3, 17, 31, 0.87); + -webkit-font-smoothing: antialiased; + line-height: 1.5; +} + +.ui-font-ydoc { + font-family: ydoc; +} + +::-webkit-scrollbar { + width: 6px; + height: 4px; +} + +::-webkit-scrollbar-track { + -webkit-box-shadow: rgba(217, 237, 255, 0.3); + background: rgba(217, 237, 255, 0.1); +} + +::-webkit-scrollbar-thumb { + border-radius: 3px; + background: rgba(101, 181, 255, 0.3); + -webkit-box-shadow: rgba(217, 237, 255, 0.5); +} + +::-webkit-scrollbar-thumb:window-inactive { + background: rgba(101, 181, 255, 0.3); +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: .5em; + font-weight: 400; + line-height: 1.5; +} + +p { + margin-top: 0; + margin-bottom: 1em; +} + +h1, .h1 { + font-size: 32px; +} + +h2, .h2 { + font-size: 24px; +} + +h3, .h3 { + font-size: 20px; +} + +h4, .h4 { + font-size: 16px; +} + +h5, .h5 { + font-size: 14px; +} + +h6, .h6 { + font-size: 12px; +} + +@media screen and (max-width: 960px) { + ::-webkit-scrollbar { + /*隐藏滚轮*/ + display: none; + } +} + +*, +::before, +::after { + box-sizing: border-box; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +ul, +ol, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +figure, +form, +fieldset, +legend, +input, +textarea, +button, +p, +blockquote, +th, +td, +pre, +xmp { + margin: 0; + padding: 0; +} + +input, +textarea, +button, +select, +pre, +xmp, +tt, +code, +kbd, +samp { + line-height: inherit; + font-family: inherit; +} + +table { + border-collapse: collapse; + border-spacing: 0; + table-layout: fixed; + text-align: left; +} + +fieldset, +img { + border: 0; + vertical-align: middle; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +main, +menu, +nav, +section, +summary { + display: block; +} + +audio, +canvas, +video { + display: inline-block; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: "\0020"; +} + +textarea { + resize: vertical; +} + +input, +textarea, +button, +select +a { + outline: 0 none; +} + +input, +textarea, +button, +select { + color: inherit; +} + +input:disabled, +textarea:disabled, +button:disabled, +select:disabled { + opacity: 1; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +mark { + background-color: rgba(0, 0, 0, 0); +} + +a, +ins, +s, +u, +del { + text-decoration: none; +} + +a, +img { + -webkit-touch-callout: none; +} + +a:link, a:visited { + color: inherit; +} + +.row { + box-sizing: border-box; + width: 100%; +} + +.row:after { + content: ''; + display: table; + clear: both; +} + +.container { + box-sizing: border-box; +} + +@media screen and (max-width: 600px) { + .col-xs-1 { + width: 8.33333%; + float: left; + box-sizing: border-box; + } + .col-xs-2 { + width: 16.66667%; + float: left; + box-sizing: border-box; + } + .col-xs-3 { + width: 25%; + float: left; + box-sizing: border-box; + } + .col-xs-4 { + width: 33.33333%; + float: left; + box-sizing: border-box; + } + .col-xs-5 { + width: 41.66667%; + float: left; + box-sizing: border-box; + } + .col-xs-6 { + width: 50%; + float: left; + box-sizing: border-box; + } + .col-xs-7 { + width: 58.33333%; + float: left; + box-sizing: border-box; + } + .col-xs-8 { + width: 66.66667%; + float: left; + box-sizing: border-box; + } + .col-xs-9 { + width: 75%; + float: left; + box-sizing: border-box; + } + .col-xs-10 { + width: 83.33333%; + float: left; + box-sizing: border-box; + } + .col-xs-11 { + width: 91.66667%; + float: left; + box-sizing: border-box; + } + .col-xs-12 { + width: 100%; + float: left; + box-sizing: border-box; + } + .container { + width: 100%; + padding: 0 .16rem; + } +} + +@media screen and (min-width: 600px) { + .col-sm-1 { + width: 8.33333%; + float: left; + box-sizing: border-box; + } + .col-sm-2 { + width: 16.66667%; + float: left; + box-sizing: border-box; + } + .col-sm-3 { + width: 25%; + float: left; + box-sizing: border-box; + } + .col-sm-4 { + width: 33.33333%; + float: left; + box-sizing: border-box; + } + .col-sm-5 { + width: 41.66667%; + float: left; + box-sizing: border-box; + } + .col-sm-6 { + width: 50%; + float: left; + box-sizing: border-box; + } + .col-sm-7 { + width: 58.33333%; + float: left; + box-sizing: border-box; + } + .col-sm-8 { + width: 66.66667%; + float: left; + box-sizing: border-box; + } + .col-sm-9 { + width: 75%; + float: left; + box-sizing: border-box; + } + .col-sm-10 { + width: 83.33333%; + float: left; + box-sizing: border-box; + } + .col-sm-11 { + width: 91.66667%; + float: left; + box-sizing: border-box; + } + .col-sm-12 { + width: 100%; + float: left; + box-sizing: border-box; + } + .container { + width: 100%; + padding: 0 .16rem; + } +} + +@media screen and (min-width: 960px) { + .col-md-1 { + width: 8.33333%; + float: left; + box-sizing: border-box; + } + .col-md-2 { + width: 16.66667%; + float: left; + box-sizing: border-box; + } + .col-md-3 { + width: 25%; + float: left; + box-sizing: border-box; + } + .col-md-4 { + width: 33.33333%; + float: left; + box-sizing: border-box; + } + .col-md-5 { + width: 41.66667%; + float: left; + box-sizing: border-box; + } + .col-md-6 { + width: 50%; + float: left; + box-sizing: border-box; + } + .col-md-7 { + width: 58.33333%; + float: left; + box-sizing: border-box; + } + .col-md-8 { + width: 66.66667%; + float: left; + box-sizing: border-box; + } + .col-md-9 { + width: 75%; + float: left; + box-sizing: border-box; + } + .col-md-10 { + width: 83.33333%; + float: left; + box-sizing: border-box; + } + .col-md-11 { + width: 91.66667%; + float: left; + box-sizing: border-box; + } + .col-md-12 { + width: 100%; + float: left; + box-sizing: border-box; + } + .container { + width: 100%; + padding: 0 .16rem; + } +} + +@media screen and (min-width: 1440px) { + .col-lg-1 { + width: 8.33333%; + float: left; + box-sizing: border-box; + } + .col-lg-2 { + width: 16.66667%; + float: left; + box-sizing: border-box; + } + .col-lg-3 { + width: 25%; + float: left; + box-sizing: border-box; + } + .col-lg-4 { + width: 33.33333%; + float: left; + box-sizing: border-box; + } + .col-lg-5 { + width: 41.66667%; + float: left; + box-sizing: border-box; + } + .col-lg-6 { + width: 50%; + float: left; + box-sizing: border-box; + } + .col-lg-7 { + width: 58.33333%; + float: left; + box-sizing: border-box; + } + .col-lg-8 { + width: 66.66667%; + float: left; + box-sizing: border-box; + } + .col-lg-9 { + width: 75%; + float: left; + box-sizing: border-box; + } + .col-lg-10 { + width: 83.33333%; + float: left; + box-sizing: border-box; + } + .col-lg-11 { + width: 91.66667%; + float: left; + box-sizing: border-box; + } + .col-lg-12 { + width: 100%; + float: left; + box-sizing: border-box; + } + .container { + width: 100%; + padding: 0 .16rem; + } +} + +.useage1 { + background-color: #d9edff; +} + +.useage2 { + background-color: #b2daff; +} + +.useage3 { + background-color: #8cc8ff; +} + +.useage4 { + background-color: #65b5ff; +} + +.useage5 { + background-color: #3fa3ff; +} + +.useage6 { + background-color: #1890ff; +} + +.useage7 { + background-color: #1373cc; +} + +.useage8 { + background-color: #0e5699; +} + +.useage9 { + background-color: #0a3a66; +} + +.useage10 { + background-color: #051d33; +} + +/*! responsive-nav.js 1.0.39 by @viljamis */ +.m-header-nav ul { + margin: 0; + padding: 0; + width: 100%; + display: block; + list-style: none; +} + +.js .m-header-nav { + clip: rect(0 0 0 0); + max-height: 0; + position: absolute; + display: block; + overflow: hidden; + zoom: 1; +} + +.m-header-nav.opened { + max-height: 9999px; +} + +.nav-toggle { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +@media screen and (min-width: 960px) { + .js .m-header-nav { + position: relative; + } + .js .m-header-nav.closed { + max-height: none; + } + .nav-toggle { + display: none; + } + .m-header-btn { + display: none; + } +} + +.slideout-menu { + position: fixed; + left: 0; + top: 0; + bottom: 0; + right: 0; + z-index: 0; + width: 256px; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + display: none; +} + +.slideout-panel { + position: relative; + z-index: 1; + will-change: transform; +} + +.slideout-open, +.slideout-open body, +.slideout-open .slideout-panel { + overflow: hidden; +} + +.slideout-open .slideout-menu { + display: block; +} + +@media screen and (min-width: 960px) { + .slideout-menu { + display: block; + } + .slideout-panel { + will-change: inherit; + } +} + +@font-face { + font-family: octicons-link; + src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format("woff"); +} + +.markdown-body { + box-sizing: border-box; + min-width: 200px; + margin: 0 auto; + padding: 0; + transition: all 0.2s; +} + +@media screen and (min-width: 960px) { + .markdown-body { + padding: 0 .4rem; + } +} + +.markdown-body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + line-height: 1.5; + color: #24292e; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; +} + +.markdown-body .pl-c { + color: #6a737d; +} + +.markdown-body .pl-c1, +.markdown-body .pl-s .pl-v { + color: #005cc5; +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: #6f42c1; +} + +.markdown-body .pl-smi, +.markdown-body .pl-s .pl-s1 { + color: #24292e; +} + +.markdown-body .pl-ent { + color: #22863a; +} + +.markdown-body .pl-k { + color: #d73a49; +} + +.markdown-body .pl-s, +.markdown-body .pl-pds, +.markdown-body .pl-s .pl-pse .pl-s1, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sre, +.markdown-body .pl-sr .pl-sra { + color: #032f62; +} + +.markdown-body .pl-v, +.markdown-body .pl-smw { + color: #e36209; +} + +.markdown-body .pl-bu { + color: #b31d28; +} + +.markdown-body .pl-ii { + color: #fafbfc; + background-color: #b31d28; +} + +.markdown-body .pl-c2 { + color: #fafbfc; + background-color: #d73a49; +} + +.markdown-body .pl-c2::before { + content: "^M"; +} + +.markdown-body .pl-sr .pl-cce { + font-weight: bold; + color: #22863a; +} + +.markdown-body .pl-ml { + color: #735c0f; +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + font-weight: bold; + color: #005cc5; +} + +.markdown-body .pl-mi { + font-style: italic; + color: #24292e; +} + +.markdown-body .pl-mb { + font-weight: bold; + color: #24292e; +} + +.markdown-body .pl-md { + color: #b31d28; + background-color: #ffeef0; +} + +.markdown-body .pl-mi1 { + color: #22863a; + background-color: #f0fff4; +} + +.markdown-body .pl-mc { + color: #e36209; + background-color: #ffebda; +} + +.markdown-body .pl-mi2 { + color: #f6f8fa; + background-color: #005cc5; +} + +.markdown-body .pl-mdr { + font-weight: bold; + color: #6f42c1; +} + +.markdown-body .pl-ba { + color: #586069; +} + +.markdown-body .pl-sg { + color: #959da5; +} + +.markdown-body .pl-corl { + text-decoration: underline; + color: #032f62; +} + +.markdown-body .octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; +} + +.markdown-body a { + background-color: transparent; +} + +.markdown-body a:active, +.markdown-body a:hover { + outline-width: 0; +} + +.markdown-body strong { + font-weight: inherit; +} + +.markdown-body strong { + font-weight: bolder; +} + +.markdown-body h1 { + font-size: 2em; + margin: 0.67em 0; +} + +.markdown-body img { + border-style: none; +} + +.markdown-body code, +.markdown-body kbd, +.markdown-body pre { + font-family: monospace, monospace; + font-size: 1em; +} + +.markdown-body hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +.markdown-body input { + font: inherit; + margin: 0; +} + +.markdown-body input { + overflow: visible; +} + +.markdown-body [type="checkbox"] { + box-sizing: border-box; + padding: 0; +} + +.markdown-body * { + box-sizing: border-box; +} + +.markdown-body input { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.markdown-body a { + color: #0366d6; + text-decoration: none; +} + +.markdown-body a:hover { + text-decoration: underline; +} + +.markdown-body strong { + font-weight: 600; +} + +.markdown-body hr { + height: 0; + margin: 15px 0; + overflow: hidden; + background: transparent; + border: 0; + border-bottom: 1px solid #dfe2e5; +} + +.markdown-body hr::before { + display: table; + content: ""; +} + +.markdown-body hr::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body table { + border-spacing: 0; + border-collapse: collapse; +} + +.markdown-body td, +.markdown-body th { + padding: 0; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body h1 { + font-size: 32px; + font-weight: 600; +} + +.markdown-body h2 { + font-size: 24px; + font-weight: 600; +} + +.markdown-body h3 { + font-size: 20px; + font-weight: 600; +} + +.markdown-body h4 { + font-size: 16px; + font-weight: 600; +} + +.markdown-body h5 { + font-size: 14px; + font-weight: 600; +} + +.markdown-body h6 { + font-size: 12px; + font-weight: 600; +} + +.markdown-body p { + margin-top: 0; + margin-bottom: 10px; +} + +.markdown-body blockquote { + margin: 0; +} + +.markdown-body ul, +.markdown-body ol { + padding-left: 0; + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ul ul ol, +.markdown-body ul ol ol, +.markdown-body ol ul ol, +.markdown-body ol ol ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body code { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 12px; +} + +.markdown-body pre { + margin-top: 0; + margin-bottom: 0; + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 12px; +} + +.markdown-body .octicon { + vertical-align: text-bottom; +} + +.markdown-body .pl-0 { + padding-left: 0 !important; +} + +.markdown-body .pl-1 { + padding-left: 4px !important; +} + +.markdown-body .pl-2 { + padding-left: 8px !important; +} + +.markdown-body .pl-3 { + padding-left: 16px !important; +} + +.markdown-body .pl-4 { + padding-left: 24px !important; +} + +.markdown-body .pl-5 { + padding-left: 32px !important; +} + +.markdown-body .pl-6 { + padding-left: 40px !important; +} + +.markdown-body::before { + display: table; + content: ""; +} + +.markdown-body::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body > *:first-child { + margin-top: 0 !important; +} + +.markdown-body > *:last-child { + margin-bottom: 0 !important; +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none; +} + +.markdown-body .anchor { + float: left; + padding-right: 4px; + margin-left: -20px; + line-height: 1; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body p, +.markdown-body blockquote, +.markdown-body ul, +.markdown-body ol, +.markdown-body dl, +.markdown-body table, +.markdown-body pre { + margin-top: 0; + margin-bottom: 16px; +} + +.markdown-body hr { + height: 0.12em; + padding: 0; + margin: 24px 0; + background-color: #e1e4e8; + border: 0; +} + +.markdown-body blockquote { + padding: 0 1em; + color: #6a737d; + border-left: 0.25em solid #dfe2e5; +} + +.markdown-body blockquote > :first-child { + margin-top: 0; +} + +.markdown-body blockquote > :last-child { + margin-bottom: 0; +} + +.markdown-body kbd { + display: inline-block; + padding: 3px 5px; + font-size: 11px; + line-height: 10px; + color: #444d56; + vertical-align: middle; + background-color: #fafbfc; + border: solid 1px #c6cbd1; + border-bottom-color: #959da5; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #959da5; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.25; +} + +.markdown-body h1 .octicon-link, +.markdown-body h2 .octicon-link, +.markdown-body h3 .octicon-link, +.markdown-body h4 .octicon-link, +.markdown-body h5 .octicon-link, +.markdown-body h6 .octicon-link { + color: #1b1f23; + vertical-align: middle; + visibility: hidden; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + text-decoration: none; +} + +.markdown-body h1:hover .anchor .octicon-link, +.markdown-body h2:hover .anchor .octicon-link, +.markdown-body h3:hover .anchor .octicon-link, +.markdown-body h4:hover .anchor .octicon-link, +.markdown-body h5:hover .anchor .octicon-link, +.markdown-body h6:hover .anchor .octicon-link { + visibility: visible; +} + +.markdown-body h1 { + padding-bottom: 0.3em; + font-size: 2em; +} + +.markdown-body h2 { + padding-bottom: 0.3em; + font-size: 1.5em; +} + +.markdown-body h3 { + font-size: 1.25em; +} + +.markdown-body h4 { + font-size: 1em; +} + +.markdown-body h5 { + font-size: 0.875em; +} + +.markdown-body h6 { + font-size: 0.85em; + color: #6a737d; +} + +.markdown-body ul, +.markdown-body ol { + padding-left: 2em; +} + +.markdown-body ul ul, +.markdown-body ul ol, +.markdown-body ol ol, +.markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body li { + word-wrap: break-all; +} + +.markdown-body li > p { + margin-top: 16px; +} + +.markdown-body li + li { + margin-top: 0.25em; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + padding: 0; + margin-top: 16px; + font-size: 1em; + font-style: italic; + font-weight: 600; +} + +.markdown-body dl dd { + padding: 0 16px; + margin-bottom: 16px; +} + +.markdown-body table { + display: block; + width: 100%; + overflow: auto; +} + +.markdown-body table th { + font-weight: 600; +} + +.markdown-body table th, +.markdown-body table td { + padding: 6px 13px; + border: 1px solid #dfe2e5; +} + +.markdown-body table tr { + background-color: #fff; + border-top: 1px solid #c6cbd1; +} + +.markdown-body table tr:nth-child(2n) { + background-color: #f6f8fa; +} + +.markdown-body img { + max-width: 100%; + box-sizing: content-box; + background-color: #fff; +} + +.markdown-body img[align=right] { + padding-left: 20px; +} + +.markdown-body img[align=left] { + padding-right: 20px; +} + +.markdown-body code { + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + background-color: rgba(27, 31, 35, 0.05); + border-radius: 3px; +} + +.markdown-body pre { + word-wrap: normal; +} + +.markdown-body pre > code { + padding: 0; + margin: 0; + font-size: 100%; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; +} + +.markdown-body .highlight { + margin-bottom: 16px; +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +.markdown-body .highlight pre, +.markdown-body pre { + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + background-color: #f6f8fa; + border-radius: 3px; +} + +.markdown-body pre code { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +.markdown-body .full-commit .btn-outline:not(:disabled):hover { + color: #005cc5; + border-color: #005cc5; +} + +.markdown-body kbd { + display: inline-block; + padding: 3px 5px; + font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + line-height: 10px; + color: #444d56; + vertical-align: middle; + background-color: #fafbfc; + border: solid 1px #d1d5da; + border-bottom-color: #c6cbd1; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #c6cbd1; +} + +.markdown-body :checked + .radio-label { + position: relative; + z-index: 1; + border-color: #0366d6; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item + .task-list-item { + margin-top: 3px; +} + +.markdown-body .task-list-item input { + margin: 0 0.2em 0.25em -1.6em; + vertical-align: middle; +} + +.markdown-body hr { + border-bottom-color: #eee; +} + +.markdown-body { + color: rgba(3, 17, 31, 0.87); +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + font-weight: 500; +} + +.markdown-body h2 { + margin: .54rem 0 .24rem; +} + +.markdown-body h3 { + margin: .32rem 0 .16rem; +} + +.markdown-body a { + text-decoration: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + color: #086fff; + transition: all .2s; +} + +.markdown-body a:hover { + text-decoration: none; + border-bottom-color: rgba(3, 17, 31, 0.87); +} + +.markdown-body .type { + color: #1890ff; + font-size: 16px; +} + +.markdown-body .versionTag { + background: #1890ff; + color: white; + border-radius: 4px; + padding: 2px 4px; + font-size: 14px; +} + +.markdown-body .desc { + font-size: 16px; + font-weight: normal; + color: rgba(0, 0, 0, 0.65); +} + +/* PrismJS 1.14.0 +http://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: rgba(255, 255, 255, 0.5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +.g-doc { + display: flex; + width: 100%; + height: 100%; + position: relative; + left: 0; + transition: all 0.2s ease-in-out; + overflow: hidden; +} + +.m-main { + width: 100%; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.m-main h2:before, .m-main h3:before { + content: ""; + display: block; + margin-top: -.8rem; + height: .8rem; + visibility: hidden; +} + +@media screen and (min-width: 960px) { + .m-main { + box-shadow: none; + } +} + +.m-header { + min-height: .64rem; + position: relative; + display: flex; + flex-direction: column; + position: fixed; + top: 0; + left: 0; + width: 100%; + color: rgba(3, 17, 31, 0.54); + background-color: rgba(255, 255, 255, 0.97); + border-bottom: 1px solid #e5f0f6; + backface-visibility: hidden; + z-index: 999; + transform: translateZ(1000px); +} + +.m-header-title { + height: .64rem; + padding: 0 .3rem; + display: flex; + align-items: center; +} + +.m-header-title .name, .m-header-title .logo { + display: inline-block; + vertical-align: middle; +} + +.m-header-title .name { + margin-left: .08rem; + color: #1890ff; + font-size: 24px; + font-family: 'Arial Rounded MT Bold'; +} + +.m-header-nav { + border-bottom: 1px solid #e5f0f6; +} + +.m-header-nav .m-header-items { + padding: 0.16rem; +} + +.m-header-nav .m-header-subtitle { + padding: .08rem 1.5em; +} + +.m-header-nav .m-header-subtitle .item { + width: 100%; +} + +.m-header-nav .m-header-subtitle .link { + display: inline-block; + padding-left: .5em; + padding-right: .5em; + width: 100%; + background-color: transparent; + border-radius: 4px; + line-height: .32rem; + transition: all 0.2s; +} + +.m-header-nav .m-header-subtitle .link:hover { + color: #086fff; + background-color: #d9edff; +} + +.m-header-nav .item { + width: 100%; + display: inline-block; + line-height: 1.8; + padding: 0 0.08rem; + position: relative; + cursor: pointer; + transition: all 0.2s; + min-height: .32rem; +} + +.m-header-nav .item:last-child { + margin-bottom: 0; +} + +.m-header-nav .item.active { + font-weight: bold; + color: #086fff; +} + +.m-header-nav .href { + display: inline-block; + padding-left: .5em; + padding-right: .5em; + height: .32rem; + line-height: .32rem; + width: 100%; + background-color: transparent; + border-radius: 4px; + transition: all 0.2s; +} + +.m-header-nav .href:active { + color: #086fff; + background-color: #d9edff; +} + +.m-header-btn { + position: absolute; + top: 0.08rem; + right: 0.08rem; + font-size: .24rem; + line-height: 1; + color: rgba(3, 17, 31, 0.87); + border-radius: 2px; + padding: .12rem; + cursor: pointer; + transition: color 0.2s; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +.m-header-btn:hover { + font-weight: bold; + color: rgba(3, 17, 31, 0.87); +} + +@media screen and (min-width: 960px) { + .m-header { + flex-direction: row; + } + .m-header-nav { + border: none; + } + .m-header-nav .m-header-items { + padding: 0; + } + .m-header-nav .m-header-items > .item:hover .m-header-subtitle { + display: flex; + flex-wrap: wrap; + } + .m-header-nav .m-header-subtitle { + display: none; + position: absolute; + left: 50%; + top: .64rem; + transform: translateX(-50%); + width: auto; + background-color: #fff; + padding: .08rem 1.5em; + border-radius: 4px; + box-shadow: 0 10px 100px rgba(50, 50, 93, 0.1), 0 5px 35px rgba(50, 50, 93, 0.15), 0 2px 15px rgba(0, 0, 0, 0.1); + } + .m-header-nav .m-header-subtitle .item { + margin: 0; + padding: 0 8px; + color: rgba(3, 17, 31, 0.54); + font-weight: normal; + white-space: nowrap; + line-height: .4rem; + } + .m-header-nav .m-header-subtitle:after { + content: ''; + display: block; + width: 0; + height: 0; + border: .08rem solid transparent; + border-bottom-color: #fff; + position: absolute; + left: 50%; + top: -.16rem; + transform: translateX(-50%); + } + .m-header-nav .m-header-subtitle .link { + display: inline-block; + padding: 0; + width: auto; + } + .m-header-nav .m-header-subtitle .link:hover { + font-weight: bold; + color: rgba(3, 17, 31, 0.87); + background-color: transparent; + } + .m-header-nav .href { + border-radius: 0; + } + .m-header-nav .href:hover { + font-weight: bold; + color: rgba(3, 17, 31, 0.87); + background-color: transparent; + } + .m-header-nav .item { + margin: 0 0.16rem; + line-height: .64rem; + width: auto; + transition: color 0.2s; + } + .m-header-nav .item.active { + font-weight: bold; + color: rgba(3, 17, 31, 0.87); + } + .m-header-nav .item.active .href { + border-bottom: 2px solid #086fff; + } + .m-header .m-header-nav { + overflow: inherit; + } +} + +.m-aside { + margin-top: .65rem; + height: 100%; + height: calc(100% - .65rem); +} + +.m-summary { + padding: 0 .24rem; + position: absolute; + left: 0; + top: 0; + z-index: 1; + width: 100%; + height: 0; + overflow-y: auto; + transform: translateY(0); + -webkit-overflow-scrolling: touch; + transition: all .2s ease-in-out; +} + +.m-summary ul, .m-summary ol, .m-summary li { + list-style: none; +} + +.m-summary.active { + height: 100%; + background-color: #fff; + padding: .24rem; + padding-bottom: 0.89rem; + transform: translateY(0.65rem); +} + +.m-summary.active .m-summary-content { + display: block; +} + +.m-summary.active + .m-summary-switch .top { + transform: translateY(10px); +} + +.m-summary.active + .m-summary-switch .bottom { + transform: translateY(-10px); +} + +.m-summary-content { + width: 100%; + height: auto; + display: none; +} + +.m-summary-block { + margin-bottom: .16rem; +} + +.m-summary-block.active > .href { + font-weight: bold; + color: #086fff; +} + +.m-summary-title { + font-size: 16px; + line-height: 2.5; + color: rgba(3, 17, 31, 0.87); + padding-left: 0.08rem; +} + +.m-summary-list { + font-size: 14px; + line-height: 2.5; +} + +.m-summary-list.indent { + padding-left: 1.5em; +} + +.m-summary-list .item { + color: rgba(3, 17, 31, 0.54); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.m-summary-list .item.active { + font-weight: bold; + color: #086fff; +} + +.m-summary-list .href { + color: inherit; + background-color: transparent; + transition: all 0.2s; + display: inline-block; + width: 100%; + border-radius: 4px; + padding-left: 0.08rem; +} + +.m-summary-list .href:hover { + color: #086fff; + background-color: #f1f6fe; +} + +.m-summary-switch { + font-family: 'ydoc'; + position: fixed; + right: .24rem; + bottom: .48rem; + width: .6rem; + height: .6rem; + border-radius: 50%; + color: #086fff; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); + background-color: rgba(3, 17, 31, 0.87); + user-select: none; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column-reverse; + z-index: 1; +} + +.m-summary-switch .top, .m-summary-switch .bottom { + transition: all 0.2s ease-in-out; +} + +@media screen and (min-width: 960px) { + .m-summary { + position: static; + height: 100%; + width: 2.4rem; + padding: .4rem .32rem; + flex: 3rem 0 0; + } + .m-summary-switch { + display: none; + } + .m-summary-content { + display: block; + } +} + +@media screen and (min-width: 1440px) { + .m-summary { + width: 3rem; + } +} + +.m-content { + overflow-y: scroll; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + background-color: #fff; + padding-top: .64rem; + height: 100%; +} + +.m-content-container { + padding: .48rem .25rem 1.2rem; + margin: 0 auto; +} + +.m-content-container .title { + line-height: 1; +} + +.m-content-container.markdown-body { + min-height: calc(100% - 2rem); +} + +.m-paging { + font-size: 16px; +} + +.m-paging-item { + width: 50%; + color: inherit; + transition: color 0.2s; +} + +.m-paging-item:hover { + color: #086fff; +} + +.m-paging-prev { + float: left; +} + +.m-paging-next { + float: right; + text-align: right; +} + +.m-paging:after { + content: ''; + display: block; + clear: both; +} + +.m-paging .href { + display: inline-block; + width: 100%; + padding: .04rem; +} + +@media screen and (min-width: 960px) { + .m-content { + overflow-x: hidden; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } +} + +.m-footer { + background-color: #fff; + padding: .24rem; +} + +.m-footer ul, .m-footer ol, .m-footer li { + list-style: none; +} + +.m-footer-container { + display: flex; + flex-direction: column; + padding: 0; + transition: all 0.2s; +} + +.m-footer-title { + text-align: center; +} + +.m-footer-links { + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.m-footer-links .group { + flex-basis: 30%; + flex-shrink: 0; + margin-bottom: .24rem; +} + +.m-footer-links .title { + font-size: 16px; + line-height: 2; + color: rgba(3, 17, 31, 0.38); +} + +.m-footer .href { + line-height: 1.8; + padding: .3em 0; + color: rgba(3, 17, 31, 0.54); + background-color: #fff; + transition: all 0.2s; +} + +.m-footer .href:hover { + color: #086fff; +} + +.m-footer-title { + color: rgba(3, 17, 31, 0.54); +} + +@media screen and (min-width: 960px) { + .m-footer { + font-size: 16px; + color: rgba(3, 17, 31, 0.54); + padding: .24rem 0rem; + } + .m-footer-container { + margin: .24rem auto; + padding: 0; + } + .m-footer-title { + flex: 1; + padding-top: .25rem; + border-top: solid 1px #999999; + } + .m-footer-links { + width: 90%; + margin: 0px auto; + justify-content: inherit; + flex: 2; + } +} + +.g-home .m-header { + background-color: #fbfbfb; +} + +.g-home .m-section { + position: relative; +} + +.g-home .m-section-container { + max-width: 10.24rem; + margin: 0 auto; + padding: .8rem .32rem; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.g-home .m-section-title { + text-align: center; +} + +.g-home .m-section-title .name { + margin-bottom: .24rem; + font-size: 32px; + position: relative; +} + +.g-home .m-section-title .name:after { + content: ''; + display: block; + position: absolute; + bottom: -.08rem; + left: 50%; + transform: translateX(-50%); + width: .48rem; + height: 3px; + background-color: #1373cc; +} + +.g-home .m-section-title .desc { + font-size: 16px; + color: rgba(3, 17, 31, 0.54); + line-height: 1.6; +} + +.g-home .m-section-banner { + width: 100%; + position: absolute; + z-index: 0; + bottom: 0; + left: 50%; + transform: translateX(-45%); +} + +.g-home .m-section-banner img { + width: 100%; +} + +.g-home .m-section-box { + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} + +.g-home .m-section .btn { + width: 1.2rem; + padding: .14rem; + line-height: .2rem; + font-size: 16px; + color: #086fff; + background-color: #fff; + margin-right: .08rem; + border-radius: 6px; + text-align: center; + user-select: none; + border: 1px solid #086fff; + cursor: pointer; + transition: all 0.2s; + transform: translateY(0); +} + +.g-home .m-section .btn:hover { + color: #fff; + background-color: #086fff; +} + +.g-home .m-section .btn:active { + background-color: #0e5699; +} + +.g-home .m-section .btn.btn-ghost { + border: none; +} + +.g-home .m-section.home { + padding: .56rem .24rem .64rem; + border-bottom: 1px solid rgba(3, 17, 31, 0.14); +} + +.g-home .m-section.home .m-section-container { + padding: .24rem .32rem; +} + +.g-home .m-section.home .m-section-title { + text-align: center; + color: rgba(3, 17, 31, 0.87); + flex: 1; +} + +.g-home .m-section.home .m-section-title .name { + margin-bottom: .16rem; +} + +.g-home .m-section.home .m-section-title .name:after { + display: none; +} + +.g-home .m-section.home .m-section-title .desc { + color: rgba(3, 17, 31, 0.54); +} + +.g-home .m-section.home .m-section-btngroup { + display: flex; + justify-content: center; + padding: .24rem 0 .16rem; + position: relative; + z-index: 1; +} + +.g-home .m-section.home .caption { + font-size: 16px; + color: rgba(3, 17, 31, 0.38); +} + +.g-home .m-section.feature { + background-color: #fbfbfb; + border-bottom: 1px solid rgba(3, 17, 31, 0.14); +} + +.g-home .m-section.feature .item { + flex: 0 0 46%; + margin-bottom: .32rem; +} + +.g-home .m-section.feature .item .title { + font-size: 32px; + margin-bottom: .16rem; +} + +.g-home .m-section.feature .item .desc { + font-size: 16px; + color: rgba(3, 17, 31, 0.54); +} + +@media screen and (min-width: 960px) { + .g-home .m-section.home .m-section-container { + flex-direction: row; + } + .g-home .m-section-title .name { + font-size: 48px; + } + .g-home .m-section-title .desc { + font-size: 20px; + line-height: 1.6; + } + .g-home .m-section-banner { + width: 7.7rem; + } + .g-home .m-section .btn { + margin-right: .16rem; + } + .g-home .m-section.home .m-section-title { + text-align: left; + } + .g-home .m-section.home .m-section-title .name { + font-size: 48px; + } + .g-home .m-section.home .m-section-title .desc { + font-size: 20px; + line-height: 1.6; + } + .g-home .m-section.home .m-section-btngroup { + justify-content: left; + margin-top: .24rem; + } + .g-home .m-footer .m-footer-container { + max-width: 10.24rem; + } +} + +* { + margin: 0; + padding: 0; +} diff --git a/ydoc/ydoc-plugin-search/core.js b/ydoc/ydoc-plugin-search/core.js new file mode 100644 index 0000000..ea37963 --- /dev/null +++ b/ydoc/ydoc-plugin-search/core.js @@ -0,0 +1,104 @@ +$(function() { + var num = 0; + var searchText; + var searchMaxNum = 8; + var releativePath = document.getElementById('releativePath').getAttribute('content') || '' + releativePath = releativePath.trim() + releativePath = releativePath === '' ? '.' : releativePath + + window.ydoc_plugin_search_core = function(text) { + if(!text)return; + var json = cloneObject(window.ydoc_plugin_search_json); + searchText = text; + num = 0; + var result = search(json) + return result; + }; + + function cloneObject(obj) { + if (typeof obj === 'object') { + if (Array.isArray(obj)) { + var newArr = []; + obj.forEach(function(item, index){ + newArr[index] = cloneObject(item) + }) + return newArr; + } else { + var newObj = {}; + for (var key in obj) { + newObj[key] = cloneObject(obj[key]); + } + return newObj; + } + } else { + return obj; + } + } + + + function search(json){ + var result = {} + for(var i in json){ + var data = searchBook(json[i]) + data = filterBook(data); + if(data.length > 0) result[i] = data; + } + return result; + } + + function filterBook(newPages){ + return newPages.filter(function(page){ + if(!page.content && page.children.length === 0){ + return false; + } + return true; + }) + } + + function searchBook(pages){ + var newPages = []; + if(num > searchMaxNum){ + return newPages; + } + for(var i=0, l=pages.length; i< l; i++){ + var page = pages[i], searchPage = null; + if(num > searchMaxNum){ + return newPages; + } + if(page.content && page.content.toLowerCase().indexOf(searchText.toLowerCase()) !== -1){ + num++; + searchPage = { + title: page.title, + content: page.content, + url: releativePath + page.url, + children: [] + }; + }else{ + searchPage = { + title: page.title, + url: releativePath + page.url, + children: [] + }; + } + + newPages.push(searchPage) + + if(page.children && Array.isArray(page.children)){ + for(var j=0, len=page.children.length; j< len; j++){ + var child = page.children[j]; + if(num >= searchMaxNum ){ + return newPages; + } + if(child.content && child.content.toLowerCase().indexOf(searchText.toLowerCase()) !== -1){ + child.url = releativePath + child.url; + searchPage.children.push(child) + num++; + } + } + } + } + return newPages; + } + + +}); diff --git a/ydoc/ydoc-plugin-search/search.css b/ydoc/ydoc-plugin-search/search.css new file mode 100644 index 0000000..b08cec4 --- /dev/null +++ b/ydoc/ydoc-plugin-search/search.css @@ -0,0 +1,118 @@ +.m-search { + position: absolute; + right: .64rem; + top: .32rem; + transform: translateY(-50%); + z-index: 9999; +} +.m-search-result { + display: none; + position: absolute; + width: 100vw; + width: calc(100vw - 32px); + right: -.48rem; + top: .48rem; + background-color: #fff; + border-radius: 4px; + box-shadow: 0 50px 100px rgba(50, 50, 93, 0.1), 0 15px 35px rgba(50, 50, 93, 0.15), 0 5px 15px rgba(0, 0, 0, 0.1); + overflow: hidden; +} +.m-search .highlight { + font-weight: bold; + color: #1890ff; +} +.m-search .icon { + font-family: ydoc; + position: absolute; + left: 16px; + top: 16px; + transform: translate(-50%, -50%); + font-size: 18px; +} +.m-search .input { + width: 72px; + border: none; + outline: none; + height: 32px; + line-height: 32px; + padding: 0 .08rem 0 .32rem; + font-size: 14px; + border-radius: 4px; + transition: all .2s ease-in-out; +} +.m-search .input:focus { + width: 160px; + background-color: #f7f7f7; +} +.m-search .empty { + padding: .32rem .24rem; + text-align: center; +} +.m-search .headline { + padding: .08rem; + background-color: #1890ff; + color: #fff; + font-weight: bold; +} +.m-search .row { + display: flex; + margin: .08rem 0; +} +.m-search .subtitle { + flex: 0 0 25%; + text-align: right; + padding: .04rem .08rem; + background-color: #fff; + cursor: pointer; + transition: all .2s ease-in-out; +} +.m-search .subtitle:hover { + background-color: #f7f7f7; +} +.m-search .content { + flex: 1; + font-weight: bold; + border-left: 1px solid rgba(3, 17, 31, 0.14); + padding: 0; + padding-right: .08rem; +} +.m-search .caption { + padding: .04rem 0; + padding-left: 0.08rem; + background-color: #fff; + cursor: pointer; + transition: all .2s ease-in-out; + display: flex; + flex-direction: column; +} +.m-search .caption:hover { + background-color: #f7f7f7; +} +.m-search .caption.active { + background-color: #f7f7f7; +} +.m-search .caption .desc { + font-weight: normal; +} + +/* 宽度小于 600px 时搜索结果横贯屏幕宽度 */ +@media screen and (min-width: 600px) { + .m-search-result { + position: absolute; + right: 0; + width: 4rem; + } + .m-search .input:focus { + width: 180px; + } +} + +/* PC 端不显示 nav 按钮 因此更靠右侧 */ +@media screen and (min-width: 960px) { + .m-search { + right: .24rem; + } + .m-search .input:focus { + width: 200px; + } +} \ No newline at end of file diff --git a/ydoc/ydoc-plugin-search/search.js b/ydoc/ydoc-plugin-search/search.js new file mode 100644 index 0000000..582b00c --- /dev/null +++ b/ydoc/ydoc-plugin-search/search.js @@ -0,0 +1,127 @@ +$(function(){ + var $searchResult = $('.js-search-result'), + $searchInput = $('.js-input'), + activeIndex = 0; + + // 去除空格 + String.prototype.trim = function () { + return this.replace(/(^\s*)|(\s*$)/g, ''); + }; + + // 判断是否为空对象 + function realObj(obj) { + if (JSON.stringify(obj) === '{}') { + return false; // 如果为空,返回false + } + return true; + } + + // 防抖函数 + function debounce(func, wait) { + var timeout; + return function () { + var context = this, args = arguments; + clearTimeout(timeout); + timeout = setTimeout(function() { + func.apply(context, args); + }, wait); + }; + } + + var highlightTextPrevNum = 6; + var highlightTextNextNum = 20; + // 简化文本内容长度 + function simplifyStrDom(str, val) { + var index = str.indexOf(val); + var startIndex = index > highlightTextPrevNum ? index - highlightTextPrevNum : 0; + var sliceStr = str.slice(startIndex, index + val.length + highlightTextNextNum); + var reg = new RegExp('(' + val + ')', 'gi'); // 搜索的值进行高亮替换时, 忽略大小写 + var addHighlightStr = sliceStr.replace(reg, '' + '$1' + ''); + var ellipsis = (sliceStr.lastIndexOf(val) != -1) || (sliceStr.lastIndexOf(val) > highlightTextNextNum) ? '...' : ''; + return addHighlightStr + ellipsis; + } + + // 隐藏搜索结果框 + // function hideSearchResult() { + // $searchResult.hide(); + // } + + // 监听输入的内容 + $searchInput.on('input', debounce(function(e) { + var val = e.target.value.trim(), + res = window.ydoc_plugin_search_core(val); + + activeIndex = 0; + $(document).off('keydown'); + $searchResult.show(); + if (realObj(res) || val === '') { + var dom = ''; + for (var key in res) { + dom += '
' + key + '
'; + res[key].forEach(function(item) { + var contentDom = ''; + if (item.children.length) { + item.children.forEach(function (i) { + i.title = simplifyStrDom(i.title, val); + i.content = simplifyStrDom(i.content, val); + contentDom += '' + + '
' + i.title + '
' + + '
' + i.content + '
'; + }); + } else { + item.title = simplifyStrDom(item.title, val); + item.content = simplifyStrDom(item.content, val); + contentDom = '' + + '
' + item.title + '
' + + '
' + item.content + '
'; + } + dom += '
' + + '' + item.title + '' + + '
' + contentDom + '
' + + '
'; + }); + } + $searchResult.html(dom); + + var $captions = $('.js-search-result .caption'); + var length = $captions.length; + if ($captions.length) { + $captions[activeIndex].classList.add('active'); + // 监听键盘事件 up: 38, down: 40, enter: 13 + $(document).on('keydown', function (e) { + if (e.keyCode == 38) { + $captions[activeIndex].classList.remove('active'); + activeIndex = (activeIndex + length - 1) % length; + $captions[activeIndex].classList.add('active'); + } else if (e.keyCode == 40) { + $captions[activeIndex].classList.remove('active'); + activeIndex = (activeIndex + 1) % length; + $captions[activeIndex].classList.add('active'); + } else if (e.keyCode == 13) { + $searchResult.hide(); + window.open($captions[activeIndex].href, '_self'); + } + }); + } + // 按下 ESC 键,清空输入框并收起搜索结果框 + $(document).on('keydown', function (e) { + if (e.keyCode == 27) { + $searchInput[0].value = ''; + $searchResult.hide(); + } + }); + } else { + $searchResult.html('
没有找到关键词 ' + val + ' 的搜索结果
') + } + }, 300)); + + $searchResult.on('click', function (e) { + return false; + }) + + $(document).on('click', function(e){ + $searchResult.hide(); + }) + + +}) \ No newline at end of file