├── lin-cms-spring-boot-starter ├── src │ └── main │ │ └── resources │ │ └── application.properties └── pom.xml ├── .github ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── maven.yml ├── lin-cms-spring-boot-autoconfigure ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring.factories │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── talelin │ │ │ └── autoconfigure │ │ │ ├── interfaces │ │ │ ├── BaseResponse.java │ │ │ ├── LoggerResolver.java │ │ │ └── AuthorizeVerifyResolver.java │ │ │ ├── response │ │ │ ├── Success.java │ │ │ ├── Created.java │ │ │ ├── Deleted.java │ │ │ └── Updated.java │ │ │ ├── validator │ │ │ ├── EqualField.java │ │ │ ├── EnumValue.java │ │ │ ├── DateTimeFormat.java │ │ │ └── impl │ │ │ │ ├── EnumValueValidator.java │ │ │ │ ├── DateTimeFormatValidator.java │ │ │ │ └── EqualFieldValidator.java │ │ │ ├── exception │ │ │ ├── FailedException.java │ │ │ ├── NotFoundException.java │ │ │ ├── ForbiddenException.java │ │ │ ├── TokenExpiredException.java │ │ │ ├── TokenInvalidException.java │ │ │ ├── RequestLimitException.java │ │ │ ├── FileTooLargeException.java │ │ │ ├── FileTooManyException.java │ │ │ ├── FileExtensionException.java │ │ │ ├── RefreshFailedException.java │ │ │ ├── AuthenticationException.java │ │ │ ├── AuthorizationException.java │ │ │ ├── DuplicatedException.java │ │ │ ├── MethodNotAllowedException.java │ │ │ ├── ParameterException.java │ │ │ └── HttpException.java │ │ │ ├── bean │ │ │ ├── MetaInfo.java │ │ │ ├── Code.java │ │ │ └── PermissionMetaCollector.java │ │ │ ├── interceptor │ │ │ ├── LogInterceptor.java │ │ │ └── AuthorizeInterceptor.java │ │ │ ├── configuration │ │ │ ├── LinCmsProperties.java │ │ │ └── LinCmsConfiguration.java │ │ │ └── util │ │ │ └── RequestUtil.java │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── talelin │ │ └── autoconfigure │ │ ├── validator │ │ └── impl │ │ │ ├── DateTimeFormatValidatorTest.java │ │ │ └── EqualFieldValidatorTest.java │ │ └── exception │ │ └── ParameterExceptionTest.java └── pom.xml ├── deploy.sh ├── core ├── src │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── talelin │ │ │ └── core │ │ │ ├── annotation │ │ │ ├── Logger.java │ │ │ ├── GroupRequired.java │ │ │ ├── AdminRequired.java │ │ │ ├── PermissionModule.java │ │ │ ├── LoginRequired.java │ │ │ ├── RefreshRequired.java │ │ │ ├── Required.java │ │ │ ├── PermissionMeta.java │ │ │ ├── AdminMeta.java │ │ │ ├── GroupMeta.java │ │ │ └── LoginMeta.java │ │ │ ├── constant │ │ │ └── TokenConstant.java │ │ │ ├── util │ │ │ ├── DateUtil.java │ │ │ ├── AnnotationUtil.java │ │ │ ├── EncryptUtil.java │ │ │ └── BeanUtil.java │ │ │ ├── enumeration │ │ │ └── UserLevel.java │ │ │ ├── token │ │ │ ├── Tokens.java │ │ │ ├── SingleJWT.java │ │ │ └── DoubleJWT.java │ │ │ └── logger │ │ │ └── AdvanceRollingFileAppender.java │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── talelin │ │ └── core │ │ ├── util │ │ └── DateUtilTest.java │ │ └── token │ │ ├── SingleJWTTest.java │ │ └── DoubleJWTTest.java └── pom.xml ├── .gitignore ├── LICENSE ├── README.md └── pom.xml /lin-cms-spring-boot-starter/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 提出问题 3 | about: 关于项目的疑问 4 | --- 5 | 6 | 请详细描述您对本项目的任何问题,我们会在第一时间查阅和解决。 7 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.github.talelin.autoconfigure.configuration.LinCmsConfiguration -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | # deploy script 2 | # deploy to sonatype and maven central 3 | # remember to modify the version of all 4 | 5 | export GPG_TTY=$(tty) 6 | 7 | mvn clean deploy -projects core,lin-cms-spring-boot-autoconfigure,lin-cms-spring-boot-starter -Dmaven.test.skip=true 8 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/Logger.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 行为日志记录 7 | * 8 | * @author pedro@TaleLin 9 | */ 10 | @Target(ElementType.METHOD) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | public @interface Logger { 14 | String template(); 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/GroupRequired.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 分组权限 9 | * 10 | * @author pedro@TaleLin 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | @Required(level = UserLevel.GROUP) 16 | public @interface GroupRequired { 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/AdminRequired.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 管理员权限 9 | * 10 | * @author pedro@TaleLin 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | @Required(level = UserLevel.ADMIN) 16 | public @interface AdminRequired { 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/PermissionModule.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author colorful@TaleLin 7 | * 8 | * 权限模块注解,打在控制器类上,可用于省略 @PermissionMeta 注解的 module 参数 9 | */ 10 | @Target(ElementType.TYPE) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | public @interface PermissionModule { 14 | 15 | String value() default ""; 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/LoginRequired.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | 4 | import io.github.talelin.core.enumeration.UserLevel; 5 | 6 | import java.lang.annotation.*; 7 | 8 | /** 9 | * 登录权限 10 | * 11 | * @author pedro@TaleLin 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | @Required(level = UserLevel.LOGIN) 17 | public @interface LoginRequired { 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/RefreshRequired.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 刷新令牌权限 9 | * 10 | * @author pedro@TaleLin 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | @Required(level = UserLevel.REFRESH) 16 | public @interface RefreshRequired { 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/constant/TokenConstant.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.constant; 2 | 3 | /** 4 | * 令牌相关常量 5 | * 6 | * @author pedro@TaleLin 7 | */ 8 | public class TokenConstant { 9 | 10 | public final static String ACCESS_TYPE = "access"; 11 | 12 | public final static String REFRESH_TYPE = "refresh"; 13 | 14 | public final static String LIN_SCOPE = "lin"; 15 | 16 | public final static String OTHER_SCOPE = "other"; 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/Required.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 表示需要权限 9 | * 10 | * @author pedro@TaleLin 11 | */ 12 | @Target({ElementType.METHOD, ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface Required { 16 | UserLevel level() default UserLevel.TOURIST; 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/util/DateUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.util; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * 日期工具函数 7 | * 8 | * @author pedro@TaleLin 9 | */ 10 | public class DateUtil { 11 | 12 | /** 13 | * 获得过期时间 14 | * 15 | * @param duration 延时时间,单位s 16 | * @return Date 17 | */ 18 | public static Date getDurationDate(long duration) { 19 | long expireTime = System.currentTimeMillis() + duration * 1000; 20 | return new Date(expireTime); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | **/target/ 7 | 8 | **.DS_Store 9 | 10 | ### STS ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBeans 17 | .sts4-cache 18 | 19 | ### IntelliJ IDEA ### 20 | .idea 21 | *.iws 22 | *.iml 23 | *.ipr 24 | 25 | ### NetBeans ### 26 | /nbproject/private/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/ 31 | build/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | assets/ 36 | logs/ 37 | poem/ 38 | latticy 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 提出新特性 3 | about: 对项目的发展提出建议 4 | --- 5 | 6 | CMS 是一个颇为复杂的应用,它需要的东西太多。我们无法涉及到方方面面,因此关于新特性,我们会以讨论的形式来确定这个特性是否去实现,以什么形式实现。 7 | 我们鼓励所有对这个特性感兴趣的人来参与讨论,当然如果你想参与特性的开发那就更好了。 8 | 9 | 如果你实现了一个 feature,并通过了单元测试,请用`git rebase`合并成一条标准的`feat: description`提交,然后向我们的 10 | 项目提 PR,我们会在第一时间审核,并感谢您的参与。 11 | 12 | **请问这个特性跟什么问题相关? 有哪些应用场景?请详细描述。** 13 | 请清晰准确的描述问题的内容,以及真实的场景。 14 | 15 | **请描述一下你想怎么实现这个特性** 16 | 怎么样去实现这个特性?加入核心库?加入工程项目?还是其他方式。 17 | 当然你也可以描述它的具体实现. 18 | 19 | **讨论** 20 | 如果这个特性应用场景非常多,或者非常重要,我们会第一时间去处理。但更多的我们希望更多的人参与讨论,来斟酌它的可行性。 21 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/enumeration/UserLevel.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.enumeration; 2 | 3 | /** 4 | * 用户等级 5 | * 6 | * @author pedro@TaleLin 7 | * @author Juzi@TaleLin 8 | */ 9 | public enum UserLevel { 10 | 11 | /** 12 | * 游客即可访问 13 | */ 14 | TOURIST, 15 | 16 | /** 17 | * 登录才可访问 18 | */ 19 | LOGIN, 20 | 21 | /** 22 | * 登录有权限才可访问 23 | */ 24 | GROUP, 25 | 26 | /** 27 | * 管理员权限 28 | */ 29 | ADMIN, 30 | 31 | /** 32 | * 令牌刷新 33 | */ 34 | REFRESH 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/PermissionMeta.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 路由信息,记录路由权限、模块等信息 7 | * 8 | * @author pedro@TaleLin 9 | * @author colorful@TaleLin 10 | */ 11 | @Target(ElementType.METHOD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | public @interface PermissionMeta { 15 | 16 | String value(); 17 | 18 | @Deprecated 19 | String permission() default ""; 20 | 21 | String module() default ""; 22 | 23 | boolean mount() default true; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/test/java/io/github/talelin/core/util/DateUtilTest.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.util; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Date; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | public class DateUtilTest { 10 | 11 | @Test 12 | public void getDurationDate() { 13 | Date durationDate = DateUtil.getDurationDate(10); 14 | long now = new Date().getTime(); 15 | assertTrue(durationDate.getTime() > now); 16 | assertTrue(durationDate.getTime() - now > 5 * 1000); 17 | assertTrue(durationDate.getTime() - now <= 10 * 1000); 18 | } 19 | } -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/interfaces/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.interfaces; 2 | 3 | /** 4 | * 基础的返回接口 5 | * 必须实现以下三个方法 6 | * 7 | * @author pedro@TaleLin 8 | */ 9 | public interface BaseResponse { 10 | 11 | /** 12 | * 返回的信息 13 | * 14 | * @return 返回的信息 15 | */ 16 | String getMessage(); 17 | 18 | /** 19 | * http 状态码 20 | * 21 | * @return http 状态码 22 | */ 23 | int getHttpCode(); 24 | 25 | /** 26 | * 错误码 27 | * 28 | * @return 错误码 29 | */ 30 | int getCode(); 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/AdminMeta.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * AdminRequired 和 PermissionMeta 融合注解 9 | * 10 | * @author pedro@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | @Required(level = UserLevel.ADMIN) 17 | @Deprecated 18 | public @interface AdminMeta { 19 | 20 | String value() default ""; 21 | 22 | String permission() default ""; 23 | 24 | String module() default ""; 25 | 26 | boolean mount() default true; 27 | 28 | } -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/GroupMeta.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * GroupRequired 和 PermissionMeta 融合注解 9 | * 10 | * @author pedro@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | @Required(level = UserLevel.GROUP) 17 | @Deprecated 18 | public @interface GroupMeta { 19 | 20 | String value() default ""; 21 | 22 | String permission() default ""; 23 | 24 | String module() default ""; 25 | 26 | boolean mount() default true; 27 | 28 | } -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/annotation/LoginMeta.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.annotation; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * LoginRequired 和 PermissionMeta 融合注解 9 | * 10 | * @author pedro@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | @Required(level = UserLevel.LOGIN) 17 | @Deprecated 18 | public @interface LoginMeta { 19 | 20 | String value() default ""; 21 | 22 | String permission() default ""; 23 | 24 | String module() default ""; 25 | 26 | boolean mount() default true; 27 | 28 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 提出一个bug 3 | about: 提出bug帮助我们完善项目 4 | --- 5 | 6 | **描述 bug** 7 | 8 | - 你是如何操作的? 9 | - 发生了什么? 10 | - 你觉得应该出现什么? 11 | 12 | **你使用哪个版本出现该问题?** 13 | 14 | 如果使用`master`,请表明是 master 分支,否则给出具体的版本号 15 | 16 | **如何再现** 17 | 18 | If your bug is deterministic, can you give a minimal reproducing code? 19 | Some bugs are not deterministic. Can you describe with precision in which context it happened? 20 | If this is possible, can you share your code? 21 | 22 | 如果你确定存在这个 bug,你能提供我们一个最小的实现代码吗? 23 | 一些 bug 是不确定,只会在某些条件下触发,你能详细描述一下具体的情况和提供复现的步骤吗? 24 | 当然如果你提供在线的 repo,那就再好不过了。 25 | 26 | 如果你发现了 bug,并修复了它,请用`git rebase`合并成一条标准的`fix: description`提交,然后向我们的 27 | 项目提 PR,我们会在第一时间审核,并感谢您的参与。 28 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/interfaces/LoggerResolver.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.interfaces; 2 | 3 | import io.github.talelin.core.annotation.Logger; 4 | import io.github.talelin.core.annotation.PermissionMeta; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * 行为日志记录 11 | * 12 | * @author pedro@TaleLin 13 | */ 14 | public interface LoggerResolver { 15 | 16 | /** 17 | * 处理 18 | * 19 | * @param meta 路由信息 20 | * @param logger logger 信息 21 | * @param request 请求 22 | * @param response 响应 23 | */ 24 | void handle(PermissionMeta meta, Logger logger, HttpServletRequest request, HttpServletResponse response); 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/util/AnnotationUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.util; 2 | 3 | import io.github.talelin.core.annotation.*; 4 | import io.github.talelin.core.enumeration.UserLevel; 5 | 6 | import java.lang.annotation.Annotation; 7 | 8 | /** 9 | * 注解工具函数 10 | * 11 | * @author pedro@TaleLin 12 | */ 13 | public class AnnotationUtil { 14 | 15 | /*** 16 | * 得到用户等级 17 | * @param annotations 注解 18 | * @return 用户等级 19 | */ 20 | public static UserLevel findRequired(Annotation[] annotations) { 21 | for (Annotation annotation : annotations) { 22 | Class aClass = annotation.annotationType(); 23 | Required required = aClass.getAnnotation(Required.class); 24 | if (required != null) { 25 | return required.level(); 26 | } 27 | } 28 | return UserLevel.TOURIST; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test_eight: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up JDK 1.8 13 | uses: actions/setup-java@v1 14 | with: 15 | java-version: 1.8 16 | - name: Build with Maven 17 | run: mvn test 18 | 19 | test_nine: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v1 25 | - name: Set up JDK 9 26 | uses: actions/setup-java@v1 27 | with: 28 | java-version: 9 29 | - name: Build with Maven 30 | run: mvn test 31 | 32 | test_eleven: 33 | 34 | runs-on: ubuntu-latest 35 | 36 | steps: 37 | - uses: actions/checkout@v1 38 | - name: Set up JDK 11 39 | uses: actions/setup-java@v1 40 | with: 41 | java-version: 11 42 | - name: Build with Maven 43 | run: mvn test 44 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/response/Success.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.response; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import io.github.talelin.autoconfigure.interfaces.BaseResponse; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * 成功响应 9 | * 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | public class Success implements BaseResponse { 14 | 15 | protected String message = Code.SUCCESS.getDescription(); 16 | 17 | protected int code = Code.SUCCESS.getCode(); 18 | 19 | protected int httpCode = HttpStatus.OK.value(); 20 | 21 | 22 | public Success(String message) { 23 | this.message = message; 24 | } 25 | 26 | public Success() { 27 | } 28 | 29 | @Override 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | @Override 35 | public int getCode() { 36 | return code; 37 | } 38 | 39 | @Override 40 | public int getHttpCode() { 41 | return httpCode; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/response/Created.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.response; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import io.github.talelin.autoconfigure.interfaces.BaseResponse; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * 新建响应 9 | * 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | public class Created implements BaseResponse { 14 | 15 | protected String message = Code.CREATED.getDescription(); 16 | 17 | protected int code = Code.CREATED.getCode(); 18 | 19 | protected int httpCode = HttpStatus.CREATED.value(); 20 | 21 | 22 | public Created(String message) { 23 | this.message = message; 24 | } 25 | 26 | public Created() { 27 | } 28 | 29 | @Override 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | @Override 35 | public int getCode() { 36 | return code; 37 | } 38 | 39 | @Override 40 | public int getHttpCode() { 41 | return httpCode; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/response/Deleted.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.response; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import io.github.talelin.autoconfigure.interfaces.BaseResponse; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * 删除相应 9 | * 10 | * @author colorful@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | public class Deleted implements BaseResponse { 14 | 15 | protected String message = Code.UPDATED.getDescription(); 16 | 17 | protected int code = Code.DELETED.getCode(); 18 | 19 | protected int httpCode = HttpStatus.OK.value(); 20 | 21 | 22 | public Deleted(String message) { 23 | this.message = message; 24 | } 25 | 26 | public Deleted() { 27 | } 28 | 29 | @Override 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | @Override 35 | public int getCode() { 36 | return code; 37 | } 38 | 39 | @Override 40 | public int getHttpCode() { 41 | return httpCode; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/response/Updated.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.response; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import io.github.talelin.autoconfigure.interfaces.BaseResponse; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * 更新相应 9 | * 10 | * @author colorful@TaleLin 11 | * @author Juzi@TaleLin 12 | */ 13 | public class Updated implements BaseResponse { 14 | 15 | protected String message = Code.UPDATED.getDescription(); 16 | 17 | protected int code = Code.UPDATED.getCode(); 18 | 19 | protected int httpCode = HttpStatus.OK.value(); 20 | 21 | 22 | public Updated(String message) { 23 | this.message = message; 24 | } 25 | 26 | public Updated() { 27 | } 28 | 29 | @Override 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | @Override 35 | public int getCode() { 36 | return code; 37 | } 38 | 39 | @Override 40 | public int getHttpCode() { 41 | return httpCode; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/token/Tokens.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.token; 2 | 3 | /** 4 | * 令牌数据 5 | * 6 | * @author pedro@TaleLin 7 | * @author Juzi@TaleLin 8 | */ 9 | public class Tokens { 10 | 11 | public Tokens(String accessToken, String refreshToken) { 12 | this.accessToken = accessToken; 13 | this.refreshToken = refreshToken; 14 | } 15 | 16 | private String accessToken; 17 | 18 | private String refreshToken; 19 | 20 | public String getAccessToken() { 21 | return accessToken; 22 | } 23 | 24 | public void setAccessToken(String accessToken) { 25 | this.accessToken = accessToken; 26 | } 27 | 28 | public String getRefreshToken() { 29 | return refreshToken; 30 | } 31 | 32 | public void setRefreshToken(String refreshToken) { 33 | this.refreshToken = refreshToken; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Tokens{" + 39 | "accessToken='" + accessToken + '\'' + 40 | ", refreshToken='" + refreshToken + '\'' + 41 | '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/validator/EqualField.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator; 2 | 3 | import io.github.talelin.autoconfigure.validator.impl.EqualFieldValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.Target; 10 | 11 | import static java.lang.annotation.ElementType.TYPE; 12 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 13 | 14 | /** 15 | * 比较两个属性是否相等 16 | * 17 | * @author pedro@TaleLin 18 | * @author Juzi@TaleLin 19 | */ 20 | @Documented 21 | @Target(TYPE) 22 | @Retention(RUNTIME) 23 | @Constraint(validatedBy = EqualFieldValidator.class) 24 | public @interface EqualField { 25 | 26 | /** 27 | * 源属性 28 | */ 29 | String srcField(); 30 | 31 | /** 32 | * 目标属性 33 | */ 34 | String dstField(); 35 | 36 | String message() default "the two fields must be equal"; 37 | 38 | Class[] groups() default {}; 39 | 40 | Class[] payload() default {}; 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/util/EncryptUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.util; 2 | 3 | import com.amdelamar.jhash.Hash; 4 | import com.amdelamar.jhash.algorithms.Type; 5 | import com.amdelamar.jhash.exception.InvalidHashException; 6 | 7 | /** 8 | * 加密工具类 9 | * 10 | * @author pedro@TaleLin 11 | */ 12 | public class EncryptUtil { 13 | 14 | /** 15 | * 设置密文密码 16 | * 17 | * @param password 原始密码 18 | * @return 加密密码 19 | */ 20 | public static String encrypt(String password) { 21 | char[] chars = password.toCharArray(); 22 | return Hash.password(chars).algorithm(Type.PBKDF2_SHA256).create(); 23 | } 24 | 25 | /** 26 | * 验证加密密码 27 | * 28 | * @param encryptedPassword 密文密码 29 | * @param plainPassword 明文密码 30 | * @return 验证是否成功 31 | */ 32 | public static boolean verify(String encryptedPassword, String plainPassword) { 33 | char[] chars = plainPassword.toCharArray(); 34 | try { 35 | return Hash.password(chars).algorithm(Type.PBKDF2_SHA256).verify(encryptedPassword); 36 | } catch (InvalidHashException e) { 37 | return false; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/validator/EnumValue.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator; 2 | 3 | import io.github.talelin.autoconfigure.validator.impl.EnumValueValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.Target; 10 | 11 | import static java.lang.annotation.ElementType.FIELD; 12 | import static java.lang.annotation.ElementType.TYPE_PARAMETER; 13 | import static java.lang.annotation.ElementType.TYPE_USE; 14 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 15 | 16 | /** 17 | * 枚举校验 18 | * 校验的值必须为 target (枚举类)中的一项 19 | * 20 | * @author pedro@TaleLin 21 | * @author jUZI@TaleLin 22 | */ 23 | @Documented 24 | @Retention(RUNTIME) 25 | @Target({FIELD, TYPE_USE, TYPE_PARAMETER}) 26 | @Constraint(validatedBy = EnumValueValidator.class) 27 | public @interface EnumValue { 28 | 29 | /** 30 | * 目标值,必须是一个枚举类 31 | */ 32 | Class> target(); 33 | 34 | String message() default "value must in enum"; 35 | 36 | Class[] groups() default {}; 37 | 38 | Class[] payload() default {}; 39 | } 40 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/validator/DateTimeFormat.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator; 2 | 3 | import io.github.talelin.autoconfigure.validator.impl.DateTimeFormatValidator; 4 | 5 | import javax.validation.Constraint; 6 | import javax.validation.Payload; 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | import static java.lang.annotation.ElementType.FIELD; 14 | import static java.lang.annotation.ElementType.TYPE_PARAMETER; 15 | import static java.lang.annotation.ElementType.TYPE_USE; 16 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 17 | 18 | /** 19 | * 日期格式校验器 20 | * 对 String 类型进行校验,是否匹配给定的模式 21 | * 默认校验日期格式 yyyy-MM-dd HH:mm:ss 22 | * 23 | * @author pedro@TaleLin 24 | * @author Juzi@TaleLin 25 | */ 26 | @Documented 27 | @Retention(RUNTIME) 28 | @Target({FIELD, TYPE_USE, TYPE_PARAMETER}) 29 | @Constraint(validatedBy = DateTimeFormatValidator.class) 30 | public @interface DateTimeFormat { 31 | 32 | String message() default "date pattern invalid"; 33 | 34 | String pattern() default "yyyy-MM-dd HH:mm:ss"; 35 | 36 | Class[] groups() default {}; 37 | 38 | Class[] payload() default {}; 39 | } 40 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/FailedException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 失败异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class FailedException extends HttpException { 14 | 15 | private static final long serialVersionUID = -661265124636854465L; 16 | 17 | protected int code = Code.FAIL.getCode(); 18 | 19 | protected int httpCode = HttpStatus.INTERNAL_SERVER_ERROR.value(); 20 | 21 | public FailedException() { 22 | super(Code.FAIL.getCode(), Code.FAIL.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public FailedException(String message) { 27 | super(message); 28 | } 29 | 30 | public FailedException(int code) { 31 | super(code, Code.FAIL.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public FailedException(String message, int code) { 38 | super(message, code); 39 | this.code = code; 40 | } 41 | 42 | public FailedException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | @Override 53 | public int getHttpCode() { 54 | return httpCode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 资源不存在异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class NotFoundException extends HttpException { 14 | 15 | private static final long serialVersionUID = 3147792856922208240L; 16 | 17 | private int code = Code.NOT_FOUND.getCode(); 18 | 19 | private int httpCode = HttpStatus.NOT_FOUND.value(); 20 | 21 | public NotFoundException() { 22 | super(Code.NOT_FOUND.getCode(), Code.NOT_FOUND.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public NotFoundException(String message) { 27 | super(message); 28 | } 29 | 30 | public NotFoundException(int code) { 31 | super(code, Code.NOT_FOUND.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public NotFoundException(String message, int code) { 38 | super(message, code); 39 | this.code = code; 40 | } 41 | 42 | public NotFoundException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | @Override 53 | public int getHttpCode() { 54 | return httpCode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/ForbiddenException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 禁止操作异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class ForbiddenException extends HttpException { 14 | 15 | private static final long serialVersionUID = 865571132800721223L; 16 | 17 | protected int code = Code.FORBIDDEN.getCode(); 18 | 19 | protected int httpCode = HttpStatus.FORBIDDEN.value(); 20 | 21 | 22 | public ForbiddenException() { 23 | super(Code.FORBIDDEN.getCode(), Code.FORBIDDEN.getDescription()); 24 | super.ifDefaultMessage=true; 25 | } 26 | 27 | public ForbiddenException(int code) { 28 | super(code, Code.FORBIDDEN.getDescription()); 29 | this.code = code; 30 | super.ifDefaultMessage=true; 31 | } 32 | 33 | @Deprecated 34 | public ForbiddenException(String message, int code) { 35 | super(message, code); 36 | this.code = code; 37 | } 38 | 39 | public ForbiddenException(int code, String message) { 40 | super(code, message); 41 | this.code = code; 42 | } 43 | 44 | public ForbiddenException(String message) { 45 | super(message); 46 | } 47 | 48 | @Override 49 | public int getCode() { 50 | return code; 51 | } 52 | 53 | @Override 54 | public int getHttpCode() { 55 | return httpCode; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/test/java/io/github/talelin/autoconfigure/validator/impl/DateTimeFormatValidatorTest.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator.impl; 2 | 3 | import io.github.talelin.autoconfigure.validator.DateTimeFormat; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.Validation; 8 | import java.util.Set; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 12 | 13 | /** 14 | * DateTimeFormatValidator 单元测试 15 | * 16 | * @author Juzi@TaleLin 17 | * @date 2020-05-25 18 | */ 19 | class DateTimeFormatValidatorTest { 20 | static class TestData { 21 | @DateTimeFormat 22 | public String datetime; 23 | } 24 | 25 | /** 26 | * 测试校验通过 27 | */ 28 | @Test 29 | public void testIsValidTrue() { 30 | TestData testData = new TestData(); 31 | testData.datetime = "2020-05-25 19:37:48"; 32 | 33 | Set> validate = 34 | Validation.buildDefaultValidatorFactory().getValidator().validate(testData); 35 | assertEquals(0, validate.size()); 36 | } 37 | 38 | /** 39 | * 测试校验不通过 40 | */ 41 | @Test 42 | public void testIsValidFalse() { 43 | TestData testData = new TestData(); 44 | testData.datetime = "2020/05/25 19:37:48"; 45 | 46 | Set> validate = 47 | Validation.buildDefaultValidatorFactory().getValidator().validate(testData); 48 | assertNotEquals(0, validate.size()); 49 | } 50 | } -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/TokenExpiredException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 令牌过期异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class TokenExpiredException extends HttpException { 14 | 15 | private static final long serialVersionUID = -562886931687729013L; 16 | 17 | protected int code = Code.TOKEN_EXPIRED.getCode(); 18 | 19 | protected int httpCode = HttpStatus.UNAUTHORIZED.value(); 20 | 21 | public TokenExpiredException() { 22 | super(Code.TOKEN_EXPIRED.getCode(), Code.TOKEN_EXPIRED.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public TokenExpiredException(String message) { 27 | super(message); 28 | } 29 | 30 | public TokenExpiredException(int code) { 31 | super(code, Code.TOKEN_EXPIRED.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public TokenExpiredException(String message, int code) { 38 | super(message, code); 39 | this.code = code; 40 | } 41 | 42 | public TokenExpiredException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | @Override 53 | public int getHttpCode() { 54 | return httpCode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/TokenInvalidException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 令牌无效异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class TokenInvalidException extends HttpException { 14 | 15 | private static final long serialVersionUID = -7844470320210708005L; 16 | 17 | protected int code = Code.TOKEN_INVALID.getCode(); 18 | 19 | protected int httpCode = HttpStatus.UNAUTHORIZED.value(); 20 | 21 | public TokenInvalidException() { 22 | super(Code.TOKEN_INVALID.getCode(), Code.TOKEN_INVALID.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public TokenInvalidException(String message) { 27 | super(message); 28 | } 29 | 30 | public TokenInvalidException(int code) { 31 | super(code, Code.TOKEN_INVALID.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public TokenInvalidException(String message, int code) { 38 | super(message, code); 39 | this.code = code; 40 | } 41 | 42 | public TokenInvalidException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | @Override 53 | public int getHttpCode() { 54 | return httpCode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/RequestLimitException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 请求过多异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class RequestLimitException extends HttpException { 14 | 15 | private static final long serialVersionUID = 1909144765577512625L; 16 | 17 | protected int code = Code.REQUEST_LIMIT.getCode(); 18 | 19 | protected int httpCode = HttpStatus.TOO_MANY_REQUESTS.value(); 20 | 21 | public RequestLimitException() { 22 | super(Code.REQUEST_LIMIT.getCode(), Code.REQUEST_LIMIT.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public RequestLimitException(String message) { 27 | super(message); 28 | } 29 | 30 | public RequestLimitException(int code) { 31 | super(code, Code.REQUEST_LIMIT.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public RequestLimitException(String message, int code) { 38 | super(message, code); 39 | this.code = code; 40 | } 41 | 42 | public RequestLimitException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | @Override 53 | public int getHttpCode() { 54 | return httpCode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/FileTooLargeException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 文件太大异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class FileTooLargeException extends HttpException { 14 | 15 | private static final long serialVersionUID = -3845807826439492213L; 16 | 17 | protected int code = Code.FILE_TOO_LARGE.getCode(); 18 | 19 | protected int httpCode = HttpStatus.PAYLOAD_TOO_LARGE.value(); 20 | 21 | public FileTooLargeException() { 22 | super(Code.FILE_TOO_LARGE.getCode(), Code.FILE_TOO_LARGE.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public FileTooLargeException(String message) { 27 | super(message); 28 | } 29 | 30 | public FileTooLargeException(int code) { 31 | super(code, Code.FILE_TOO_LARGE.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public FileTooLargeException(String message, int code) { 38 | super(message, code); 39 | this.code = code; 40 | } 41 | 42 | public FileTooLargeException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | @Override 53 | public int getHttpCode() { 54 | return httpCode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/FileTooManyException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 文件太多异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class FileTooManyException extends HttpException { 14 | 15 | private static final long serialVersionUID = -3189291002817434249L; 16 | 17 | protected int code = Code.FILE_TOO_MANY.getCode(); 18 | 19 | protected int httpCode = HttpStatus.PAYLOAD_TOO_LARGE.value(); 20 | 21 | 22 | public FileTooManyException() { 23 | super(Code.FILE_TOO_MANY.getCode(), Code.FILE_TOO_MANY.getDescription()); 24 | super.ifDefaultMessage=true; 25 | } 26 | 27 | public FileTooManyException(String message) { 28 | super(message); 29 | } 30 | 31 | public FileTooManyException(int code) { 32 | super(code, Code.FILE_TOO_MANY.getDescription()); 33 | this.code = code; 34 | super.ifDefaultMessage=true; 35 | } 36 | 37 | 38 | @Deprecated 39 | public FileTooManyException(String message, int code) { 40 | super(message, code); 41 | this.code = code; 42 | } 43 | 44 | public FileTooManyException(int code, String message) { 45 | super(code, message); 46 | this.code = code; 47 | } 48 | 49 | @Override 50 | public int getCode() { 51 | return code; 52 | } 53 | 54 | @Override 55 | public int getHttpCode() { 56 | return httpCode; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/FileExtensionException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 文件扩展异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class FileExtensionException extends HttpException { 14 | 15 | private static final long serialVersionUID = 7328828500425088567L; 16 | 17 | protected int code = Code.FILE_EXTENSION.getCode(); 18 | 19 | protected int httpCode = HttpStatus.NOT_ACCEPTABLE.value(); 20 | 21 | public FileExtensionException() { 22 | super(Code.FILE_EXTENSION.getCode(), Code.FILE_EXTENSION.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public FileExtensionException(String message) { 27 | super(message); 28 | } 29 | 30 | 31 | public FileExtensionException(int code) { 32 | super(code, Code.FILE_EXTENSION.getDescription()); 33 | this.code = code; 34 | super.ifDefaultMessage=true; 35 | } 36 | 37 | @Deprecated 38 | public FileExtensionException(String message, int code) { 39 | super(message, code); 40 | this.code = code; 41 | } 42 | 43 | public FileExtensionException(int code, String message) { 44 | super(code, message); 45 | this.code = code; 46 | } 47 | 48 | @Override 49 | public int getCode() { 50 | return code; 51 | } 52 | 53 | @Override 54 | public int getHttpCode() { 55 | return httpCode; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/RefreshFailedException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | 4 | import io.github.talelin.autoconfigure.bean.Code; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * 刷新令牌失败异常 9 | * 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | * @author colorful@TaleLin 13 | */ 14 | public class RefreshFailedException extends HttpException { 15 | 16 | private static final long serialVersionUID = -8626126405157905110L; 17 | 18 | protected int code = Code.REFRESH_FAILED.getCode(); 19 | 20 | protected int httpCode = HttpStatus.UNAUTHORIZED.value(); 21 | 22 | public RefreshFailedException() { 23 | super(Code.REFRESH_FAILED.getCode(), Code.REFRESH_FAILED.getDescription()); 24 | super.ifDefaultMessage=true; 25 | } 26 | 27 | public RefreshFailedException(String message) { 28 | super(message); 29 | } 30 | 31 | public RefreshFailedException(int code) { 32 | super(code, Code.REFRESH_FAILED.getDescription()); 33 | this.code = code; 34 | super.ifDefaultMessage=true; 35 | } 36 | 37 | @Deprecated 38 | public RefreshFailedException(String message, int code) { 39 | super(message, code); 40 | this.code = code; 41 | } 42 | 43 | public RefreshFailedException(int code, String message) { 44 | super(code, message); 45 | this.code = code; 46 | } 47 | 48 | @Override 49 | public int getCode() { 50 | return code; 51 | } 52 | 53 | @Override 54 | public int getHttpCode() { 55 | return httpCode; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 授权异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class AuthenticationException extends HttpException { 14 | 15 | private static final long serialVersionUID = -222891683232481602L; 16 | 17 | protected int httpCode = HttpStatus.UNAUTHORIZED.value(); 18 | 19 | protected int code = Code.UN_AUTHENTICATION.getCode(); 20 | 21 | public AuthenticationException() { 22 | super(Code.UN_AUTHENTICATION.getCode(), Code.UN_AUTHENTICATION.getDescription()); 23 | super.ifDefaultMessage = true; 24 | } 25 | 26 | public AuthenticationException(String message) { 27 | super(message); 28 | } 29 | 30 | public AuthenticationException(int code) { 31 | super(code, Code.UN_AUTHENTICATION.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage = true; 34 | } 35 | 36 | @Deprecated 37 | public AuthenticationException(String message, int code) { 38 | super(code, message); 39 | this.code = code; 40 | } 41 | 42 | public AuthenticationException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getHttpCode() { 49 | return httpCode; 50 | } 51 | 52 | @Override 53 | public int getCode() { 54 | return code; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/AuthorizationException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | 7 | /** 8 | * 认证异常 9 | * 10 | * @author pedro@TaleLin 11 | * @author Juzi@TaleLin 12 | * @author colorful@TaleLin 13 | */ 14 | public class AuthorizationException extends HttpException { 15 | 16 | private static final long serialVersionUID = -432605618235404747L; 17 | 18 | protected int httpCode = HttpStatus.UNAUTHORIZED.value(); 19 | 20 | protected int code = Code.UN_AUTHORIZATION.getCode(); 21 | 22 | public AuthorizationException() { 23 | super(Code.UN_AUTHORIZATION.getCode(), Code.UN_AUTHORIZATION.getDescription()); 24 | super.ifDefaultMessage=true; 25 | } 26 | 27 | public AuthorizationException(String message) { 28 | super(message); 29 | } 30 | 31 | public AuthorizationException(int code) { 32 | super(code, Code.UN_AUTHORIZATION.getDescription()); 33 | this.code = code; 34 | super.ifDefaultMessage=true; 35 | } 36 | 37 | @Deprecated 38 | public AuthorizationException(String message, int code) { 39 | super(message, code); 40 | this.code = code; 41 | } 42 | 43 | public AuthorizationException(int code, String message) { 44 | super(code, message); 45 | this.code = code; 46 | } 47 | 48 | 49 | 50 | @Override 51 | public int getHttpCode() { 52 | return httpCode; 53 | } 54 | 55 | @Override 56 | public int getCode() { 57 | return code; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/DuplicatedException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 字段重复 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class DuplicatedException extends HttpException { 14 | 15 | private static final long serialVersionUID = -6459429029468050951L; 16 | 17 | protected int code = Code.DUPLICATED.getCode(); 18 | 19 | protected int httpCode = HttpStatus.BAD_REQUEST.value(); 20 | 21 | public DuplicatedException(String message) { 22 | super(message); 23 | } 24 | 25 | public DuplicatedException() { 26 | super(Code.DUPLICATED.getCode(), Code.DUPLICATED.getDescription()); 27 | super.ifDefaultMessage=true; 28 | } 29 | 30 | public DuplicatedException(int code) { 31 | super(Code.DUPLICATED.getCode(), Code.DUPLICATED.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public DuplicatedException(String message, int code) { 38 | super(message, Code.DUPLICATED.getCode()); 39 | this.code = code; 40 | } 41 | 42 | 43 | public DuplicatedException(int code, String message) { 44 | super(Code.DUPLICATED.getCode(), message); 45 | this.code = code; 46 | } 47 | 48 | @Override 49 | public int getCode() { 50 | return code; 51 | } 52 | 53 | @Override 54 | public int getHttpCode() { 55 | return httpCode; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/MethodNotAllowedException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | /** 7 | * 方法不允许异常 8 | * 9 | * @author pedro@TaleLin 10 | * @author Juzi@TaleLin 11 | * @author colorful@TaleLin 12 | */ 13 | public class MethodNotAllowedException extends HttpException { 14 | 15 | private static final long serialVersionUID = 1223018751542741014L; 16 | 17 | protected int code = Code.METHOD_NOT_ALLOWED.getCode(); 18 | 19 | protected int httpCode = HttpStatus.METHOD_NOT_ALLOWED.value(); 20 | 21 | public MethodNotAllowedException() { 22 | super(Code.METHOD_NOT_ALLOWED.getCode(), Code.METHOD_NOT_ALLOWED.getDescription()); 23 | super.ifDefaultMessage=true; 24 | } 25 | 26 | public MethodNotAllowedException(String message) { 27 | super(message); 28 | } 29 | 30 | public MethodNotAllowedException(int code) { 31 | super(code, Code.METHOD_NOT_ALLOWED.getDescription()); 32 | this.code = code; 33 | super.ifDefaultMessage=true; 34 | } 35 | 36 | @Deprecated 37 | public MethodNotAllowedException(String message, int code) { 38 | super(message, code); 39 | this.code = code; 40 | } 41 | 42 | public MethodNotAllowedException(int code, String message) { 43 | super(code, message); 44 | this.code = code; 45 | } 46 | 47 | @Override 48 | public int getCode() { 49 | return code; 50 | } 51 | 52 | @Override 53 | public int getHttpCode() { 54 | return httpCode; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/bean/MetaInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.bean; 2 | 3 | import io.github.talelin.core.enumeration.UserLevel; 4 | 5 | /** 6 | * @author pedro@TaleLin 7 | */ 8 | public class MetaInfo { 9 | 10 | private String permission; 11 | private String module; 12 | private String identity; 13 | private UserLevel userLevel; 14 | 15 | public MetaInfo(String permission, String module, String identity, UserLevel userLevel) { 16 | this.permission = permission; 17 | this.module = module; 18 | this.identity = identity; 19 | this.userLevel = userLevel; 20 | } 21 | 22 | public String getPermission() { 23 | return permission; 24 | } 25 | 26 | public void setPermission(String permission) { 27 | this.permission = permission; 28 | } 29 | 30 | public String getModule() { 31 | return module; 32 | } 33 | 34 | public void setModule(String module) { 35 | this.module = module; 36 | } 37 | 38 | public String getIdentity() { 39 | return identity; 40 | } 41 | 42 | public void setIdentity(String identity) { 43 | this.identity = identity; 44 | } 45 | 46 | public UserLevel getUserLevel() { 47 | return userLevel; 48 | } 49 | 50 | public void setUserLevel(UserLevel userLevel) { 51 | this.userLevel = userLevel; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "MetaInfo{" + 57 | "permission='" + permission + '\'' + 58 | ", module='" + module + '\'' + 59 | ", identity='" + identity + '\'' + 60 | ", userLevel=" + userLevel + 61 | '}'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/validator/impl/EnumValueValidator.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator.impl; 2 | 3 | import io.github.talelin.autoconfigure.validator.EnumValue; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.validation.ConstraintValidator; 8 | import javax.validation.ConstraintValidatorContext; 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * 枚举校验器 13 | * 14 | * @author pedro@TaleLin 15 | * @author Juzi@TaleLin 16 | */ 17 | public class EnumValueValidator implements ConstraintValidator { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(EnumValueValidator.class); 20 | 21 | /** 22 | * 枚举类 23 | */ 24 | private Class cls; 25 | 26 | @Override 27 | public void initialize(EnumValue constraintAnnotation) { 28 | cls = constraintAnnotation.target(); 29 | } 30 | 31 | /** 32 | * 校验 33 | * 34 | * @param value 传入值 35 | * @param context 上下文 36 | * @return 是否成功 37 | */ 38 | @Override 39 | public boolean isValid(Object value, ConstraintValidatorContext context) { 40 | if (value == null) { 41 | return true; 42 | } 43 | 44 | try { 45 | Object[] objs = cls.getEnumConstants(); 46 | Method method = cls.getMethod("getValue"); 47 | for (Object obj : objs) { 48 | Object val = method.invoke(obj); 49 | if (val.equals(value)) { 50 | return true; 51 | } 52 | } 53 | return false; 54 | } catch (Exception e) { 55 | log.warn("EnumValue 校验异常", e); 56 | return false; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/validator/impl/DateTimeFormatValidator.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator.impl; 2 | 3 | import io.github.talelin.autoconfigure.validator.DateTimeFormat; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.validation.ConstraintValidator; 8 | import javax.validation.ConstraintValidatorContext; 9 | import java.text.SimpleDateFormat; 10 | 11 | /** 12 | * 日期校验器实现 13 | * 14 | * @author pedro@TaleLin 15 | * @author Juzi@TaleLin 16 | */ 17 | public class DateTimeFormatValidator implements ConstraintValidator { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(DateTimeFormatValidator.class); 20 | 21 | private DateTimeFormat dateTimeFormat; 22 | 23 | @Override 24 | public void initialize(DateTimeFormat dateTime) { 25 | this.dateTimeFormat = dateTime; 26 | } 27 | 28 | /** 29 | * 校验 30 | * 31 | * @param value 传入数据 32 | * @param context 上下文 33 | * @return 是否成功 34 | */ 35 | @Override 36 | public boolean isValid(String value, ConstraintValidatorContext context) { 37 | // value 为 null,校验通过 38 | if (value == null) { 39 | return true; 40 | } 41 | 42 | String format = dateTimeFormat.pattern(); 43 | 44 | // 长度不同,则肯定格式不匹配,校验不通过 45 | if (value.length() != format.length()) { 46 | return false; 47 | } 48 | 49 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); 50 | try { 51 | simpleDateFormat.parse(value); 52 | return true; 53 | } catch (Exception e) { 54 | log.warn("DateTimeFormat 校验异常", e); 55 | return false; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/interceptor/LogInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.interceptor; 2 | 3 | import io.github.talelin.autoconfigure.interfaces.LoggerResolver; 4 | import io.github.talelin.core.annotation.Logger; 5 | import io.github.talelin.core.annotation.PermissionMeta; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.method.HandlerMethod; 8 | import org.springframework.web.servlet.ModelAndView; 9 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.lang.reflect.Method; 14 | 15 | /** 16 | * 行为日志拦截器 17 | * 18 | * @author pedro@TaleLin 19 | * @author Juzi@TaleLin 20 | */ 21 | public class LogInterceptor extends HandlerInterceptorAdapter { 22 | 23 | @Autowired 24 | private LoggerResolver loggerResolver; 25 | 26 | /** 27 | * 后置处理 28 | * 29 | * @param request 请求 30 | * @param response 响应 31 | * @param handler 处理器 32 | * @param modelAndView 视图 33 | */ 34 | @Override 35 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { 36 | // 记录日志 37 | if (handler instanceof HandlerMethod) { 38 | HandlerMethod handlerMethod = (HandlerMethod) handler; 39 | Method method = handlerMethod.getMethod(); 40 | Logger logger = method.getAnnotation(Logger.class); 41 | if (logger != null) { 42 | PermissionMeta meta = method.getAnnotation(PermissionMeta.class); 43 | // parse template and extract properties from request,response and modelAndView 44 | loggerResolver.handle(meta, logger, request, response); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/bean/Code.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.bean; 2 | 3 | 4 | /** 5 | * 消息码 6 | * 7 | * @author pedro@TaleLin 8 | * @author colorful@TaleLin 9 | */ 10 | public enum Code { 11 | 12 | SUCCESS(0, "OK", "成功"), 13 | 14 | CREATED(1, "Created", "创建成功"), 15 | 16 | UPDATED(2, "Updated", "更新成功"), 17 | 18 | DELETED(3, "Deleted", "删除成功"), 19 | 20 | FAIL(10200, "Failed", "失败"), 21 | 22 | UN_AUTHORIZATION(10000, "Authorization Failed", "认证失败"), 23 | 24 | UN_AUTHENTICATION(10010, "Authentication Failed", "授权失败"), 25 | 26 | NOT_FOUND(10020, "Not Found", "资源不存在"), 27 | 28 | PARAMETER_ERROR(10030, "Parameters Error", "参数错误"), 29 | 30 | TOKEN_INVALID(10040, "Token Invalid", "令牌失效"), 31 | 32 | TOKEN_EXPIRED(10050, "Token Expired", "令牌过期"), 33 | 34 | DUPLICATED(10060, "Duplicated", "字段重复"), 35 | 36 | INTERNAL_SERVER_ERROR(9999, "Internal Server Error", "服务器未知错误"), 37 | 38 | FORBIDDEN(10070, "Forbidden", "禁止操作"), 39 | 40 | METHOD_NOT_ALLOWED(10080, "Method Not Allowed", "请求方法不允许"), 41 | 42 | REFRESH_FAILED(10100, "Get Refresh Token Failed", "刷新令牌获取失败"), 43 | 44 | FILE_TOO_LARGE(10110, "File Too Large", "文件体积过大"), 45 | 46 | FILE_TOO_MANY(10120, "File Too Many", "文件数量过多"), 47 | 48 | FILE_EXTENSION(10130, "File Extension Not Allowed", "文件扩展名不符合规范"), 49 | 50 | REQUEST_LIMIT(10140, "Too Many Requests", "请求过于频繁,请稍后重试"); 51 | 52 | private int code; 53 | 54 | private String description; 55 | 56 | private String zhDescription; 57 | 58 | Code(int code, String description, String zhDescription) { 59 | this.code = code; 60 | this.description = description; 61 | this.zhDescription = zhDescription; 62 | } 63 | 64 | public int getCode() { 65 | return code; 66 | } 67 | 68 | public String getDescription() { 69 | return description; 70 | } 71 | 72 | public String getZhDescription() { 73 | return zhDescription; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/configuration/LinCmsProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.configuration; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * lin-cms 配置属性 7 | * 8 | * @author pedro@TaleLin 9 | * @author Juzi@TaleLin 10 | */ 11 | @ConfigurationProperties("lin.cms") 12 | public class LinCmsProperties { 13 | 14 | private static final String[] DEFAULT_EXCLUDE_METHODS = new String[]{"OPTIONS"}; 15 | 16 | private String tokenSecret = ""; 17 | 18 | private String[] excludeMethods = DEFAULT_EXCLUDE_METHODS; 19 | 20 | private Long tokenAccessExpire = 3600L; 21 | 22 | private Long tokenRefreshExpire = 2592000L; 23 | 24 | private boolean loggerEnabled = true; 25 | 26 | public boolean isLoggerEnabled() { 27 | return loggerEnabled; 28 | } 29 | 30 | public void setLoggerEnabled(boolean loggerEnabled) { 31 | this.loggerEnabled = loggerEnabled; 32 | } 33 | 34 | public String getTokenSecret() { 35 | return tokenSecret; 36 | } 37 | 38 | public void setTokenSecret(String tokenSecret) { 39 | this.tokenSecret = tokenSecret; 40 | } 41 | 42 | public Long getTokenAccessExpire() { 43 | return tokenAccessExpire; 44 | } 45 | 46 | /** 47 | * 设置 access token 过期时间 48 | * 49 | * @param tokenAccessExpire 过期时间 50 | */ 51 | public void setTokenAccessExpire(Long tokenAccessExpire) { 52 | this.tokenAccessExpire = tokenAccessExpire; 53 | } 54 | 55 | public Long getTokenRefreshExpire() { 56 | return tokenRefreshExpire; 57 | } 58 | 59 | /** 60 | * 设置 refresh token 过期时间 61 | * 62 | * @param tokenRefreshExpire 过期时间 63 | */ 64 | public void setTokenRefreshExpire(Long tokenRefreshExpire) { 65 | this.tokenRefreshExpire = tokenRefreshExpire; 66 | } 67 | 68 | public String[] getExcludeMethods() { 69 | return excludeMethods; 70 | } 71 | 72 | public void setExcludeMethods(String[] excludeMethods) { 73 | this.excludeMethods = excludeMethods; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/util/RequestUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.util; 2 | 3 | import org.springframework.web.context.request.RequestAttributes; 4 | import org.springframework.web.context.request.RequestContextHolder; 5 | import org.springframework.web.context.request.ServletRequestAttributes; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | /** 10 | * 请求工具类 11 | * 12 | * @author pedro@TaleLin 13 | * @author Juzi@TaleLin 14 | */ 15 | public class RequestUtil { 16 | 17 | /** 18 | * 获得当前请求 19 | * 20 | * @return Request 对象,如果没有绑定会返回 null 21 | */ 22 | public static HttpServletRequest getRequest() { 23 | RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); 24 | // 当前线程没有绑定 Request 25 | if (requestAttributes == null) { 26 | return null; 27 | } 28 | 29 | if (requestAttributes instanceof ServletRequestAttributes) { 30 | ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes; 31 | return servletRequestAttributes.getRequest(); 32 | } else { 33 | return null; 34 | } 35 | } 36 | 37 | /** 38 | * 获得请求 url 39 | * 40 | * @return url 41 | */ 42 | public static String getRequestUrl() { 43 | HttpServletRequest request = RequestUtil.getRequest(); 44 | if (request == null) { 45 | return null; 46 | } 47 | return request.getServletPath(); 48 | } 49 | 50 | /** 51 | * 获得请求简略信息 52 | * 53 | * @param request 请求 54 | * @return 简略信息 55 | */ 56 | public static String getSimpleRequest(HttpServletRequest request) { 57 | return request.getMethod() + " " + request.getServletPath(); 58 | } 59 | 60 | /** 61 | * 获得请求简略信息 62 | * 63 | * @return 简略信息 64 | */ 65 | public static String getSimpleRequest() { 66 | HttpServletRequest request = getRequest(); 67 | if (request == null) { 68 | return null; 69 | } 70 | return request.getMethod() + " " + request.getServletPath(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/ParameterException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import org.springframework.http.HttpStatus; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * 参数错误异常 11 | * 12 | * @author pedro@TaleLin 13 | * @author Juzi@TaleLin 14 | * @author colorful@TaleLin 15 | */ 16 | public class ParameterException extends HttpException { 17 | 18 | private static final long serialVersionUID = 3437368397374839983L; 19 | 20 | protected int httpCode = HttpStatus.BAD_REQUEST.value(); 21 | 22 | protected int code = Code.PARAMETER_ERROR.getCode(); 23 | 24 | private Map errors = new HashMap<>(); 25 | 26 | public ParameterException() { 27 | super(Code.PARAMETER_ERROR.getCode(), Code.PARAMETER_ERROR.getDescription()); 28 | super.ifDefaultMessage = true; 29 | } 30 | 31 | public ParameterException(String message) { 32 | super(message); 33 | } 34 | 35 | public ParameterException(int code) { 36 | super(code, Code.PARAMETER_ERROR.getDescription()); 37 | this.code = code; 38 | super.ifDefaultMessage=true; 39 | } 40 | 41 | @Deprecated 42 | public ParameterException(String message, int code) { 43 | super(message, code); 44 | this.code = code; 45 | } 46 | 47 | public ParameterException(int code, String message) { 48 | super(code, message); 49 | this.code = code; 50 | } 51 | 52 | public ParameterException(Map errors) { 53 | this.errors = errors; 54 | } 55 | 56 | public ParameterException addError(String key, Object val) { 57 | this.errors.put(key, val); 58 | return this; 59 | } 60 | 61 | @Override 62 | public String getMessage() { 63 | if (errors.isEmpty()) { 64 | return super.getMessage(); 65 | } 66 | return errors.toString(); 67 | } 68 | 69 | @Override 70 | public int getHttpCode() { 71 | return httpCode; 72 | } 73 | 74 | @Override 75 | public int getCode() { 76 | return code; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |
5 | Lin-CMS-Java-Core 6 |

7 | 8 |

9 | 一个简单易用的CMS后端项目 | 10 | Lin-CMS-Java 11 | 核心部分 12 |

13 | 14 |
15 | Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套内容管理系统框架
16 | Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率。 17 |
18 | 19 | ## 预览 20 | 21 | ### 线上 demo 22 | 23 | [http://face.cms.talelin.com/](http://face.cms.talelin.com/) 24 | 25 | ### 文档地址 26 | 27 | [http://doc.cms.talelin.com/](http://doc.cms.talelin.com/) 28 | 29 | ## 简介 30 | 31 | ### 什么是 Lin CMS? 32 | 33 | Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套**内容管理系统框 34 | 架**。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率。 35 | 36 | 本项目是 Lin CMS 后端的 java 实现,需要前端?请访 37 | 问[前端仓库](https://github.com/TaleLin/lin-cms-vue)。 38 | 39 | ### Lin CMS 特点 40 | 41 | Lin CMS 的构筑思想是有其自身特点的。下面我们阐述一些 Lin 的主要特点。 42 | 43 | #### Lin CMS 是一个前后端分离的 CMS 解决方案 44 | 45 | Lin 既提供后台的支撑,也有一套对应的前端系统,你也可以选择不同的后端实现, 46 | 如 koa 和 flask。 47 | 48 | #### 框架本身已内置了 CMS 常用的功能 49 | 50 | Lin 已经内置了 CMS 中最为常见的需求:用户管理、权限管理、日志系统等。开发者只需 51 | 要集中精力开发自己的 CMS 业务即可 52 | 53 | #### Lin CMS 本身也是一套开发规范 54 | 55 | Lin CMS 除了内置常见的功能外,还提供了一套开发规范与工具类。 56 | Lin CMS 只需要开发者关注自己的业务开发,它已经内置了很多机制帮助开发者快速开发自己的业务。 57 | 58 | #### 扩展灵活 59 | 60 | Lin CMS 支持 `extension` 来便捷地增强你的业务。 61 | 62 | #### 前端支持 63 | 64 | Lin CMS 也有自己的前端实现,强强联合为你助力。 65 | 66 | #### 完善的文档 67 | 68 | Lin CMS 提供大量的文档来帮助开发者使用 69 | 70 | 71 | ## 联系和交流 72 | 73 | ### QQ 交流群 74 | 75 | QQ 群号:643205479 76 | 77 | 78 | 79 | ### 微信公众号 80 | 81 | 微信搜索:林间有风 82 | 83 | 84 | 85 | ## 注意事项 86 | 87 | - Lin CMS 基于 spring boot ,因此也采取了 spring boot 的 starter (启动器)机制,Lin-CMS-Java-Core 正是 Lin CMS 的 starter 部分。 88 | 89 | - Lin-CMS-Java-Core 是一个多模块的 maven 项目,其中 `core` 部分完全与 spring 解耦,可以使用到其它类型的项目中。 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/configuration/LinCmsConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.configuration; 2 | 3 | import io.github.talelin.autoconfigure.interceptor.AuthorizeInterceptor; 4 | import io.github.talelin.autoconfigure.interceptor.LogInterceptor; 5 | import io.github.talelin.core.token.DoubleJWT; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.core.Ordered; 12 | import org.springframework.core.annotation.Order; 13 | 14 | /** 15 | * lin cms 配置类 16 | * 17 | * @author pedro@TaleLin 18 | */ 19 | @Configuration(proxyBeanMethods = false) 20 | @Order(Ordered.HIGHEST_PRECEDENCE) 21 | @EnableConfigurationProperties(LinCmsProperties.class) 22 | public class LinCmsConfiguration { 23 | 24 | @Autowired 25 | private LinCmsProperties properties; 26 | 27 | /** 28 | * jwt bean 29 | * 30 | * @return jwt bean 31 | */ 32 | @Bean 33 | public DoubleJWT jwter() { 34 | String secret = properties.getTokenSecret(); 35 | Long accessExpire = properties.getTokenAccessExpire(); 36 | Long refreshExpire = properties.getTokenRefreshExpire(); 37 | if (accessExpire == null) { 38 | // 一个小时 39 | accessExpire = 60 * 60L; 40 | } 41 | if (refreshExpire == null) { 42 | // 一个月 43 | refreshExpire = 60 * 60 * 24 * 30L; 44 | } 45 | return new DoubleJWT(secret, accessExpire, refreshExpire); 46 | } 47 | 48 | /** 49 | * AuthorizeInterceptor bean 50 | * 51 | * @return bean 52 | */ 53 | @Bean 54 | public AuthorizeInterceptor authInterceptor() { 55 | String[] excludeMethods = properties.getExcludeMethods(); 56 | return new AuthorizeInterceptor(excludeMethods); 57 | } 58 | 59 | /** 60 | * LogInterceptor bean 61 | * 62 | * @return bean 63 | */ 64 | @Bean 65 | @ConditionalOnProperty(prefix = "lin.cms", value = "logger-enabled", havingValue = "true") 66 | public LogInterceptor logInterceptor() { 67 | return new LogInterceptor(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/validator/impl/EqualFieldValidator.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator.impl; 2 | 3 | import io.github.talelin.autoconfigure.validator.EqualField; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.util.ReflectionUtils; 7 | 8 | import javax.validation.ConstraintValidator; 9 | import javax.validation.ConstraintValidatorContext; 10 | import javax.validation.ValidationException; 11 | import java.lang.reflect.Field; 12 | 13 | /** 14 | * 相等值校验器 15 | * 16 | * @author pedro@TaleLin 17 | * @author Juzi@TaleLin 18 | */ 19 | public class EqualFieldValidator implements ConstraintValidator { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(EqualFieldValidator.class); 22 | 23 | private String srcField; 24 | private String dstField; 25 | 26 | @Override 27 | public void initialize(EqualField constraintAnnotation) { 28 | this.srcField = constraintAnnotation.srcField(); 29 | this.dstField = constraintAnnotation.dstField(); 30 | } 31 | 32 | /** 33 | * 校验 34 | * 35 | * @param object 传入值 36 | * @param context 上下文 37 | * @return 是否成功 38 | */ 39 | @Override 40 | public boolean isValid(Object object, ConstraintValidatorContext context) { 41 | Class clazz = object.getClass(); 42 | 43 | Field srcField = ReflectionUtils.findField(clazz, this.srcField); 44 | Field dstField = ReflectionUtils.findField(clazz, this.dstField); 45 | 46 | try { 47 | 48 | if (srcField == null || dstField == null) { 49 | throw new ValidationException("反射获取变量失败"); 50 | } 51 | 52 | srcField.setAccessible(true); 53 | dstField.setAccessible(true); 54 | Object src = srcField.get(object); 55 | Object dst = dstField.get(object); 56 | 57 | // 其中一个变量为 null 时,则必须两个都为 null 才相等 58 | if (src == null || dst == null) { 59 | return src == dst; 60 | } 61 | 62 | // 如果两个对象内存地址相同,则一定相等 63 | if (src == dst) { 64 | return true; 65 | } 66 | 67 | // 调用 equals 方法比较 68 | return src.equals(dst); 69 | } catch (Exception e) { 70 | log.warn("EqualFieldValidator 校验异常", e); 71 | return false; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/interfaces/AuthorizeVerifyResolver.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.interfaces; 2 | 3 | import io.github.talelin.autoconfigure.bean.MetaInfo; 4 | import org.springframework.web.servlet.ModelAndView; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * 权限认证接口 11 | * 12 | * @author pedro@TaleLin 13 | * @author Juzi@TaleLin 14 | */ 15 | public interface AuthorizeVerifyResolver { 16 | 17 | 18 | /** 19 | * 处理 LoginRequired的情况 20 | * 21 | * @param request 请求 22 | * @param response 响应 23 | * @param meta 路由信息 24 | * @return 是否成功 25 | */ 26 | boolean handleLogin(HttpServletRequest request, HttpServletResponse response, MetaInfo meta); 27 | 28 | /** 29 | * 处理 GroupRequired的情况 30 | * 31 | * @param request 请求 32 | * @param response 响应 33 | * @param meta 路由信息 34 | * @return 是否成功 35 | */ 36 | boolean handleGroup(HttpServletRequest request, HttpServletResponse response, MetaInfo meta); 37 | 38 | /** 39 | * 处理 AdminRequired的情况 40 | * 41 | * @param request 请求 42 | * @param response 响应 43 | * @param meta 路由信息 44 | * @return 是否成功 45 | */ 46 | boolean handleAdmin(HttpServletRequest request, HttpServletResponse response, MetaInfo meta); 47 | 48 | /** 49 | * 处理 RefreshRequired的情况 50 | * 51 | * @param request 请求 52 | * @param response 响应 53 | * @param meta 路由信息 54 | * @return 是否成功 55 | */ 56 | boolean handleRefresh(HttpServletRequest request, HttpServletResponse response, MetaInfo meta); 57 | 58 | /** 59 | * 处理 当前的handler 不是 HandlerMethod 的情况 60 | * 61 | * @param request 请求 62 | * @param response 响应 63 | * @param handler 处理器 64 | * @return 是否成功 65 | */ 66 | boolean handleNotHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler); 67 | 68 | /** 69 | * 后置处理 70 | * 71 | * @param request 请求 72 | * @param response 响应 73 | * @param handler 处理器 74 | * @param modelAndView 视图 75 | */ 76 | default void handlePostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { 77 | } 78 | 79 | /** 80 | * 相应完成后处理 81 | * 82 | * @param request 请求 83 | * @param response 响应 84 | * @param handler 处理器 85 | * @param ex 异常 86 | */ 87 | default void handleAfterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/test/java/io/github/talelin/core/token/SingleJWTTest.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.token; 2 | 3 | import com.auth0.jwt.algorithms.Algorithm; 4 | import com.auth0.jwt.interfaces.Claim; 5 | import io.github.talelin.core.util.DateUtil; 6 | import org.junit.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Map; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * @author Juzi@TaleLin 16 | */ 17 | public class SingleJWTTest { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(SingleJWTTest.class); 20 | 21 | @Test 22 | public void generateToken() { 23 | SingleJWT jwt = new SingleJWT("secret", 1000); 24 | String token = jwt.generateToken("test", 1, "test", 1000); 25 | assertNotNull(token); 26 | log.info(token); 27 | } 28 | 29 | @Test 30 | public void getSpecifyToken() { 31 | SingleJWT jwt = new SingleJWT("secret", 1000); 32 | String token = jwt.getBuilder() 33 | .withClaim("type", "test") 34 | .withClaim("identity", 1) 35 | .withClaim("scope", "test") 36 | .withExpiresAt(DateUtil.getDurationDate(10000)) 37 | .sign(jwt.getAlgorithm()); 38 | assertNotNull(token); 39 | log.info(token); 40 | } 41 | 42 | @Test 43 | public void decodeToken() { 44 | SingleJWT jwt = new SingleJWT("secret", 10000); 45 | String token = jwt.getBuilder() 46 | .withClaim("type", "test") 47 | .withClaim("identity", 1) 48 | .withClaim("scope", "test") 49 | .withExpiresAt(DateUtil.getDurationDate(10000)) 50 | .sign(jwt.getAlgorithm()); 51 | assertNotNull(token); 52 | log.info(token); 53 | Map claimMap = jwt.decodeToken(token); 54 | log.info("{}", claimMap); 55 | assertEquals(claimMap.get("type").asString(), "test"); 56 | assertEquals(claimMap.get("scope").asString(), "test"); 57 | } 58 | 59 | @Test 60 | public void getVerifier() { 61 | Algorithm algorithm = Algorithm.HMAC256("secret"); 62 | SingleJWT jwt = new SingleJWT(algorithm, 1000); 63 | assertNotNull(jwt.getVerifier()); 64 | } 65 | 66 | @Test 67 | public void getBuilder() { 68 | Algorithm algorithm = Algorithm.HMAC256("secret"); 69 | SingleJWT jwt = new SingleJWT(algorithm, 1000); 70 | assertNotNull(jwt.getBuilder()); 71 | } 72 | 73 | @Test 74 | public void getAlgorithm() { 75 | Algorithm algorithm = Algorithm.HMAC256("secret"); 76 | SingleJWT jwt = new SingleJWT(algorithm, 1000); 77 | assertNotNull(jwt.getAlgorithm()); 78 | } 79 | 80 | @Test 81 | public void getExpire() { 82 | Algorithm algorithm = Algorithm.HMAC256("secret"); 83 | SingleJWT jwt = new SingleJWT(algorithm, 1000); 84 | assertEquals(1000L, (long) jwt.getExpire()); 85 | } 86 | } -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/token/SingleJWT.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.token; 2 | 3 | import com.auth0.jwt.JWTCreator; 4 | import com.auth0.jwt.JWTVerifier; 5 | import com.auth0.jwt.algorithms.Algorithm; 6 | import com.auth0.jwt.exceptions.TokenExpiredException; 7 | import com.auth0.jwt.interfaces.Claim; 8 | import com.auth0.jwt.interfaces.DecodedJWT; 9 | import io.github.talelin.core.util.DateUtil; 10 | 11 | import java.util.Date; 12 | import java.util.Map; 13 | 14 | /** 15 | * 单令牌模式 16 | * single token 17 | * 18 | * @author pedro@TaleLin 19 | * @author Juzi@TaleLin 20 | */ 21 | public class SingleJWT { 22 | 23 | private Algorithm algorithm; 24 | 25 | private long expire; 26 | 27 | 28 | private JWTVerifier verifier; 29 | 30 | private JWTCreator.Builder builder; 31 | 32 | /** 33 | * @param algorithm 加密算法 34 | * @param expire token过期时间 35 | */ 36 | public SingleJWT(Algorithm algorithm, long expire) { 37 | this.algorithm = algorithm; 38 | this.expire = expire; 39 | this.initBuilderAndVerifier(); 40 | } 41 | 42 | /** 43 | * @param secret 不传入加密算法,传入密钥,则默认使用 HMAC256 加密算法 44 | * @param expire token过期时间 45 | */ 46 | public SingleJWT(String secret, long expire) { 47 | this.algorithm = Algorithm.HMAC256(secret); 48 | this.expire = expire; 49 | this.initBuilderAndVerifier(); 50 | } 51 | 52 | public String generateToken(String tokenType, long identity, String scope, long expire) { 53 | Date expireDate = DateUtil.getDurationDate(expire); 54 | return builder 55 | .withClaim("type", tokenType) 56 | .withClaim("identity", identity) 57 | .withClaim("scope", scope) 58 | .withExpiresAt(expireDate) 59 | .sign(algorithm); 60 | } 61 | 62 | public Map decodeToken(String token) { 63 | DecodedJWT jwt = verifier.verify(token); 64 | checkTokenExpired(jwt.getExpiresAt()); 65 | return jwt.getClaims(); 66 | } 67 | 68 | private void checkTokenExpired(Date expiresAt) { 69 | long now = System.currentTimeMillis(); 70 | if (expiresAt.getTime() < now) { 71 | throw new TokenExpiredException("token is expired"); 72 | } 73 | } 74 | 75 | /*** 76 | * 获得令牌的验证器 77 | * @return JWTVerifier 78 | */ 79 | public JWTVerifier getVerifier() { 80 | return verifier; 81 | } 82 | 83 | /** 84 | * 获得令牌构建器 85 | * 86 | * @return Builder 87 | */ 88 | public JWTCreator.Builder getBuilder() { 89 | return builder; 90 | } 91 | 92 | /** 93 | * 获得加密方法 94 | * 95 | * @return Algorithm 96 | */ 97 | public Algorithm getAlgorithm() { 98 | return algorithm; 99 | } 100 | 101 | public Long getExpire() { 102 | return expire; 103 | } 104 | 105 | 106 | private void initBuilderAndVerifier() { 107 | verifier = com.auth0.jwt.JWT.require(algorithm) 108 | .acceptExpiresAt(this.expire) 109 | .build(); 110 | builder = com.auth0.jwt.JWT.create(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/test/java/io/github/talelin/autoconfigure/validator/impl/EqualFieldValidatorTest.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.validator.impl; 2 | 3 | import io.github.talelin.autoconfigure.validator.EqualField; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.Validation; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 14 | 15 | /** 16 | * EualFieldValidator 单元测试 17 | * 18 | * @author Juzi@TaleLin 19 | * @date 2020-05-25 20 | */ 21 | class EqualFieldValidatorTest { 22 | 23 | @EqualField(srcField = "src", dstField = "dst") 24 | static class TestDataString { 25 | public String src; 26 | public String dst; 27 | } 28 | 29 | @EqualField(srcField = "src", dstField = "dst") 30 | static class TestDataObject { 31 | public List src; 32 | public List dst; 33 | } 34 | 35 | /** 36 | * 校验 String 通过 37 | */ 38 | @Test 39 | void testIsValidStringTrue() { 40 | TestDataString testDataString = new TestDataString(); 41 | testDataString.src = "桔子"; 42 | testDataString.dst = "桔子"; 43 | Set> validate = 44 | Validation.buildDefaultValidatorFactory().getValidator().validate(testDataString); 45 | assertEquals(0, validate.size()); 46 | } 47 | 48 | /** 49 | * 校验 String 不通过 50 | */ 51 | @Test 52 | void testIsValidStringFalse() { 53 | TestDataString testDataString = new TestDataString(); 54 | testDataString.src = "桔子"; 55 | testDataString.dst = "橘子"; 56 | Set> validate = 57 | Validation.buildDefaultValidatorFactory().getValidator().validate(testDataString); 58 | assertNotEquals(0, validate.size()); 59 | } 60 | 61 | /** 62 | * 校验 Object 通过 63 | */ 64 | @Test 65 | void testIsValidObjectTrue() { 66 | List stringListA = new ArrayList<>(); 67 | stringListA.add(1); 68 | List stringListB = new ArrayList<>(); 69 | stringListB.add(1); 70 | 71 | TestDataObject testDataObject = new TestDataObject(); 72 | testDataObject.src = stringListA; 73 | testDataObject.dst = stringListB; 74 | 75 | Set> validate = 76 | Validation.buildDefaultValidatorFactory().getValidator().validate(testDataObject); 77 | assertEquals(0, validate.size()); 78 | } 79 | 80 | /** 81 | * 校验 Object 不通过 82 | */ 83 | @Test 84 | void testIsValidObjectFalse() { 85 | List stringListA = new ArrayList<>(); 86 | stringListA.add(1); 87 | List stringListB = new ArrayList<>(); 88 | stringListB.add(2); 89 | 90 | TestDataObject testDataObject = new TestDataObject(); 91 | testDataObject.src = stringListA; 92 | testDataObject.dst = stringListB; 93 | 94 | Set> validate = 95 | Validation.buildDefaultValidatorFactory().getValidator().validate(testDataObject); 96 | assertNotEquals(0, validate.size()); 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/exception/HttpException.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import io.github.talelin.autoconfigure.bean.Code; 4 | import io.github.talelin.autoconfigure.interfaces.BaseResponse; 5 | import org.springframework.http.HttpStatus; 6 | 7 | /** 8 | * HttpException 异常类 9 | * 含异常信息 message 10 | * http状态码 httpCode 11 | * 错误码 code 12 | * 13 | * @author pedro@TaleLin 14 | * @author Juzi@TaleLin 15 | * @author colorful@TaleLin 16 | */ 17 | public class HttpException extends RuntimeException implements BaseResponse { 18 | 19 | private static final long serialVersionUID = 2359767895161832954L; 20 | 21 | protected int httpCode = HttpStatus.INTERNAL_SERVER_ERROR.value(); 22 | 23 | protected int code = Code.INTERNAL_SERVER_ERROR.getCode(); 24 | 25 | /** 26 | * 是否是默认消息 27 | * true: 没有通过构造函数传入 message 28 | * false:通过构造函数传入了 message 29 | * 30 | * 没有用 isDefaultMessage 是因为和部分 rpc 框架存在兼容问题 31 | */ 32 | protected boolean ifDefaultMessage = true; 33 | 34 | public HttpException() { 35 | super(Code.INTERNAL_SERVER_ERROR.getDescription()); 36 | } 37 | 38 | public HttpException(String message) { 39 | super(message); 40 | this.ifDefaultMessage = false; 41 | } 42 | 43 | public HttpException(int code) { 44 | super(Code.INTERNAL_SERVER_ERROR.getDescription()); 45 | this.code = code; 46 | } 47 | 48 | public HttpException(int code, int httpCode) { 49 | super(Code.INTERNAL_SERVER_ERROR.getDescription()); 50 | this.httpCode = httpCode; 51 | this.code = code; 52 | } 53 | 54 | @Deprecated 55 | public HttpException(String message, int code) { 56 | super(message); 57 | this.code = code; 58 | this.ifDefaultMessage = false; 59 | } 60 | 61 | @Deprecated 62 | public HttpException(String message, int code, int httpCode) { 63 | super(message); 64 | this.httpCode = httpCode; 65 | this.code = code; 66 | this.ifDefaultMessage = false; 67 | } 68 | 69 | public HttpException(int code, String message) { 70 | super(message); 71 | this.code = code; 72 | this.ifDefaultMessage = false; 73 | } 74 | 75 | public HttpException(int code, String message, int httpCode) { 76 | super(message); 77 | this.code = code; 78 | this.httpCode = httpCode; 79 | this.ifDefaultMessage = false; 80 | } 81 | 82 | public HttpException(Throwable cause, int code) { 83 | super(cause); 84 | this.code = code; 85 | } 86 | 87 | public HttpException(Throwable cause, int code, int httpCode) { 88 | super(cause); 89 | this.code = code; 90 | this.httpCode = httpCode; 91 | } 92 | 93 | public HttpException(String message, Throwable cause) { 94 | super(message, cause); 95 | this.ifDefaultMessage = false; 96 | } 97 | 98 | 99 | /** 100 | * for better performance 101 | * 102 | * @return Throwable 103 | */ 104 | public Throwable doFillInStackTrace() { 105 | return super.fillInStackTrace(); 106 | } 107 | 108 | @Override 109 | public int getHttpCode() { 110 | return this.httpCode; 111 | } 112 | 113 | @Override 114 | public int getCode() { 115 | return this.code; 116 | } 117 | 118 | public boolean ifDefaultMessage() { 119 | return this.ifDefaultMessage; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/test/java/io/github/talelin/autoconfigure/exception/ParameterExceptionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.exception; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | /** 13 | * @author Juzi@TaleLin 14 | */ 15 | public class ParameterExceptionTest { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(ParameterExceptionTest.class); 18 | 19 | @Test 20 | public void test() { 21 | ParameterException exception = new ParameterException(); 22 | String message = exception.getMessage(); 23 | String localizedMessage = exception.getLocalizedMessage(); 24 | int code = exception.getCode(); 25 | assertEquals(10030, code); 26 | assertEquals("Parameters Error", message); 27 | assertEquals("Parameters Error", localizedMessage); 28 | } 29 | 30 | @Test 31 | public void test1() { 32 | ParameterException exception = new ParameterException("pedro犯了一个错误"); 33 | String message = exception.getMessage(); 34 | String localizedMessage = exception.getLocalizedMessage(); 35 | int code = exception.getCode(); 36 | assertEquals(10030, code); 37 | assertEquals("pedro犯了一个错误", message); 38 | assertEquals("pedro犯了一个错误", localizedMessage); 39 | } 40 | 41 | @Test 42 | public void test2() { 43 | Map errors = new HashMap<>(); 44 | errors.put("nickname", "名称不能超过100字符"); 45 | errors.put("age", "年龄不能为负数"); 46 | ParameterException exception = new ParameterException(errors); 47 | String message = exception.getMessage(); 48 | int code = exception.getCode(); 49 | assertEquals(10030, code); 50 | assertEquals("{nickname=名称不能超过100字符, age=年龄不能为负数}", message); 51 | } 52 | 53 | @Test 54 | public void test3() { 55 | ParameterException exception = new ParameterException(); 56 | exception.addError("nickname", "名称不能超过100字符"); 57 | String message = exception.getMessage(); 58 | int code = exception.getCode(); 59 | assertEquals(10030, code); 60 | assertEquals("{nickname=名称不能超过100字符}", message); 61 | } 62 | 63 | @Test 64 | public void testNoArgsConstructor() { 65 | ParameterException exception = new ParameterException(); 66 | int code = exception.getCode(); 67 | boolean ifDefaultMessage = exception.ifDefaultMessage(); 68 | assertEquals(10030, code); 69 | assertTrue(ifDefaultMessage); 70 | } 71 | 72 | @Test 73 | public void testMessageOnlyConstructor() { 74 | ParameterException exception = new ParameterException("参数错了吧"); 75 | String message = exception.getMessage(); 76 | boolean ifDefaultMessage = exception.ifDefaultMessage(); 77 | assertEquals("参数错了吧", message); 78 | assertFalse(ifDefaultMessage); 79 | } 80 | 81 | @Test 82 | public void testAllArgsConstructor() { 83 | ParameterException exception = new ParameterException("参数错了吧", 10040); 84 | int code = exception.getCode(); 85 | String message = exception.getMessage(); 86 | boolean ifDefaultMessage = exception.ifDefaultMessage(); 87 | assertEquals(10040, code); 88 | assertEquals("参数错了吧", message); 89 | assertFalse(ifDefaultMessage); 90 | } 91 | 92 | @Test 93 | public void testCodeOnlyConstructor() { 94 | ParameterException exception = new ParameterException(10040); 95 | int code = exception.getCode(); 96 | String message = exception.getMessage(); 97 | boolean ifDefaultMessage = exception.ifDefaultMessage(); 98 | assertEquals(10040, code); 99 | assertEquals("Parameters Error", message); 100 | assertTrue(ifDefaultMessage); 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.talelin 8 | cms 9 | 0.2.0-RC2 10 | 11 | 12 | core 13 | lin-cms-spring-boot-autoconfigure 14 | lin-cms-spring-boot-starter 15 | 16 | 17 | 18 | 1.8 19 | 1.8 20 | true 21 | 22 | 23 | pom 24 | 25 | lin-cms 26 | https://github.com/TaleLin/lin-cms-java-core 27 | A simple and practical CMS implememted by java 28 | 29 | 30 | 31 | The MIT License (MIT) 32 | https://github.com/TaleLin/lin-cms-java-core/blob/master/LICENSE 33 | repo 34 | 35 | 36 | 37 | 38 | https://github.com/TaleLin/lin-cms-java-core 39 | https://github.com/TaleLin/lin-cms-java-core.git 40 | 41 | 42 | 43 | 44 | pedro 45 | pedrogao1996@gmail.com 46 | 47 | 48 | 49 | 50 | 51 | ossrh 52 | https://oss.sonatype.org/content/repositories/snapshots 53 | 54 | 55 | ossrh 56 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.sonatype.plugins 65 | nexus-staging-maven-plugin 66 | 1.6.3 67 | true 68 | 69 | ossrh 70 | https://oss.sonatype.org/ 71 | true 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-source-plugin 78 | 2.2.1 79 | 80 | 81 | attach-sources 82 | 83 | jar-no-fork 84 | 85 | 86 | 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-javadoc-plugin 92 | 2.9.1 93 | 94 | 95 | attach-javadocs 96 | 97 | jar 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-gpg-plugin 106 | 1.5 107 | 108 | 109 | verify 110 | 111 | sign 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/util/BeanUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.util; 2 | 3 | import java.beans.IntrospectionException; 4 | import java.beans.PropertyDescriptor; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * Bean 工具函数 10 | * 11 | * @author pedro@TaleLin 12 | * @author Juzi@TaleLin 13 | */ 14 | public class BeanUtil { 15 | 16 | private static final String SET_PREFIX = "set"; 17 | private static final String IS_PREFIX = "is"; 18 | private static final String GET_PREFIX = "get"; 19 | 20 | /** 21 | * 获得类的属性 22 | * 23 | * @param clazz 类 24 | * @param propertyName 属性名 25 | * @return 属性值 26 | */ 27 | public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) { 28 | //根据需求,定制 自己的get和set方法 29 | Method setMethod; 30 | Method getMethod; 31 | PropertyDescriptor pd = null; 32 | try { 33 | // 根据字段名来获取字段 34 | Field field = clazz.getDeclaredField(propertyName); 35 | // 构建方法的后缀 36 | String methodEnd = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); 37 | setMethod = clazz.getDeclaredMethod(SET_PREFIX + methodEnd, field.getType()); 38 | // 构建get 方法 39 | getMethod = clazz.getDeclaredMethod(GET_PREFIX + methodEnd); 40 | // 构建一个属性描述器 把对应属性 propertyName 的 get 和 set 方法保存到属性描述器中 41 | pd = new PropertyDescriptor(propertyName, getMethod, setMethod); 42 | } catch (Exception ex) { 43 | ex.printStackTrace(); 44 | } 45 | 46 | return pd; 47 | } 48 | 49 | /** 50 | * 获得类的属性2 51 | * 52 | * @param clazz 类 53 | * @param propertyName 属性名 54 | * @return 属性值 55 | */ 56 | public static PropertyDescriptor getPropertyDescriptor2(Class clazz, String propertyName) { 57 | //使用 PropertyDescriptor 提供的 get和set方法 58 | try { 59 | return new PropertyDescriptor(propertyName, clazz); 60 | } catch (IntrospectionException e) { 61 | e.printStackTrace(); 62 | } 63 | return null; 64 | } 65 | 66 | /** 67 | * 设置对象属性 68 | * 69 | * @param obj 对象 70 | * @param propertyName 属性名 71 | * @param value 属性值 72 | */ 73 | public static void setProperty(Object obj, String propertyName, Object value) { 74 | // 获取对象的类型 75 | Class clazz = obj.getClass(); 76 | // 获取 clazz 类型中的 propertyName 的属性描述器 77 | PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName); 78 | // 从属性描述器中获取 set 方法 79 | Method setMethod = pd.getWriteMethod(); 80 | try { 81 | // 调用 set 方法将传入的value值保存属性中去 82 | setMethod.invoke(obj, value); 83 | } catch (Exception e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | 88 | /** 89 | * 获得对象的属性 90 | * 91 | * @param obj 对象 92 | * @param propertyName 属性名 93 | * @return 属性值 94 | */ 95 | public static String getProperty(Object obj, String propertyName) { 96 | // 获取对象的类型 97 | Class clazz = obj.getClass(); 98 | String value; 99 | try { 100 | // 获取 101 | PropertyDescriptor pd = getPropertyDescriptor(clazz, propertyName); 102 | // 从属性描述器中获取 get 方法 103 | Method getMethod = pd.getReadMethod(); 104 | // 调用方法获取方法的返回值 105 | value = getMethod.invoke(clazz).toString(); 106 | } catch (Exception e) { 107 | return ""; 108 | } 109 | // 返回值 110 | return value; 111 | } 112 | 113 | /** 114 | * 获得string属性值 115 | * 116 | * @param obj 对象 117 | * @param propName 属性名 118 | * @return 属性值 119 | */ 120 | public static String getValueByPropName(Object obj, String propName) { 121 | // 获取对象的类型 122 | Class clazz = obj.getClass(); 123 | String value; 124 | try { 125 | String upperCaseFirstOne = toUpperCaseFirstOne(propName); 126 | Method method = clazz.getMethod(GET_PREFIX + upperCaseFirstOne); 127 | Object result = method.invoke(obj); 128 | value = result.toString(); 129 | } catch (Exception e) { 130 | // 如果异常,或者没有返回空字符串 131 | return ""; 132 | } 133 | return value; 134 | } 135 | 136 | /** 137 | * 字符串首字母大写 138 | * 139 | * @param s 字符串 140 | * @return 处理后字符串 141 | */ 142 | public static String toUpperCaseFirstOne(String s) { 143 | if (Character.isUpperCase(s.charAt(0))) { 144 | return s; 145 | } else { 146 | return Character.toUpperCase(s.charAt(0)) + s.substring(1); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.0 9 | 10 | 11 | 12 | io.github.talelin 13 | lin-cms-spring-boot-starter 14 | 0.2.0-RC2 15 | lin-cms-spring-boot-starter 16 | starter project for lin cms 17 | https://github.com/TaleLin/lin-cms-java-core 18 | jar 19 | 20 | 21 | UTF-8 22 | UTF-8 23 | 1.8 24 | 1.8 25 | 1.8 26 | 27 | 28 | 29 | 30 | io.github.talelin 31 | lin-cms-spring-boot-autoconfigure 32 | 0.2.0-RC2 33 | 34 | 35 | 36 | 37 | 38 | The MIT License (MIT) 39 | https://github.com/TaleLin/lin-cms-java-core/blob/master/LICENSE 40 | repo 41 | 42 | 43 | 44 | 45 | https://github.com/TaleLin/lin-cms-java-core 46 | https://github.com/TaleLin/lin-cms-java-core.git 47 | 48 | 49 | 50 | 51 | pedro 52 | pedrogao1996@gmail.com 53 | 54 | 55 | 56 | 57 | 58 | ossrh 59 | https://oss.sonatype.org/content/repositories/snapshots 60 | 61 | 62 | ossrh 63 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.sonatype.plugins 73 | nexus-staging-maven-plugin 74 | 1.6.3 75 | true 76 | 77 | ossrh 78 | https://oss.sonatype.org/ 79 | true 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-source-plugin 86 | 2.2.1 87 | 88 | 89 | attach-sources 90 | 91 | jar-no-fork 92 | 93 | 94 | 95 | 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-javadoc-plugin 100 | 2.9.1 101 | 102 | 103 | attach-javadocs 104 | 105 | jar 106 | 107 | 108 | 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-gpg-plugin 114 | 1.5 115 | 116 | 117 | verify 118 | 119 | sign 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/interceptor/AuthorizeInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.interceptor; 2 | 3 | import io.github.talelin.autoconfigure.bean.MetaInfo; 4 | import io.github.talelin.autoconfigure.bean.PermissionMetaCollector; 5 | import io.github.talelin.autoconfigure.interfaces.AuthorizeVerifyResolver; 6 | import io.github.talelin.core.enumeration.UserLevel; 7 | import io.github.talelin.core.util.AnnotationUtil; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.method.HandlerMethod; 10 | import org.springframework.web.servlet.ModelAndView; 11 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.lang.annotation.Annotation; 16 | import java.lang.reflect.Method; 17 | 18 | /** 19 | * 权限拦截器 20 | * 21 | * @author pedro@TaleLin 22 | * @author Juzi@TaleLin 23 | */ 24 | public class AuthorizeInterceptor extends HandlerInterceptorAdapter { 25 | 26 | @Autowired 27 | private AuthorizeVerifyResolver authorizeVerifyResolver; 28 | 29 | @Autowired 30 | private PermissionMetaCollector permissionMetaCollector; 31 | 32 | private String[] excludeMethods = new String[]{"OPTIONS"}; 33 | 34 | public AuthorizeInterceptor() { 35 | } 36 | 37 | /** 38 | * 构造函数 39 | * 40 | * @param excludeMethods 不检查方法 41 | */ 42 | public AuthorizeInterceptor(String[] excludeMethods) { 43 | this.excludeMethods = excludeMethods; 44 | } 45 | 46 | /** 47 | * 前置处理 48 | * 49 | * @param request request 请求 50 | * @param response 相应 51 | * @param handler 处理器 52 | * @return 是否成功 53 | */ 54 | @Override 55 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { 56 | if (checkInExclude(request.getMethod())) { 57 | // 有些请求方法无需检测,如OPTIONS 58 | return true; 59 | } 60 | if (handler instanceof HandlerMethod) { 61 | HandlerMethod handlerMethod = (HandlerMethod) handler; 62 | Method method = handlerMethod.getMethod(); 63 | String methodName = method.getName(); 64 | String className = method.getDeclaringClass().getName(); 65 | String identity = className + "#" + methodName; 66 | MetaInfo meta = permissionMetaCollector.findMeta(identity); 67 | // AdminMeta adminMeta = method.getAnnotation(AdminMeta.class); 68 | // PermissionMeta meta = method.getAnnotation(PermissionMeta.class); 69 | // 考虑两种情况,1. 有 meta;2. 无 meta 70 | if (meta == null) { 71 | // 无meta的话,adminRequired和loginRequired 72 | return this.handleNoMeta(request, response, method); 73 | } else { 74 | // 有meta在权限范围之内,需要判断权限 75 | return this.handleMeta(request, response, method, meta); 76 | } 77 | } else { 78 | // handler不是HandlerMethod的情况 79 | return authorizeVerifyResolver.handleNotHandlerMethod(request, response, handler); 80 | } 81 | } 82 | 83 | @Override 84 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { 85 | authorizeVerifyResolver.handlePostHandle(request, response, handler, modelAndView); 86 | } 87 | 88 | @Override 89 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { 90 | authorizeVerifyResolver.handleAfterCompletion(request, response, handler, ex); 91 | } 92 | 93 | private boolean handleNoMeta(HttpServletRequest request, HttpServletResponse response, Method method) { 94 | Annotation[] annotations = method.getAnnotations(); 95 | UserLevel level = AnnotationUtil.findRequired(annotations); 96 | switch (level) { 97 | case LOGIN: 98 | case GROUP: 99 | // 分组权限 100 | // 登陆权限 101 | return authorizeVerifyResolver.handleLogin(request, response, null); 102 | case ADMIN: 103 | // 管理员权限 104 | return authorizeVerifyResolver.handleAdmin(request, response, null); 105 | case REFRESH: 106 | // 刷新令牌 107 | return authorizeVerifyResolver.handleRefresh(request, response, null); 108 | default: 109 | return true; 110 | } 111 | } 112 | 113 | private boolean handleMeta(HttpServletRequest request, HttpServletResponse response, Method method, MetaInfo meta) { 114 | // Annotation[] annotations = method.getAnnotations(); 115 | // UserLevel level = AnnotationUtil.findRequired(annotations); 116 | UserLevel level = meta.getUserLevel(); 117 | switch (level) { 118 | case LOGIN: 119 | // 登陆权限 120 | return authorizeVerifyResolver.handleLogin(request, response, meta); 121 | case GROUP: 122 | // 分组权限 123 | return authorizeVerifyResolver.handleGroup(request, response, meta); 124 | case ADMIN: 125 | // 管理员权限 126 | return authorizeVerifyResolver.handleAdmin(request, response, meta); 127 | case REFRESH: 128 | // 刷新令牌 129 | return authorizeVerifyResolver.handleRefresh(request, response, meta); 130 | default: 131 | return true; 132 | } 133 | } 134 | 135 | private boolean checkInExclude(String method) { 136 | for (String excludeMethod : excludeMethods) { 137 | if (method.equals(excludeMethod)) { 138 | return true; 139 | } 140 | } 141 | return false; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.github.talelin 7 | lin-cms-core 8 | 0.2.0-RC2 9 | jar 10 | lin-cms-core 11 | https://github.com/TaleLin/lin-cms-java-core 12 | core library for lin-cms 13 | 14 | 15 | UTF-8 16 | UTF-8 17 | 1.8 18 | 1.8 19 | 1.8 20 | 21 | 22 | 23 | 24 | jcenter 25 | https://jcenter.bintray.com/ 26 | 27 | 28 | 29 | 30 | 31 | 32 | com.auth0 33 | java-jwt 34 | 3.10.1 35 | 36 | 37 | 38 | com.amdelamar 39 | jhash 40 | 2.1.2 41 | 42 | 43 | 44 | ch.qos.logback 45 | logback-core 46 | 1.2.3 47 | 48 | 49 | 50 | ch.qos.logback 51 | logback-classic 52 | 1.2.3 53 | test 54 | 55 | 56 | 57 | org.slf4j 58 | slf4j-api 59 | 1.7.25 60 | test 61 | 62 | 63 | 64 | junit 65 | junit 66 | 4.13.1 67 | test 68 | 69 | 70 | 71 | 72 | 73 | 74 | The MIT License (MIT) 75 | https://github.com/TaleLin/lin-cms-java-core/blob/master/LICENSE 76 | repo 77 | 78 | 79 | 80 | 81 | https://github.com/TaleLin/lin-cms-java-core 82 | https://github.com/TaleLin/lin-cms-java-core.git 83 | 84 | 85 | 86 | 87 | pedro 88 | pedrogao1996@gmail.com 89 | 90 | 91 | 92 | 93 | 94 | ossrh 95 | https://oss.sonatype.org/content/repositories/snapshots 96 | 97 | 98 | ossrh 99 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.sonatype.plugins 109 | nexus-staging-maven-plugin 110 | 1.6.3 111 | true 112 | 113 | ossrh 114 | https://oss.sonatype.org/ 115 | true 116 | 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-source-plugin 122 | 2.2.1 123 | 124 | 125 | attach-sources 126 | 127 | jar-no-fork 128 | 129 | 130 | 131 | 132 | 133 | 134 | org.apache.maven.plugins 135 | maven-javadoc-plugin 136 | 2.9.1 137 | 138 | 139 | attach-javadocs 140 | 141 | jar 142 | 143 | 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-gpg-plugin 150 | 1.5 151 | 152 | 153 | verify 154 | 155 | sign 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.4.3 10 | 11 | 12 | 13 | io.github.talelin 14 | lin-cms-spring-boot-autoconfigure 15 | 0.2.0-RC2 16 | lin-cms-spring-boot-autoconfigure 17 | autoconfigure project for lin cms 18 | https://github.com/TaleLin/lin-cms-java-core 19 | jar 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 1.8 26 | 1.8 27 | 28 | 29 | 30 | 31 | io.github.talelin 32 | lin-cms-core 33 | 0.2.0-RC2 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-autoconfigure 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-validation 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter 49 | provided 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-web 55 | provided 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-test 61 | test 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-configuration-processor 67 | true 68 | 69 | 70 | 71 | 72 | 73 | The MIT License (MIT) 74 | https://github.com/TaleLin/lin-cms-java-core/blob/master/LICENSE 75 | repo 76 | 77 | 78 | 79 | 80 | https://github.com/TaleLin/lin-cms-java-core 81 | https://github.com/TaleLin/lin-cms-java-core.git 82 | 83 | 84 | 85 | 86 | pedro 87 | pedrogao1996@gmail.com 88 | 89 | 90 | 91 | 92 | 93 | ossrh 94 | https://oss.sonatype.org/content/repositories/snapshots 95 | 96 | 97 | ossrh 98 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | org.sonatype.plugins 108 | nexus-staging-maven-plugin 109 | 1.6.3 110 | true 111 | 112 | ossrh 113 | https://oss.sonatype.org/ 114 | true 115 | 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-source-plugin 121 | 2.2.1 122 | 123 | 124 | attach-sources 125 | 126 | jar-no-fork 127 | 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-javadoc-plugin 135 | 2.9.1 136 | 137 | 138 | attach-javadocs 139 | 140 | jar 141 | 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-gpg-plugin 149 | 1.5 150 | 151 | 152 | verify 153 | 154 | sign 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/token/DoubleJWT.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.token; 2 | 3 | import com.auth0.jwt.JWTCreator; 4 | import com.auth0.jwt.JWTVerifier; 5 | import com.auth0.jwt.algorithms.Algorithm; 6 | import com.auth0.jwt.exceptions.InvalidClaimException; 7 | import com.auth0.jwt.exceptions.TokenExpiredException; 8 | import com.auth0.jwt.interfaces.Claim; 9 | import com.auth0.jwt.interfaces.DecodedJWT; 10 | import io.github.talelin.core.util.DateUtil; 11 | import io.github.talelin.core.constant.TokenConstant; 12 | 13 | import java.util.Date; 14 | import java.util.Map; 15 | 16 | /** 17 | * 支持双令牌模式 18 | * access、refresh token 19 | * 20 | * @author pedro@TaleLin 21 | * @author colorful@TaleLin 22 | */ 23 | public class DoubleJWT { 24 | 25 | private long accessExpire; 26 | 27 | private long refreshExpire; 28 | 29 | private Algorithm algorithm; 30 | 31 | private JWTVerifier accessVerifier; 32 | 33 | private JWTVerifier refreshVerifier; 34 | 35 | private JWTCreator.Builder builder; 36 | 37 | /** 38 | * 传入加密算法,双token模式 39 | * 40 | * @param algorithm 加密算法 41 | * @param accessExpire access_token过期时间 42 | * @param refreshExpire refresh_token过期时间 43 | */ 44 | public DoubleJWT(Algorithm algorithm, long accessExpire, long refreshExpire) { 45 | this.algorithm = algorithm; 46 | this.accessExpire = accessExpire; 47 | this.refreshExpire = refreshExpire; 48 | this.initBuilderAndVerifier(); 49 | } 50 | 51 | /** 52 | * 不传入加密算法,传入密钥,则默认使用 HMAC256 加密算法 53 | * 双token模式 54 | * 55 | * @param secret 加密算法 56 | * @param accessExpire access_token过期时间 57 | * @param refreshExpire refresh_token过期时间 58 | */ 59 | public DoubleJWT(String secret, long accessExpire, long refreshExpire) { 60 | this.algorithm = Algorithm.HMAC256(secret); 61 | this.accessExpire = accessExpire; 62 | this.refreshExpire = refreshExpire; 63 | this.initBuilderAndVerifier(); 64 | } 65 | 66 | public String generateToken(String tokenType, long identity, String scope, long expire) { 67 | Date expireDate = DateUtil.getDurationDate(expire); 68 | return builder 69 | .withClaim("type", tokenType) 70 | .withClaim("identity", identity) 71 | .withClaim("scope", scope) 72 | .withExpiresAt(expireDate) 73 | .sign(algorithm); 74 | } 75 | 76 | public String generateToken(String tokenType, String identity, String scope, long expire) { 77 | Date expireDate = DateUtil.getDurationDate(expire); 78 | return builder 79 | .withClaim("type", tokenType) 80 | .withClaim("identity", identity) 81 | .withClaim("scope", scope) 82 | .withExpiresAt(expireDate) 83 | .sign(algorithm); 84 | } 85 | 86 | public Map decodeAccessToken(String token) { 87 | DecodedJWT jwt = accessVerifier.verify(token); 88 | checkTokenExpired(jwt.getExpiresAt()); 89 | checkTokenType(jwt.getClaim("type").asString(), TokenConstant.ACCESS_TYPE); 90 | checkTokenScope(jwt.getClaim("scope").asString()); 91 | return jwt.getClaims(); 92 | } 93 | 94 | public Map decodeRefreshToken(String token) { 95 | DecodedJWT jwt = refreshVerifier.verify(token); 96 | checkTokenExpired(jwt.getExpiresAt()); 97 | checkTokenType(jwt.getClaim("type").asString(), TokenConstant.REFRESH_TYPE); 98 | checkTokenScope(jwt.getClaim("scope").asString()); 99 | return jwt.getClaims(); 100 | } 101 | 102 | private void checkTokenScope(String scope) { 103 | if (scope == null || !scope.equals(TokenConstant.LIN_SCOPE)) { 104 | throw new InvalidClaimException("token scope is invalid"); 105 | } 106 | } 107 | 108 | private void checkTokenType(String type, String accessType) { 109 | if (type == null || !type.equals(accessType)) { 110 | throw new InvalidClaimException("token type is invalid"); 111 | } 112 | } 113 | 114 | private void checkTokenExpired(Date expiresAt) { 115 | long now = System.currentTimeMillis(); 116 | if (expiresAt.getTime() < now) { 117 | throw new TokenExpiredException("token is expired"); 118 | } 119 | } 120 | 121 | public String generateAccessToken(long identity) { 122 | return generateToken(TokenConstant.ACCESS_TYPE, identity, TokenConstant.LIN_SCOPE, this.accessExpire); 123 | } 124 | 125 | public String generateAccessToken(String identity) { 126 | return generateToken(TokenConstant.ACCESS_TYPE, identity, TokenConstant.LIN_SCOPE, this.accessExpire); 127 | } 128 | 129 | public String generateRefreshToken(long identity) { 130 | return generateToken(TokenConstant.REFRESH_TYPE, identity, TokenConstant.LIN_SCOPE, this.refreshExpire); 131 | } 132 | 133 | public String generateRefreshToken(String identity) { 134 | return generateToken(TokenConstant.REFRESH_TYPE, identity, TokenConstant.LIN_SCOPE, this.refreshExpire); 135 | } 136 | 137 | public Tokens generateTokens(long identity) { 138 | String access = this.generateToken(TokenConstant.ACCESS_TYPE, identity, TokenConstant.LIN_SCOPE, this.accessExpire); 139 | String refresh = this.generateToken(TokenConstant.REFRESH_TYPE, identity, TokenConstant.LIN_SCOPE, this.refreshExpire); 140 | return new Tokens(access, refresh); 141 | } 142 | 143 | public Tokens generateTokens(String identity) { 144 | String access = this.generateToken(TokenConstant.ACCESS_TYPE, identity, TokenConstant.LIN_SCOPE, this.accessExpire); 145 | String refresh = this.generateToken(TokenConstant.REFRESH_TYPE, identity, TokenConstant.LIN_SCOPE, this.refreshExpire); 146 | return new Tokens(access, refresh); 147 | } 148 | 149 | 150 | /*** 151 | * 获得令牌的验证器 152 | * @return JWTVerifier 153 | */ 154 | public JWTVerifier getAccessVerifier() { 155 | return accessVerifier; 156 | } 157 | 158 | /*** 159 | * 获得令牌的验证器 160 | * @return JWTVerifier 161 | */ 162 | public JWTVerifier getRefreshVerifier() { 163 | return refreshVerifier; 164 | } 165 | 166 | /** 167 | * 获得令牌构建器 168 | * 169 | * @return Builder 170 | */ 171 | public JWTCreator.Builder getBuilder() { 172 | return builder; 173 | } 174 | 175 | 176 | /** 177 | * 获得加密方法 178 | * 179 | * @return Algorithm 180 | */ 181 | public Algorithm getAlgorithm() { 182 | return algorithm; 183 | } 184 | 185 | public Long getAccessExpire() { 186 | return accessExpire; 187 | } 188 | 189 | 190 | public Long getRefreshExpire() { 191 | return refreshExpire; 192 | } 193 | 194 | private void initBuilderAndVerifier() { 195 | accessVerifier = com.auth0.jwt.JWT.require(algorithm) 196 | .acceptExpiresAt(this.accessExpire) 197 | .build(); 198 | refreshVerifier = com.auth0.jwt.JWT.require(algorithm) 199 | .acceptExpiresAt(this.refreshExpire) 200 | .build(); 201 | builder = com.auth0.jwt.JWT.create(); 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /core/src/test/java/io/github/talelin/core/token/DoubleJWTTest.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.token; 2 | 3 | import com.auth0.jwt.algorithms.Algorithm; 4 | import com.auth0.jwt.exceptions.JWTVerificationException; 5 | import com.auth0.jwt.interfaces.Claim; 6 | import io.github.talelin.core.constant.TokenConstant; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.Map; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.junit.Assert.assertNotNull; 16 | 17 | public class DoubleJWTTest { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(DoubleJWTTest.class); 20 | 21 | @Test 22 | public void generateToken() { 23 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 24 | String token = jwt.generateToken("test", 1, "test", 1000); 25 | assertNotNull(token); 26 | log.info(token); 27 | } 28 | 29 | @Test 30 | public void decodeAccessToken() { 31 | DoubleJWT jwt = new DoubleJWT("secret", 10000, 20000); 32 | String token = jwt.generateAccessToken(1); 33 | assertNotNull(token); 34 | log.info(token); 35 | Map claimMap = jwt.decodeAccessToken(token); 36 | Assert.assertEquals(TokenConstant.LIN_SCOPE, claimMap.get("scope").asString()); 37 | Assert.assertEquals(TokenConstant.ACCESS_TYPE, claimMap.get("type").asString()); 38 | } 39 | 40 | @Test 41 | public void decodeRefreshToken() { 42 | DoubleJWT jwt = new DoubleJWT("secret", 10000, 20000); 43 | String token = jwt.generateRefreshToken(1); 44 | assertNotNull(token); 45 | log.info(token); 46 | Map claimMap = jwt.decodeRefreshToken(token); 47 | Assert.assertEquals(TokenConstant.LIN_SCOPE, claimMap.get("scope").asString()); 48 | Assert.assertEquals(TokenConstant.REFRESH_TYPE, claimMap.get("type").asString()); 49 | } 50 | 51 | @Test 52 | public void generateAccessToken() { 53 | DoubleJWT jwt = new DoubleJWT("secret", 10000, 20000); 54 | String token = jwt.generateAccessToken(1); 55 | assertNotNull(token); 56 | log.info(token); 57 | } 58 | 59 | @Test 60 | public void decodeAccessToken1() throws InterruptedException { 61 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 62 | String token = jwt.generateAccessToken(1); 63 | assertNotNull(token); 64 | log.info(token); 65 | Thread.sleep(1000); 66 | try { 67 | Map claimMap = jwt.decodeAccessToken(token); 68 | } catch (JWTVerificationException e) { 69 | assertEquals("token is expired", e.getMessage()); 70 | } 71 | } 72 | 73 | @Test 74 | public void decodeAccessToken2() throws InterruptedException { 75 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 76 | String token = jwt.generateAccessToken("Colorful"); 77 | assertNotNull(token); 78 | log.info(token); 79 | Thread.sleep(1000); 80 | try { 81 | Map claimMap = jwt.decodeAccessToken(token); 82 | System.out.println(claimMap); 83 | } catch (JWTVerificationException e) { 84 | assertEquals("token is expired", e.getMessage()); 85 | } 86 | } 87 | 88 | @Test 89 | public void decodeRefreshToken1() throws InterruptedException { 90 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 91 | String token = jwt.generateRefreshToken(1); 92 | assertNotNull(token); 93 | log.info(token); 94 | Thread.sleep(2100); 95 | try { 96 | Map claimMap = jwt.decodeRefreshToken(token); 97 | } catch (JWTVerificationException e) { 98 | assertEquals("token is expired", e.getMessage()); 99 | } 100 | } 101 | 102 | @Test 103 | public void decodeRefreshToken2() { 104 | DoubleJWT jwt = new DoubleJWT("secret", 6000, 2000); 105 | String token = jwt.generateRefreshToken(1); 106 | assertNotNull(token); 107 | log.info(token); 108 | try { 109 | Map claimMap = jwt.decodeAccessToken(token); 110 | } catch (JWTVerificationException e) { 111 | assertEquals("token type is invalid", e.getMessage()); 112 | } 113 | } 114 | 115 | @Test 116 | public void generateRefreshToken() { 117 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 118 | String token = jwt.generateRefreshToken(1); 119 | assertNotNull(token); 120 | log.info(token); 121 | } 122 | 123 | @Test 124 | public void generateRefreshToken1() { 125 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 126 | String token = jwt.generateRefreshToken("Colorful"); 127 | assertNotNull(token); 128 | log.info(token); 129 | } 130 | 131 | @Test 132 | public void generateTokens() { 133 | DoubleJWT jwt = new DoubleJWT("secret", 10000, 20000); 134 | Tokens tokens = jwt.generateTokens(1); 135 | assertNotNull(tokens.getAccessToken()); 136 | assertNotNull(tokens.getRefreshToken()); 137 | log.info("{}", tokens); 138 | 139 | Map claimMap = jwt.decodeAccessToken(tokens.getAccessToken()); 140 | Assert.assertEquals(TokenConstant.LIN_SCOPE, claimMap.get("scope").asString()); 141 | Assert.assertEquals(TokenConstant.ACCESS_TYPE, claimMap.get("type").asString()); 142 | } 143 | 144 | @Test 145 | public void generateTokens1() { 146 | DoubleJWT jwt = new DoubleJWT("secret", 10000, 20000); 147 | Tokens tokens = jwt.generateTokens("Colorful"); 148 | assertNotNull(tokens.getAccessToken()); 149 | assertNotNull(tokens.getRefreshToken()); 150 | log.info("{}", tokens); 151 | 152 | Map claimMap = jwt.decodeAccessToken(tokens.getAccessToken()); 153 | Assert.assertEquals(TokenConstant.LIN_SCOPE, claimMap.get("scope").asString()); 154 | Assert.assertEquals(TokenConstant.ACCESS_TYPE, claimMap.get("type").asString()); 155 | } 156 | 157 | @Test 158 | public void getAccessVerifier() { 159 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 160 | assertNotNull(jwt.getAccessVerifier()); 161 | } 162 | 163 | @Test 164 | public void getRefreshVerifier() { 165 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 166 | assertNotNull(jwt.getRefreshVerifier()); 167 | } 168 | 169 | @Test 170 | public void getBuilder() { 171 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 172 | assertNotNull(jwt.getBuilder()); 173 | } 174 | 175 | @Test 176 | public void getAlgorithm() { 177 | DoubleJWT jwt = new DoubleJWT("secret", 1000, 2000); 178 | assertNotNull(jwt.getAlgorithm()); 179 | } 180 | 181 | @Test 182 | public void getAccessExpire() { 183 | Algorithm algorithm = Algorithm.HMAC256("secret"); 184 | DoubleJWT jwt = new DoubleJWT(algorithm, 1000, 2000); 185 | assertEquals(1000L, (long) jwt.getAccessExpire()); 186 | } 187 | 188 | @Test 189 | public void getRefreshExpire() { 190 | Algorithm algorithm = Algorithm.HMAC256("secret"); 191 | DoubleJWT jwt = new DoubleJWT(algorithm, 1000, 2000); 192 | assertEquals(2000, (long) jwt.getRefreshExpire()); 193 | } 194 | } -------------------------------------------------------------------------------- /lin-cms-spring-boot-autoconfigure/src/main/java/io/github/talelin/autoconfigure/bean/PermissionMetaCollector.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.autoconfigure.bean; 2 | 3 | import io.github.talelin.core.annotation.AdminMeta; 4 | import io.github.talelin.core.annotation.GroupMeta; 5 | import io.github.talelin.core.annotation.LoginMeta; 6 | import io.github.talelin.core.annotation.PermissionMeta; 7 | import io.github.talelin.core.annotation.PermissionModule; 8 | import io.github.talelin.core.enumeration.UserLevel; 9 | import io.github.talelin.core.util.AnnotationUtil; 10 | import org.springframework.beans.factory.config.BeanPostProcessor; 11 | import org.springframework.core.annotation.AnnotationUtils; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.util.ReflectionUtils; 14 | import org.springframework.util.StringUtils; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import java.lang.reflect.Method; 18 | import java.util.Collection; 19 | import java.util.HashMap; 20 | import java.util.HashSet; 21 | import java.util.Map; 22 | import java.util.Set; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | 25 | /** 26 | * 路由信息收集器 27 | * 28 | * @author pedro@TaleLin 29 | * @author Juzi@TaleLin 30 | * @author colorful@TaleLin 31 | */ 32 | public class PermissionMetaCollector implements BeanPostProcessor { 33 | 34 | private Map metaMap = new ConcurrentHashMap<>(); 35 | 36 | private Map>> structuralMeta = new ConcurrentHashMap<>(); 37 | 38 | public PermissionMetaCollector() { 39 | } 40 | 41 | @Override 42 | public Object postProcessBeforeInitialization(Object bean, String beanName) { 43 | return bean; 44 | } 45 | 46 | /** 47 | * 扫描注解信息,并提取 48 | * 49 | * @param bean spring bean 50 | * @param beanName 名称 51 | * @return spring bean 52 | */ 53 | @Override 54 | public Object postProcessAfterInitialization(Object bean, String beanName) { 55 | Controller controllerAnnotation = AnnotationUtils.findAnnotation(bean.getClass(), Controller.class); 56 | // 非 Controller 类,无需检查权限信息 57 | if (controllerAnnotation == null) { 58 | return bean; 59 | } 60 | 61 | Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass()); 62 | for (Method method : methods) { 63 | AdminMeta adminMeta = AnnotationUtils.findAnnotation(method, AdminMeta.class); 64 | if (adminMeta != null && adminMeta.mount()) { 65 | String permission = StringUtils.isEmpty(adminMeta.value()) 66 | ? adminMeta.permission() : adminMeta.value(); 67 | putOneMetaInfo(method, permission, adminMeta.module(), UserLevel.ADMIN); 68 | continue; 69 | } 70 | GroupMeta groupMeta = AnnotationUtils.findAnnotation(method, GroupMeta.class); 71 | if (groupMeta != null && groupMeta.mount()) { 72 | String permission = StringUtils.isEmpty(groupMeta.value()) 73 | ? groupMeta.permission() : groupMeta.value(); 74 | putOneMetaInfo(method, permission, groupMeta.module(), UserLevel.GROUP); 75 | continue; 76 | } 77 | LoginMeta loginMeta = AnnotationUtils.findAnnotation(method, LoginMeta.class); 78 | if (loginMeta != null && loginMeta.mount()) { 79 | String permission = StringUtils.isEmpty(loginMeta.value()) 80 | ? loginMeta.permission() : loginMeta.value(); 81 | putOneMetaInfo(method, permission, loginMeta.module(), UserLevel.LOGIN); 82 | continue; 83 | } 84 | // 最后寻找 PermissionMeta 85 | PermissionMeta permissionMeta = AnnotationUtils.findAnnotation(method, 86 | PermissionMeta.class); 87 | if (permissionMeta != null && permissionMeta.mount()) { 88 | String permission = StringUtils.isEmpty(permissionMeta.value()) 89 | ? permissionMeta.permission() : permissionMeta.value(); 90 | UserLevel level = AnnotationUtil.findRequired(method.getAnnotations()); 91 | putOneMetaInfo(method, permission, permissionMeta.module(), level); 92 | } 93 | } 94 | return bean; 95 | } 96 | 97 | private void putOneMetaInfo(Method method, String permission, String module, 98 | UserLevel userLevel) { 99 | if (StringUtils.isEmpty(module)) { 100 | PermissionModule permissionModule = AnnotationUtils.findAnnotation( 101 | method.getDeclaringClass(), PermissionModule.class); 102 | if (permissionModule != null) { 103 | module = StringUtils.isEmpty(permissionModule.value()) ? 104 | method.getDeclaringClass().getName() : permissionModule.value(); 105 | } 106 | } 107 | String methodName = method.getName(); 108 | String className = method.getDeclaringClass().getName(); 109 | String identity = className + "#" + methodName; 110 | MetaInfo metaInfo = new MetaInfo(permission, module, identity, userLevel); 111 | metaMap.put(identity, metaInfo); 112 | this.putMetaIntoStructuralMeta(identity, metaInfo); 113 | } 114 | 115 | private void putMetaIntoStructuralMeta(String identity, MetaInfo meta) { 116 | String module = meta.getModule(); 117 | String permission = meta.getPermission(); 118 | // 如果已经存在了该 module,直接向里面增加 119 | if (structuralMeta.containsKey(module)) { 120 | Map> moduleMap = structuralMeta.get(module); 121 | // 如果 permission 已经存在 122 | this.putIntoModuleMap(moduleMap, identity, permission); 123 | } else { 124 | // 不存在 该 module,创建该 module 125 | Map> moduleMap = new HashMap<>(); 126 | // 如果 permission 已经存在 127 | this.putIntoModuleMap(moduleMap, identity, permission); 128 | structuralMeta.put(module, moduleMap); 129 | } 130 | } 131 | 132 | private void putIntoModuleMap(Map> moduleMap, String identity, 133 | String auth) { 134 | if (moduleMap.containsKey(auth)) { 135 | moduleMap.get(auth).add(identity); 136 | } else { 137 | Set eps = new HashSet<>(); 138 | eps.add(identity); 139 | moduleMap.put(auth, eps); 140 | } 141 | } 142 | 143 | /** 144 | * 获取路由信息map 145 | * 146 | * @return 路由信息map 147 | */ 148 | public Map getMetaMap() { 149 | return metaMap; 150 | } 151 | 152 | public MetaInfo findMeta(String key) { 153 | return metaMap.get(key); 154 | } 155 | 156 | public MetaInfo findMetaByPermission(String permission) { 157 | Collection values = metaMap.values(); 158 | MetaInfo[] objects = values.toArray(new MetaInfo[0]); 159 | for (MetaInfo object : objects) { 160 | if (object.getPermission().equals(permission)) { 161 | return object; 162 | } 163 | } 164 | return null; 165 | } 166 | 167 | public void setMetaMap(Map metaMap) { 168 | this.metaMap = metaMap; 169 | } 170 | 171 | /** 172 | * 获得结构化路由信息 173 | * 174 | * @return 路由信息 175 | */ 176 | public Map>> getStructuralMeta() { 177 | return structuralMeta; 178 | } 179 | 180 | public void setStructrualMeta(Map>> structuralMeta) { 181 | this.structuralMeta = structuralMeta; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /core/src/main/java/io/github/talelin/core/logger/AdvanceRollingFileAppender.java: -------------------------------------------------------------------------------- 1 | package io.github.talelin.core.logger; 2 | 3 | import ch.qos.logback.core.CoreConstants; 4 | import ch.qos.logback.core.FileAppender; 5 | import ch.qos.logback.core.recovery.ResilientFileOutputStream; 6 | import ch.qos.logback.core.util.ContextUtil; 7 | import ch.qos.logback.core.util.FileSize; 8 | import ch.qos.logback.core.util.FileUtil; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.nio.channels.FileChannel; 13 | import java.nio.channels.FileLock; 14 | import java.nio.file.FileSystems; 15 | import java.nio.file.Path; 16 | import java.text.SimpleDateFormat; 17 | import java.util.Date; 18 | import java.util.Map; 19 | 20 | /** 21 | * 自定义日志记录 Appender 22 | * @param 时间 23 | * 24 | * @author pedro@TaleLin 25 | * @author Juzi@TaleLin 26 | */ 27 | @Deprecated 28 | public class AdvanceRollingFileAppender extends FileAppender { 29 | 30 | File currentlyActiveFile; 31 | 32 | public static final long DEFAULT_BUFFER_SIZE = 8192; 33 | 34 | /** 35 | * Append to or truncate the file? The default value for this variable is 36 | * true, meaning that by default a FileAppender will 37 | * append to an existing file and not truncate it. 38 | */ 39 | protected boolean append = true; 40 | 41 | /** 42 | * The name of the active log file. 43 | */ 44 | protected String fileName = null; 45 | 46 | private boolean prudent = false; 47 | 48 | private FileSize bufferSize = new FileSize(DEFAULT_BUFFER_SIZE); 49 | 50 | private String dir; 51 | 52 | /** 53 | * 最大文件大小 10MB 54 | */ 55 | public static final long DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; 56 | 57 | FileSize maxFileSize = new FileSize(DEFAULT_MAX_FILE_SIZE); 58 | 59 | /** 60 | * 获取日志文件目录 61 | * 62 | * @return dir 63 | */ 64 | public String getDir() { 65 | return dir; 66 | } 67 | 68 | /** 69 | * 设置日志文件目录 70 | * 71 | * @param dir 72 | */ 73 | public void setDir(String dir) { 74 | if (this.isAbsolute(dir)) { 75 | this.dir = dir; 76 | } else { 77 | String cmd = System.getProperty("user.dir"); 78 | Path path = FileSystems.getDefault().getPath(cmd, dir); 79 | this.dir = path.toAbsolutePath().toString(); 80 | } 81 | } 82 | 83 | public FileSize getMaxFileSize() { 84 | return maxFileSize; 85 | } 86 | 87 | public void setMaxFileSize(FileSize maxFileSize) { 88 | this.maxFileSize = maxFileSize; 89 | } 90 | 91 | /** 92 | * The File property takes a string value which should be the name of 93 | * the file to append to. 94 | */ 95 | @Override 96 | public void setFile(String file) { 97 | if (file == null) { 98 | Date now = new Date(); 99 | String subDir = this.getPresentTime(now, "yyyy-MM"); 100 | String filename = this.getPresentTime(now, "yyyy-MM-dd"); 101 | String trueFilename = String.format("%s/%s/%s.log", this.dir, subDir, filename); 102 | this.fileName = trueFilename; 103 | } else { 104 | this.fileName = file; 105 | } 106 | } 107 | 108 | /** 109 | * Returns the value of the Append property. 110 | */ 111 | @Override 112 | public boolean isAppend() { 113 | return append; 114 | } 115 | 116 | /** 117 | * Returns the value of the File property. 118 | * 119 | *

120 | * This method may be overridden by derived classes. 121 | */ 122 | @Override 123 | public String getFile() { 124 | return fileName; 125 | } 126 | 127 | /** 128 | * yyyy-MM-dd 129 | * yyyy-MM-dd hh:mm:ss 130 | * yyyy-MM 131 | * 132 | * @param format 格式 133 | * @return 字符串 134 | */ 135 | public String getPresentTime(String format) { 136 | Date now = new Date(); 137 | SimpleDateFormat dateFormat = new SimpleDateFormat(format); 138 | return dateFormat.format(now); 139 | } 140 | 141 | public String getPresentTime(Date date, String format) { 142 | SimpleDateFormat dateFormat = new SimpleDateFormat(format); 143 | return dateFormat.format(date); 144 | } 145 | 146 | /** 147 | * If the value of File is not null, then 148 | * {@link #openFile} is called with the values of File and 149 | * Append properties. 150 | */ 151 | @Override 152 | public void start() { 153 | if (this.dir == null) { 154 | addError("log dir must be not be empty. Aborting."); 155 | return; 156 | } 157 | this.setFile(null); 158 | // we don't want to void existing log files 159 | if (!append) { 160 | addWarn("Append mode is mandatory for RollingFileAppender. Defaulting to append=true."); 161 | append = true; 162 | } 163 | if (isPrudent()) { 164 | if (rawFileProperty() != null) { 165 | addWarn("Setting \"File\" property to null on account of prudent mode"); 166 | setFile(null); 167 | } 168 | } 169 | currentlyActiveFile = new File(getFile()); 170 | addInfo("Active log file name: " + getFile()); 171 | super.start(); 172 | } 173 | 174 | @Override 175 | public void stop() { 176 | super.stop(); 177 | 178 | Map map = ContextUtil.getFilenameCollisionMap(context); 179 | if (map == null || getName() == null) { 180 | return; 181 | } 182 | 183 | map.remove(getName()); 184 | } 185 | 186 | @Override 187 | protected boolean checkForFileCollisionInPreviousFileAppenders() { 188 | boolean collisionsDetected = false; 189 | if (fileName == null) { 190 | return false; 191 | } 192 | @SuppressWarnings("unchecked") 193 | Map map = (Map) context.getObject(CoreConstants.FA_FILENAME_COLLISION_MAP); 194 | if (map == null) { 195 | return collisionsDetected; 196 | } 197 | for (Map.Entry entry : map.entrySet()) { 198 | if (fileName.equals(entry.getValue())) { 199 | addErrorForCollision("File", entry.getValue(), entry.getKey()); 200 | collisionsDetected = true; 201 | } 202 | } 203 | if (name != null) { 204 | map.put(getName(), fileName); 205 | } 206 | return collisionsDetected; 207 | } 208 | 209 | @Override 210 | protected void addErrorForCollision(String optionName, String optionValue, String appenderName) { 211 | addError("'" + optionName + "' option has the same value \"" + optionValue + "\" as that given for appender [" + appenderName + "] defined earlier."); 212 | } 213 | 214 | /** 215 | *

216 | * Sets and opens the file where the log output will go. The specified 217 | * file must be writable. 218 | * 219 | *

220 | * If there was already an opened file, then the previous file is closed 221 | * first. 222 | * 223 | *

224 | * Do not use this method directly. To configure a FileAppender or one of 225 | * its subclasses, set its properties one by one and then call start(). 226 | * 227 | * @param fileName The path to the log file. 228 | */ 229 | @Override 230 | public void openFile(String fileName) throws IOException { 231 | lock.lock(); 232 | try { 233 | File file = new File(fileName); 234 | boolean result = FileUtil.createMissingParentDirectories(file); 235 | if (!result) { 236 | addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]"); 237 | } 238 | 239 | ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize()); 240 | resilientFos.setContext(context); 241 | setOutputStream(resilientFos); 242 | } finally { 243 | lock.unlock(); 244 | } 245 | } 246 | 247 | /** 248 | * @return true if in prudent mode 249 | * @see #setPrudent(boolean) 250 | */ 251 | @Override 252 | public boolean isPrudent() { 253 | return prudent; 254 | } 255 | 256 | /** 257 | * When prudent is set to true, file appenders from multiple JVMs can safely 258 | * write to the same file. 259 | * 260 | * @param prudent 261 | */ 262 | @Override 263 | public void setPrudent(boolean prudent) { 264 | this.prudent = prudent; 265 | } 266 | 267 | @Override 268 | public void setAppend(boolean append) { 269 | this.append = append; 270 | } 271 | 272 | @Override 273 | public void setBufferSize(FileSize bufferSize) { 274 | addInfo("Setting bufferSize to [" + bufferSize.toString() + "]"); 275 | this.bufferSize = bufferSize; 276 | } 277 | 278 | private void safeWrite(E event) throws IOException { 279 | ResilientFileOutputStream resilientFileOutputStream = (ResilientFileOutputStream) getOutputStream(); 280 | FileChannel fileChannel = resilientFileOutputStream.getChannel(); 281 | if (fileChannel == null) { 282 | return; 283 | } 284 | 285 | // Clear any current interrupt (see LOGBACK-875) 286 | boolean interrupted = Thread.interrupted(); 287 | 288 | FileLock fileLock = null; 289 | try { 290 | fileLock = fileChannel.lock(); 291 | long position = fileChannel.position(); 292 | long size = fileChannel.size(); 293 | if (size != position) { 294 | fileChannel.position(size); 295 | } 296 | super.writeOut(event); 297 | } catch (IOException e) { 298 | // Mainly to catch FileLockInterruptionExceptions (see LOGBACK-875) 299 | resilientFileOutputStream.postIOFailure(e); 300 | } finally { 301 | if (fileLock != null && fileLock.isValid()) { 302 | fileLock.release(); 303 | } 304 | 305 | // Re-interrupt if we started in an interrupted state (see LOGBACK-875) 306 | if (interrupted) { 307 | Thread.currentThread().interrupt(); 308 | } 309 | } 310 | } 311 | 312 | /** 313 | * Implemented by delegating most of the rollover work to a rolling policy. 314 | */ 315 | public void rollover() { 316 | lock.lock(); 317 | try { 318 | // Note: This method needs to be synchronized because it needs exclusive 319 | // access while it closes and then re-opens the target file. 320 | // 321 | // make sure to close the hereto active log file! Renaming under windows 322 | // does not work for open files. 323 | this.closeOutputStream(); 324 | attemptRollover(); 325 | attemptOpenFile(); 326 | } finally { 327 | lock.unlock(); 328 | } 329 | } 330 | 331 | private void attemptOpenFile() { 332 | try { 333 | this.setFile(null); 334 | // update the currentlyActiveFile LOGBACK-64 335 | currentlyActiveFile = new File(getFile()); 336 | 337 | // This will also close the file. This is OK since multiple close operations are safe. 338 | this.openFile(getFile()); 339 | } catch (IOException e) { 340 | addError("setFile(" + fileName + ", false) call failed.", e); 341 | } 342 | } 343 | 344 | private void attemptRollover() { 345 | File renamedFile = getRenameFile(); 346 | currentlyActiveFile.renameTo(renamedFile); 347 | } 348 | 349 | /** 350 | * This method differentiates RollingFileAppender from its super class. 351 | */ 352 | @Override 353 | protected void subAppend(E event) { 354 | // 需要同步 355 | synchronized (this) { 356 | // 1. size 超过 357 | if (currentlyActiveFile.length() >= maxFileSize.getSize()) { 358 | this.rollover(); 359 | } 360 | // 2. 时间,过了一天 361 | if (!this.checkIsPresent()) { 362 | this.rollover(); 363 | } 364 | } 365 | super.subAppend(event); 366 | } 367 | 368 | /** 369 | * 检查当天的日志文件是否存在 370 | * 371 | * @return 372 | */ 373 | private boolean checkIsPresent() { 374 | this.setFile(null); 375 | File f = new File(getFile()); 376 | return f.exists(); 377 | } 378 | 379 | private File getRenameFile() { 380 | Date now = new Date(); 381 | String t1 = this.getPresentTime(now, "yyyy-MM"); 382 | String t2 = this.getPresentTime(now, "yyyy-MM-dd"); 383 | String t3 = this.getPresentTime(now, "hh:mm:ss"); 384 | String trueFilename = String.format("%s/%s/%s-%s.log", this.dir, t1, t2, t3); 385 | File file = new File(trueFilename); 386 | return file; 387 | } 388 | 389 | /** 390 | * 重写 append 391 | * 392 | * @param eventObject 393 | */ 394 | @Override 395 | protected void append(E eventObject) { 396 | if (!isStarted()) { 397 | return; 398 | } 399 | subAppend(eventObject); 400 | } 401 | 402 | @Override 403 | protected void writeOut(E event) throws IOException { 404 | if (prudent) { 405 | safeWrite(event); 406 | } else { 407 | super.writeOut(event); 408 | } 409 | } 410 | 411 | @SuppressWarnings("Since15") 412 | private boolean isAbsolute(String str) { 413 | Path path = FileSystems.getDefault().getPath(str); 414 | return path.isAbsolute(); 415 | } 416 | } 417 | --------------------------------------------------------------------------------