├── keeper-examples ├── keeper-spring-boot-session │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── keeper │ │ │ ├── model │ │ │ ├── User.java │ │ │ └── Response.java │ │ │ ├── SessionApplication.java │ │ │ ├── service │ │ │ └── UserService.java │ │ │ ├── config │ │ │ ├── KeeperBeanConfig.java │ │ │ ├── GlobalExceptionHandler.java │ │ │ └── AuthorizationBean.java │ │ │ └── controller │ │ │ ├── PermController.java │ │ │ └── LoginController.java │ └── pom.xml ├── keeper-spring-boot-jwt │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── keeper │ │ │ ├── model │ │ │ ├── User.java │ │ │ └── Response.java │ │ │ ├── JwtApplication.java │ │ │ ├── service │ │ │ └── UserService.java │ │ │ ├── controller │ │ │ ├── PermController.java │ │ │ └── LoginController.java │ │ │ └── config │ │ │ ├── GlobalExceptionHandler.java │ │ │ ├── JwtAuthorizeBean.java │ │ │ └── KeeperBeanConfig.java │ └── pom.xml ├── keeper-spring-boot-session-redis │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── keeper │ │ │ ├── model │ │ │ ├── User.java │ │ │ └── Response.java │ │ │ ├── SessionRedisApplication.java │ │ │ ├── service │ │ │ └── UserService.java │ │ │ ├── config │ │ │ ├── GlobalExceptionHandler.java │ │ │ ├── KepperAuthorizeBean.java │ │ │ └── KeeperBeanConfig.java │ │ │ └── controller │ │ │ ├── PermController.java │ │ │ └── LoginController.java │ └── pom.xml ├── keeper-spring-boot-jwt-redis │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── keeper │ │ │ ├── model │ │ │ ├── User.java │ │ │ └── Response.java │ │ │ ├── JwtRedisApplication.java │ │ │ ├── service │ │ │ └── UserService.java │ │ │ ├── controller │ │ │ ├── PermController.java │ │ │ └── LoginController.java │ │ │ └── config │ │ │ ├── GlobalExceptionHandler.java │ │ │ ├── KeeperAuthorizeBean.java │ │ │ └── KeeperBeanConfig.java │ └── pom.xml └── pom.xml ├── .travis.yml ├── keeper-spring-boot-web-starter ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring.factories │ │ └── java │ │ └── io │ │ └── github │ │ └── biezhi │ │ └── keeper │ │ └── starter │ │ ├── KeeperProperties.java │ │ └── KeeperAutoConfiguration.java └── pom.xml ├── keeper-core ├── src │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── biezhi │ │ └── keeper │ │ ├── core │ │ ├── config │ │ │ ├── KeeperConfig.java │ │ │ ├── SessionConfig.java │ │ │ └── JwtConfig.java │ │ ├── authc │ │ │ ├── AuthenticInfo.java │ │ │ ├── cipher │ │ │ │ ├── BCryptCipher.java │ │ │ │ ├── Md5Cipher.java │ │ │ │ ├── Cipher.java │ │ │ │ └── EqualsCipher.java │ │ │ ├── impl │ │ │ │ ├── SimpleAuthorizeInfo.java │ │ │ │ ├── SimpleAuthenticInfo.java │ │ │ │ ├── SimpleAuthorToken.java │ │ │ │ └── Tokens.java │ │ │ ├── Authentication.java │ │ │ ├── AuthorToken.java │ │ │ ├── AuthorizeInfo.java │ │ │ └── Authorization.java │ │ ├── cache │ │ │ ├── Cache.java │ │ │ ├── map │ │ │ │ └── MapCache.java │ │ │ ├── AuthorizeCache.java │ │ │ └── redis │ │ │ │ └── RedisCache.java │ │ ├── subject │ │ │ ├── Subject.java │ │ │ ├── JwtSubject.java │ │ │ ├── SessionSubject.java │ │ │ └── SimpleSubject.java │ │ ├── jwt │ │ │ ├── JwtToken.java │ │ │ └── SimpleJwtToken.java │ │ ├── aspect │ │ │ └── KeeperAspect.java │ │ └── web │ │ │ └── filter │ │ │ └── AuthenticFilter.java │ │ ├── utils │ │ ├── DateUtil.java │ │ ├── StringUtil.java │ │ ├── CipherUtil.java │ │ ├── JsonUtil.java │ │ ├── SpringContextUtil.java │ │ └── WebUtil.java │ │ ├── exception │ │ ├── WrongPasswordException.java │ │ ├── UnauthenticException.java │ │ ├── ExpiredException.java │ │ ├── KeeperException.java │ │ └── UnauthorizedException.java │ │ ├── enums │ │ ├── Logical.java │ │ └── SubjectType.java │ │ ├── annotation │ │ ├── Permissions.java │ │ └── Roles.java │ │ ├── keeperConst.java │ │ └── Keeper.java └── pom.xml ├── README.md ├── .gitignore ├── pom.xml └── LICENSE /keeper-examples/keeper-spring-boot-session/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9991 3 | 4 | keeper: 5 | session: 6 | renewExpires: 7d 7 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9991 3 | 4 | keeper: 5 | jwt: 6 | expires: 20s 7 | renewExpires: 7d -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false 3 | 4 | jdk: 5 | - oraclejdk8 6 | 7 | notifications: 8 | email: false 9 | 10 | after_success: 11 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /keeper-spring-boot-web-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | io.github.biezhi.keeper.starter.KeeperAutoConfiguration -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9991 3 | 4 | spring: 5 | redis: 6 | database: 0 7 | host: localhost 8 | port: 6379 9 | keeper: 10 | session: 11 | renewExpires: 7d 12 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9991 3 | 4 | spring: 5 | redis: 6 | database: 0 7 | host: localhost 8 | port: 6379 9 | keeper: 10 | jwt: 11 | expires: 30s 12 | renewExpires: 60s 13 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class User { 7 | 8 | private String username; 9 | private String password; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/config/KeeperConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.config; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author biezhi 7 | * @date 2019-04-09 8 | */ 9 | @Data 10 | public class KeeperConfig { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class User { 7 | 8 | private String username; 9 | private String password; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class User { 7 | 8 | private String username; 9 | private String password; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class User { 7 | 8 | private String username; 9 | private String password; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/JwtApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class JwtApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(JwtApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/utils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.utils; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * DateUtil 9 | * 10 | * @author biezhi 11 | * @since 2019/4/9 12 | */ 13 | @UtilityClass 14 | public class DateUtil { 15 | 16 | public Date plus(long millis){ 17 | return new Date(System.currentTimeMillis() + millis); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/model/Response.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class Response { 13 | 14 | private int code; 15 | private String msg; 16 | private T data; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/model/Response.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class Response { 13 | 14 | private int code; 15 | private String msg; 16 | private T data; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/model/Response.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class Response { 13 | 14 | private int code; 15 | private String msg; 16 | private T data; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/model/Response.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class Response { 13 | 14 | private int code; 15 | private String msg; 16 | private T data; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/SessionApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SessionApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SessionApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/JwtRedisApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class JwtRedisApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(JwtRedisApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/SessionRedisApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SessionRedisApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SessionRedisApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/utils/StringUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.utils; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | /** 6 | * StringUtil 7 | * 8 | * @author biezhi 9 | * @since 2019/4/9 10 | */ 11 | @UtilityClass 12 | public class StringUtil { 13 | 14 | public boolean isEmpty(String value) { 15 | if (null == value || "".equals(value.trim())) { 16 | return true; 17 | } 18 | return false; 19 | } 20 | 21 | public boolean isNotEmpty(String value) { 22 | return null != value && "".equals(value.trim()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /keeper-spring-boot-web-starter/src/main/java/io/github/biezhi/keeper/starter/KeeperProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.starter; 2 | 3 | import io.github.biezhi.keeper.core.config.JwtConfig; 4 | import io.github.biezhi.keeper.core.config.SessionConfig; 5 | import lombok.Data; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | 8 | /** 9 | * @author biezhi 10 | * @date 2019-04-05 11 | */ 12 | @Data 13 | @ConfigurationProperties("keeper") 14 | public class KeeperProperties { 15 | 16 | private JwtConfig jwt = new JwtConfig(); 17 | private SessionConfig session = new SessionConfig(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/AuthenticInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.authc; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author biezhi 7 | * @date 2019-04-09 8 | */ 9 | public interface AuthenticInfo { 10 | 11 | String username(); 12 | 13 | String password(); 14 | 15 | /** 16 | * Payload information when logging in, optional 17 | * 18 | * @return payload 19 | */ 20 | default Object payload() { 21 | return null; 22 | } 23 | 24 | /** 25 | * Context information to be stored when logging in, optional 26 | * 27 | * @return claims 28 | */ 29 | default Map claims() { 30 | return null; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # keeper 2 | 3 | --- 4 | 5 | [![](https://img.shields.io/travis/biezhi/keeper.svg)](https://travis-ci.org/biezhi/keeper) 6 | [![](https://img.shields.io/maven-central/v/io.github.biezhi/keeper-spring-boot-web-starter.svg)](https://search.maven.org/search?q=keeper-spring-boot-web-starter) 7 | [![](https://img.shields.io/badge/license-Apache2-FF0080.svg)](https://github.com/biezhi/excel-plus/blob/master/LICENSE) 8 | 9 | keeper 是解决 SpringMVC Web 工程的认证、授权问题而生,和 SpringBoot 完美结合,自带 JWT 方案,支持缓存和自定义扩展。 10 | 11 | ## Features 12 | 13 | ## Usage 14 | 15 | **SpringBoot** 16 | 17 | ```xml 18 | 19 | io.github.biezhi 20 | keeper-spring-boot-web-starter 21 | 0.0.1-beta 22 | 23 | ``` 24 | 25 | ## License 26 | 27 | [Apache2](LICENSE) -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/exception/WrongPasswordException.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.exception; 2 | 3 | import lombok.NoArgsConstructor; 4 | 5 | import static io.github.biezhi.keeper.keeperConst.ERROR_MESSAGE_WRONG_PASSWORD; 6 | 7 | /** 8 | * WrongPasswordException 9 | * 10 | * @author biezhi 11 | * @since 2019/4/9 12 | */ 13 | @NoArgsConstructor 14 | public class WrongPasswordException extends UnauthenticException { 15 | 16 | public WrongPasswordException(String message) { 17 | super(message); 18 | } 19 | 20 | public static WrongPasswordException build() { 21 | return build(ERROR_MESSAGE_WRONG_PASSWORD); 22 | } 23 | 24 | public static WrongPasswordException build(String msg) { 25 | return new WrongPasswordException(msg); 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/enums/Logical.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.enums; 17 | 18 | public enum Logical { 19 | AND, OR 20 | } 21 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/cipher/BCryptCipher.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.authc.cipher; 2 | 3 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 4 | import io.github.biezhi.keeper.core.authc.AuthorToken; 5 | import io.github.biezhi.keeper.utils.BCrypt; 6 | 7 | /** 8 | * BCryptCipher 9 | * 10 | * @author biezhi 11 | * @since 2019/4/9 12 | */ 13 | public class BCryptCipher implements Cipher { 14 | 15 | @Override 16 | public boolean verify(AuthorToken token, AuthenticInfo authenticInfo) { 17 | String rawPassword = tokenCipher(token); 18 | String encryptPassword = authenticCipher(authenticInfo); 19 | if (null == rawPassword || null == encryptPassword) { 20 | return false; 21 | } 22 | return BCrypt.checkpw(rawPassword, encryptPassword); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.service; 2 | 3 | import com.example.keeper.model.User; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | @Service 11 | public class UserService { 12 | 13 | public User findByUsername(String username) { 14 | User user = new User(); 15 | user.setUsername(username); 16 | user.setPassword("123456"); 17 | return user; 18 | } 19 | 20 | public Set findRoles(String username) { 21 | return new HashSet<>(Arrays.asList("admin")); 22 | } 23 | 24 | public Set findPermissions(String username) { 25 | return new HashSet<>(Arrays.asList("user:edit", "user:view")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/cipher/Md5Cipher.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.authc.cipher; 2 | 3 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 4 | import io.github.biezhi.keeper.core.authc.AuthorToken; 5 | import io.github.biezhi.keeper.utils.CipherUtil; 6 | 7 | /** 8 | * Md5Cipher 9 | * 10 | * @author biezhi 11 | * @since 2019/4/9 12 | */ 13 | public class Md5Cipher implements Cipher { 14 | 15 | @Override 16 | public boolean verify(AuthorToken token, AuthenticInfo authenticInfo) { 17 | String rawPassword = tokenCipher(token); 18 | String encryptPassword = authenticCipher(authenticInfo); 19 | if (null == rawPassword || null == encryptPassword) { 20 | return false; 21 | } 22 | return encryptPassword.equalsIgnoreCase(CipherUtil.md5(rawPassword)); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.service; 2 | 3 | import com.example.keeper.model.User; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | @Service 11 | public class UserService { 12 | 13 | public Set findRoles(String username) { 14 | return new HashSet<>(Arrays.asList("admin")); 15 | } 16 | 17 | public Set findPermissions(String username) { 18 | return new HashSet<>(Arrays.asList("user:edit", "user:view")); 19 | } 20 | 21 | public User findByUsername(String username) { 22 | User user = new User(); 23 | user.setUsername(username); 24 | user.setPassword("e10adc3949ba59abbe56e057f20f883e"); 25 | return user; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/enums/SubjectType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.enums; 17 | 18 | /** 19 | * SubjectType 20 | * 21 | * @author biezhi 22 | * @date 2019-04-05 23 | */ 24 | public enum SubjectType { 25 | 26 | SESSION, JWT 27 | 28 | } 29 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/cipher/Cipher.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.authc.cipher; 2 | 3 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 4 | import io.github.biezhi.keeper.core.authc.AuthorToken; 5 | 6 | /** 7 | * Cipher 8 | * 9 | * @author biezhi 10 | * @since 2019/4/9 11 | */ 12 | public interface Cipher { 13 | 14 | Cipher MD5 = new Md5Cipher(); 15 | Cipher EQUALS = new EqualsCipher(); 16 | Cipher BCRYPT = new BCryptCipher(); 17 | 18 | boolean verify(AuthorToken token, AuthenticInfo authenticInfo); 19 | 20 | default String tokenCipher(AuthorToken token) { 21 | if (null == token) { 22 | return null; 23 | } 24 | return token.password(); 25 | } 26 | 27 | default String authenticCipher(AuthenticInfo token) { 28 | if (null == token) { 29 | return null; 30 | } 31 | return token.password(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.service; 2 | 3 | import com.example.keeper.model.User; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | @Service 11 | public class UserService { 12 | 13 | public User login(String username, String password) { 14 | User user = new User(); 15 | user.setUsername(username); 16 | user.setPassword(password); 17 | return user; 18 | } 19 | 20 | public User findByUsername(String username) { 21 | User user = new User(); 22 | user.setUsername(username); 23 | user.setPassword("123456"); 24 | return user; 25 | } 26 | 27 | public Set findRoles(String username) { 28 | return new HashSet<>(Arrays.asList("admin")); 29 | } 30 | 31 | public Set findPermissions(String username) { 32 | return new HashSet<>(Arrays.asList("user:edit", "user:view")); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.service; 2 | 3 | import com.example.keeper.model.User; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | @Service 11 | public class UserService { 12 | 13 | public User login(String username, String password) { 14 | User user = new User(); 15 | user.setUsername(username); 16 | user.setPassword(password); 17 | return user; 18 | } 19 | 20 | public Set findRoles(String username) { 21 | return new HashSet<>(Arrays.asList("admin")); 22 | } 23 | 24 | public Set findPermissions(String username) { 25 | return new HashSet<>(Arrays.asList("user:edit", "user:view")); 26 | } 27 | 28 | public User findByUsername(String username) { 29 | User user = new User(); 30 | user.setUsername(username); 31 | user.setPassword("e10adc3949ba59abbe56e057f20f883e"); 32 | return user; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/cipher/EqualsCipher.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.authc.cipher; 2 | 3 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 4 | import io.github.biezhi.keeper.core.authc.AuthorToken; 5 | 6 | /** 7 | * EqualsCipher 8 | * 9 | * @author biezhi 10 | * @since 2019/4/9 11 | */ 12 | public class EqualsCipher implements Cipher { 13 | 14 | private boolean ignoreCase; 15 | 16 | public EqualsCipher() { 17 | this(false); 18 | } 19 | 20 | public EqualsCipher(boolean ignoreCase) { 21 | this.ignoreCase = ignoreCase; 22 | } 23 | 24 | @Override 25 | public boolean verify(AuthorToken token, AuthenticInfo authenticInfo) { 26 | String rawPassword = tokenCipher(token); 27 | String encryptPassword = authenticCipher(authenticInfo); 28 | if (null == rawPassword || null == encryptPassword) { 29 | return false; 30 | } 31 | if (ignoreCase) { 32 | return encryptPassword.equalsIgnoreCase(rawPassword); 33 | } 34 | return encryptPassword.equals(rawPassword); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/annotation/Permissions.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.annotation; 17 | 18 | import io.github.biezhi.keeper.enums.Logical; 19 | 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | @Target({ElementType.TYPE, ElementType.METHOD}) 26 | @Retention(RetentionPolicy.RUNTIME) 27 | public @interface Permissions { 28 | 29 | String[] value(); 30 | 31 | Logical logical() default Logical.AND; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/utils/CipherUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.utils; 2 | 3 | import lombok.experimental.UtilityClass; 4 | 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | /** 9 | * CipherUtil 10 | * 11 | * @author biezhi 12 | * @since 2019/4/9 13 | */ 14 | @UtilityClass 15 | public class CipherUtil { 16 | 17 | public String md5(String text) { 18 | byte[] data = text.getBytes(); 19 | MessageDigest md5 = null; 20 | try { 21 | md5 = MessageDigest.getInstance("MD5"); 22 | md5.update(data); 23 | } catch (NoSuchAlgorithmException e) { 24 | return ""; 25 | } 26 | byte[] resultBytes = md5.digest(); 27 | 28 | StringBuilder builder = new StringBuilder(); 29 | 30 | for (int i = 0; i < resultBytes.length; i++) { 31 | if (Integer.toHexString(0xFF & resultBytes[i]).length() == 1) { 32 | builder.append("0").append( 33 | Integer.toHexString(0xFF & resultBytes[i])); 34 | } else { 35 | builder.append(Integer.toHexString(0xFF & resultBytes[i])); 36 | } 37 | } 38 | return builder.toString(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/config/SessionConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.config; 17 | 18 | import lombok.Data; 19 | 20 | import java.time.Duration; 21 | 22 | /** 23 | * SessionConfig 24 | * 25 | * @author biezhi 26 | * @date 2019-04-08 27 | */ 28 | @Data 29 | public class SessionConfig { 30 | 31 | private String cookieName = "kid"; 32 | private String secret = "keeper"; 33 | private Duration renewExpires; 34 | private String domain; 35 | private String path = "/"; 36 | private boolean secure; 37 | private boolean isHttpOnly; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/config/KeeperBeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.core.web.filter.AuthenticFilter; 5 | import io.github.biezhi.keeper.utils.WebUtil; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | @Configuration 13 | public class KeeperBeanConfig { 14 | 15 | @Bean 16 | public AuthenticFilter authenticFilter() { 17 | return new AuthenticFilter() { 18 | @Override 19 | protected void initFilterBean() { 20 | this.addPathPatterns("/**") 21 | .excludePathPatterns("/guest", "/login"); 22 | } 23 | 24 | @Override 25 | protected void unAuthentic(HttpServletRequest request, HttpServletResponse response) { 26 | WebUtil.writeJSON(response, 27 | Response.builder().code(500) 28 | .msg("请登录后访问") 29 | .build()); 30 | } 31 | }; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/impl/SimpleAuthorizeInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.authc.impl; 17 | 18 | import io.github.biezhi.keeper.core.authc.AuthorizeInfo; 19 | import lombok.Data; 20 | 21 | import java.util.Set; 22 | 23 | @Data 24 | public class SimpleAuthorizeInfo implements AuthorizeInfo { 25 | 26 | private Set roles; 27 | private Set permissions; 28 | 29 | @Override 30 | public Set getRoles() { 31 | return roles; 32 | } 33 | 34 | @Override 35 | public Set getPermissions() { 36 | return permissions; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/cache/Cache.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.cache; 17 | 18 | 19 | import java.util.Set; 20 | 21 | /** 22 | * Cache 23 | * 24 | * @author biezhi 25 | * @date 2019-04-07 26 | */ 27 | public interface Cache { 28 | 29 | void set(K key, V value); 30 | 31 | void set(K key, V value, long seconds); 32 | 33 | T get(K key, Class type); 34 | 35 | void delWith(String keyPrefix); 36 | 37 | boolean exists(K key); 38 | 39 | boolean expire(K key); 40 | 41 | void remove(K key); 42 | 43 | Set keySet(); 44 | 45 | void clear(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/Authentication.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.authc; 2 | 3 | import io.github.biezhi.keeper.core.authc.cipher.Cipher; 4 | import io.github.biezhi.keeper.core.authc.cipher.EqualsCipher; 5 | import io.github.biezhi.keeper.core.authc.cipher.Md5Cipher; 6 | import io.github.biezhi.keeper.exception.KeeperException; 7 | 8 | /** 9 | * Authentication 10 | * 11 | * @author biezhi 12 | * @date 2019-04-09 13 | */ 14 | public interface Authentication { 15 | 16 | /** 17 | * User authentication is performed, 18 | * and the method is only used to load the authentication information without authentication 19 | * 20 | * @param token login information 21 | * @return return the identity information to be stored after successful login, 22 | * either in session or in a custom cache 23 | * @throws KeeperException thrown when the login fails or an exception occurs 24 | */ 25 | AuthenticInfo doAuthentic(AuthorToken token) throws KeeperException; 26 | 27 | /** 28 | * The method of password verification after loading the identity information 29 | * 30 | * @return 31 | * @see EqualsCipher 32 | * @see Md5Cipher 33 | */ 34 | default Cipher cipher() { 35 | return null; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/AuthorToken.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.authc; 17 | 18 | /** 19 | * AuthorToken 20 | *

21 | * The identity of the user when logging in 22 | * 23 | * @author biezhi 24 | * @date 2019-04-07 25 | */ 26 | public interface AuthorToken { 27 | 28 | /** 29 | * The unique identifier when logging in, which can be user_id or username, etc. 30 | * 31 | * @return username 32 | */ 33 | String username(); 34 | 35 | default String password() { 36 | return null; 37 | } 38 | 39 | default boolean remember() { 40 | return false; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/exception/UnauthenticException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.exception; 17 | 18 | import lombok.NoArgsConstructor; 19 | 20 | import static io.github.biezhi.keeper.keeperConst.ERROR_MESSAGE_NOT_LOGIN; 21 | 22 | @NoArgsConstructor 23 | public class UnauthenticException extends KeeperException { 24 | 25 | public UnauthenticException(String message) { 26 | super(message); 27 | } 28 | 29 | public static UnauthenticException build() { 30 | return build(ERROR_MESSAGE_NOT_LOGIN); 31 | } 32 | 33 | public static UnauthenticException build(String msg) { 34 | return new UnauthenticException(msg); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/AuthorizeInfo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.authc; 17 | 18 | import java.util.Set; 19 | 20 | /** 21 | * AuthorizeInfo interface 22 | *

23 | * Define the selection of user authorization information, 24 | * including role information and permission information. 25 | * 26 | * @author biezhi 27 | * @date 2019-04-08 28 | */ 29 | public interface AuthorizeInfo { 30 | 31 | /** 32 | * @return a list of roles owned by the user, stored as a string 33 | */ 34 | Set getRoles(); 35 | 36 | /** 37 | * @return a list of permissions owned by the user, stored as a string 38 | */ 39 | Set getPermissions(); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/config/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.exception.KeeperException; 5 | import io.github.biezhi.keeper.exception.UnauthorizedException; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.RestControllerAdvice; 9 | 10 | @Slf4j 11 | @RestControllerAdvice 12 | public class GlobalExceptionHandler { 13 | 14 | @ExceptionHandler(value = KeeperException.class) 15 | public Response defaultErrorHandler(KeeperException e) { 16 | log.warn(e.getMessage()); 17 | return Response.builder().data("请登录后访问").build(); 18 | } 19 | 20 | @ExceptionHandler(value = UnauthorizedException.class) 21 | public Response defaultErrorHandler(UnauthorizedException e) { 22 | log.warn(e.getMessage()); 23 | return Response.builder().data("您没有权限访问").build(); 24 | } 25 | 26 | @ExceptionHandler(value = Exception.class) 27 | public Response defaultErrorHandler(Exception e) { 28 | log.warn("请求发生异常: {}", e.getMessage()); 29 | e.printStackTrace(); 30 | return Response.builder().code(500).data(e.getMessage()).build(); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/impl/SimpleAuthenticInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.authc.impl; 2 | 3 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * @author biezhi 11 | * @date 2019-04-09 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | public class SimpleAuthenticInfo implements AuthenticInfo { 16 | 17 | private String username; 18 | private String password; 19 | private Object payload; 20 | private Map claims; 21 | 22 | public SimpleAuthenticInfo(String username, String password) { 23 | this.username = username; 24 | this.password = password; 25 | } 26 | 27 | public SimpleAuthenticInfo(String username, String password, Object payload) { 28 | this.username = username; 29 | this.password = password; 30 | this.payload = payload; 31 | } 32 | 33 | @Override 34 | public String username() { 35 | return username; 36 | } 37 | 38 | @Override 39 | public String password() { 40 | return password; 41 | } 42 | 43 | @Override 44 | public Object payload() { 45 | return payload; 46 | } 47 | 48 | @Override 49 | public Map claims() { 50 | return claims; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/exception/ExpiredException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.exception; 17 | 18 | import lombok.NoArgsConstructor; 19 | 20 | import static io.github.biezhi.keeper.keeperConst.ERROR_MESSAGE_EXPIRED; 21 | 22 | /** 23 | * 认证过期 24 | * 25 | * @author biezhi 26 | * @date 2019-04-04 27 | */ 28 | @NoArgsConstructor 29 | public class ExpiredException extends UnauthenticException { 30 | 31 | public ExpiredException(String message) { 32 | super(message); 33 | } 34 | 35 | public static ExpiredException build() { 36 | return build(ERROR_MESSAGE_EXPIRED); 37 | } 38 | 39 | public static ExpiredException build(String msg) { 40 | return new ExpiredException(msg); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/exception/KeeperException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.exception; 17 | 18 | import lombok.NoArgsConstructor; 19 | 20 | @NoArgsConstructor 21 | public class KeeperException extends RuntimeException { 22 | 23 | public KeeperException(String message) { 24 | super(message); 25 | } 26 | 27 | public KeeperException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | 31 | public KeeperException(Throwable cause) { 32 | super(cause); 33 | } 34 | 35 | public KeeperException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 36 | super(message, cause, enableSuppression, writableStackTrace); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/keeperConst.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper; 17 | 18 | /** 19 | * @author biezhi 20 | * @date 2019-04-04 21 | */ 22 | public interface keeperConst { 23 | 24 | String KEEPER_SESSION_KEY = "KEEPER_USER_TOKEN"; 25 | 26 | String KEEPER_LOGIN_KEY = "keeper:login:%s:%s"; 27 | String KEEPER_AUTHENTIC_KEY = "keeper:authentic:%s"; 28 | 29 | String ERROR_MESSAGE_NOT_PERMISSION = "You don't have permission to access this resource!"; 30 | String ERROR_MESSAGE_NOT_LOGIN = "Please isLogin and take action!"; 31 | String ERROR_MESSAGE_EXPIRED = "Your account has expired!"; 32 | String ERROR_MESSAGE_WRONG_PASSWORD = "Wrong password!"; 33 | 34 | String LOGOUT_KEY = "keeper:logout:token:%s"; 35 | } 36 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/exception/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.exception; 17 | 18 | import lombok.NoArgsConstructor; 19 | 20 | import static io.github.biezhi.keeper.keeperConst.ERROR_MESSAGE_NOT_PERMISSION; 21 | 22 | /** 23 | * 没有权限 24 | * 25 | * @author biezhi 26 | * @date 2019-04-04 27 | */ 28 | @NoArgsConstructor 29 | public class UnauthorizedException extends KeeperException { 30 | 31 | public UnauthorizedException(String message) { 32 | super(message); 33 | } 34 | 35 | public static UnauthorizedException build() { 36 | return build(ERROR_MESSAGE_NOT_PERMISSION); 37 | } 38 | 39 | public static UnauthorizedException build(String msg) { 40 | return new UnauthorizedException(msg); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/controller/PermController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.annotation.Permissions; 5 | import io.github.biezhi.keeper.annotation.Roles; 6 | import io.github.biezhi.keeper.enums.Logical; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping 13 | public class PermController { 14 | 15 | // 拥有 admin 角色或 role 角色即可访问 16 | @GetMapping("/role1") 17 | @Roles(value = {"admin", "role"}, logical = Logical.OR) 18 | public Response role1() { 19 | return Response.builder().data("角色111").build(); 20 | } 21 | 22 | // 拥有 sys 角色可以访问 23 | @GetMapping("/role2") 24 | @Roles("sys") 25 | public Response role2() { 26 | return Response.builder().data("角色222").build(); 27 | } 28 | 29 | // 拥有 user:view 权限可以访问 30 | @GetMapping("/perm1") 31 | @Permissions("user:view") 32 | public Response perm1() { 33 | return Response.builder().data("perm1 111").build(); 34 | } 35 | 36 | // 拥有 sys:view 权限可以访问 37 | @GetMapping("/perm2") 38 | @Permissions("sys:view") 39 | public Response perm2() { 40 | return Response.builder().data("perm2 222").build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/controller/PermController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.annotation.Permissions; 5 | import io.github.biezhi.keeper.annotation.Roles; 6 | import io.github.biezhi.keeper.enums.Logical; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping 13 | public class PermController { 14 | 15 | // 拥有 admin 角色或 role 角色即可访问 16 | @GetMapping("/role1") 17 | @Roles(value = {"admin", "role"}, logical = Logical.OR) 18 | public Response role1() { 19 | return Response.builder().data("角色111").build(); 20 | } 21 | 22 | // 拥有 sys 角色可以访问 23 | @GetMapping("/role2") 24 | @Roles("sys") 25 | public Response role2() { 26 | return Response.builder().data("角色222").build(); 27 | } 28 | 29 | // 拥有 user:view 权限可以访问 30 | @GetMapping("/perm1") 31 | @Permissions("user:view") 32 | public Response perm1() { 33 | return Response.builder().data("perm1 111").build(); 34 | } 35 | 36 | // 拥有 sys:view 权限可以访问 37 | @GetMapping("/perm2") 38 | @Permissions("sys:view") 39 | public Response perm2() { 40 | return Response.builder().data("perm2 222").build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/controller/PermController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.annotation.Permissions; 5 | import io.github.biezhi.keeper.annotation.Roles; 6 | import io.github.biezhi.keeper.enums.Logical; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping 13 | public class PermController { 14 | 15 | // 拥有 admin 角色或 role 角色即可访问 16 | @GetMapping("/role1") 17 | @Roles(value = {"admin", "role"}, logical = Logical.OR) 18 | public Response role1() { 19 | return Response.builder().data("角色111").build(); 20 | } 21 | 22 | // 拥有 sys 角色可以访问 23 | @GetMapping("/role2") 24 | @Roles("sys") 25 | public Response role2() { 26 | return Response.builder().data("角色222").build(); 27 | } 28 | 29 | // 拥有 user:view 权限可以访问 30 | @GetMapping("/perm1") 31 | @Permissions("user:view") 32 | public Response perm1() { 33 | return Response.builder().data("perm1 111").build(); 34 | } 35 | 36 | // 拥有 sys:view 权限可以访问 37 | @GetMapping("/perm2") 38 | @Permissions("sys:view") 39 | public Response perm2() { 40 | return Response.builder().data("perm2 222").build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keeper-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | keeper 7 | io.github.biezhi 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | keeper-examples 13 | pom 14 | 15 | 16 | keeper-spring-boot-jwt 17 | keeper-spring-boot-jwt-redis 18 | keeper-spring-boot-session 19 | keeper-spring-boot-session-redis 20 | 21 | 22 | 23 | 24 | org.projectlombok 25 | lombok 26 | 1.18.6 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-deploy-plugin 35 | 2.8.2 36 | 37 | true 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/controller/PermController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.annotation.Permissions; 5 | import io.github.biezhi.keeper.annotation.Roles; 6 | import io.github.biezhi.keeper.enums.Logical; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping 13 | public class PermController { 14 | 15 | // 拥有 admin 角色或 role 角色即可访问 16 | @GetMapping("/role1") 17 | @Roles(value = {"admin", "role"}, logical = Logical.OR) 18 | public Response role1() { 19 | return Response.builder().data("角色111").build(); 20 | } 21 | 22 | // 拥有 sys 角色可以访问 23 | @GetMapping("/role2") 24 | @Roles("sys") 25 | public Response role2() { 26 | return Response.builder().data("角色222").build(); 27 | } 28 | 29 | // 拥有 user:view 权限可以访问 30 | @GetMapping("/perm1") 31 | @Permissions("user:view") 32 | public Response perm1() { 33 | return Response.builder().data("perm1 111").build(); 34 | } 35 | 36 | // 拥有 sys:view 权限可以访问 37 | @GetMapping("/perm2") 38 | @Permissions("sys:view") 39 | public Response perm2() { 40 | return Response.builder().data("perm2 222").build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/annotation/Roles.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.annotation; 17 | 18 | import io.github.biezhi.keeper.enums.Logical; 19 | 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | @Target({ElementType.TYPE, ElementType.METHOD}) 26 | @Retention(RetentionPolicy.RUNTIME) 27 | public @interface Roles { 28 | 29 | /** 30 | * A single String role name or multiple comma-delimited role names required in order for the method 31 | * invocation to be allowed. 32 | */ 33 | String[] value(); 34 | 35 | /** 36 | * The logical operation for the permission check in case multiple roles are specified. AND is the default 37 | * @since 1.1.0 38 | */ 39 | Logical logical() default Logical.AND; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.Keeper; 5 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorToken; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @Slf4j 13 | @RestController 14 | @RequestMapping 15 | public class LoginController { 16 | 17 | @PostMapping("/login") 18 | public Response login(String username, String password) { 19 | 20 | Keeper.getSubject().login( 21 | new SimpleAuthorToken(username, password, true)); 22 | 23 | return Response.builder().code(200).data("登录成功").build(); 24 | } 25 | 26 | /** 27 | * 无需登录访问 28 | * 29 | * @return 30 | */ 31 | @GetMapping("/guest") 32 | public Response guest() { 33 | return Response.builder().code(200).data("guest!").build(); 34 | } 35 | 36 | /** 37 | * 需登陆后访问 38 | * 39 | * @return 40 | */ 41 | @RequestMapping("/hello") 42 | public Response hello() { 43 | return Response.builder().code(200).data("i,m hello!").build(); 44 | } 45 | 46 | @RequestMapping("/logout") 47 | public Response logout() { 48 | Keeper.getSubject().logout(); 49 | return Response.builder().code(200).data("注销成功").build(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /keeper-spring-boot-web-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | keeper 7 | io.github.biezhi 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | ${keeper-spring-boot-web.version} 13 | keeper-spring-boot-web-starter 14 | 15 | 16 | 17 | io.github.biezhi 18 | keeper-core 19 | ${keeper-core.version} 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | provided 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-autoconfigure 30 | provided 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-configuration-processor 35 | provided 36 | 37 | 38 | org.projectlombok 39 | lombok 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/impl/SimpleAuthorToken.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.authc.impl; 17 | 18 | import io.github.biezhi.keeper.core.authc.AuthorToken; 19 | import lombok.AllArgsConstructor; 20 | import lombok.Data; 21 | import lombok.NoArgsConstructor; 22 | 23 | @Data 24 | @NoArgsConstructor 25 | @AllArgsConstructor 26 | public class SimpleAuthorToken implements AuthorToken { 27 | 28 | private String username; 29 | private String password; 30 | private boolean remember; 31 | 32 | public SimpleAuthorToken(String username) { 33 | this.username = username; 34 | } 35 | 36 | public SimpleAuthorToken(String username, String password) { 37 | this.username = username; 38 | this.password = password; 39 | } 40 | 41 | @Override 42 | public String username() { 43 | return username; 44 | } 45 | 46 | @Override 47 | public String password() { 48 | return password; 49 | } 50 | 51 | @Override 52 | public boolean remember() { 53 | return remember; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/config/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.exception.KeeperException; 5 | import io.github.biezhi.keeper.exception.UnauthenticException; 6 | import io.github.biezhi.keeper.exception.UnauthorizedException; 7 | import io.github.biezhi.keeper.exception.WrongPasswordException; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | 12 | @Slf4j 13 | @RestControllerAdvice 14 | public class GlobalExceptionHandler { 15 | 16 | @ExceptionHandler(value = UnauthorizedException.class) 17 | public Response defaultErrorHandler(UnauthorizedException e) { 18 | log.warn(e.getMessage()); 19 | return Response.builder().data("您没有权限访问").build(); 20 | } 21 | 22 | @ExceptionHandler(value = WrongPasswordException.class) 23 | public Response defaultErrorHandler(WrongPasswordException e) { 24 | log.warn(e.getMessage()); 25 | return Response.builder().data("错误的密码").build(); 26 | } 27 | 28 | @ExceptionHandler(value = UnauthenticException.class) 29 | public Response defaultErrorHandler(UnauthenticException e) { 30 | log.warn(e.getMessage()); 31 | return Response.builder().data("请登录后访问").build(); 32 | } 33 | 34 | @ExceptionHandler(value = Exception.class) 35 | public Response defaultErrorHandler(Exception e) { 36 | log.warn("请求发生异常: {}", e.getMessage()); 37 | return Response.builder().code(500).data(e.getMessage()).build(); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/config/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.exception.KeeperException; 5 | import io.github.biezhi.keeper.exception.UnauthenticException; 6 | import io.github.biezhi.keeper.exception.UnauthorizedException; 7 | import io.github.biezhi.keeper.exception.WrongPasswordException; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | 12 | @Slf4j 13 | @RestControllerAdvice 14 | public class GlobalExceptionHandler { 15 | 16 | @ExceptionHandler(value = UnauthorizedException.class) 17 | public Response defaultErrorHandler(UnauthorizedException e) { 18 | log.warn(e.getMessage()); 19 | return Response.builder().data("您没有权限访问").build(); 20 | } 21 | 22 | @ExceptionHandler(value = WrongPasswordException.class) 23 | public Response defaultErrorHandler(WrongPasswordException e) { 24 | log.warn(e.getMessage()); 25 | return Response.builder().data("错误的密码").build(); 26 | } 27 | 28 | @ExceptionHandler(value = UnauthenticException.class) 29 | public Response defaultErrorHandler(UnauthenticException e) { 30 | log.warn(e.getMessage()); 31 | return Response.builder().data("请登录后访问").build(); 32 | } 33 | 34 | @ExceptionHandler(value = Exception.class) 35 | public Response defaultErrorHandler(Exception e) { 36 | log.warn("请求发生异常: {}", e.getMessage()); 37 | return Response.builder().code(500).data(e.getMessage()).build(); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/config/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.exception.KeeperException; 5 | import io.github.biezhi.keeper.exception.UnauthenticException; 6 | import io.github.biezhi.keeper.exception.UnauthorizedException; 7 | import io.github.biezhi.keeper.exception.WrongPasswordException; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.RestControllerAdvice; 11 | 12 | @Slf4j 13 | @RestControllerAdvice 14 | public class GlobalExceptionHandler { 15 | 16 | @ExceptionHandler(value = UnauthorizedException.class) 17 | public Response defaultErrorHandler(UnauthorizedException e) { 18 | log.warn(e.getMessage()); 19 | return Response.builder().data("您没有权限访问").build(); 20 | } 21 | 22 | @ExceptionHandler(value = WrongPasswordException.class) 23 | public Response defaultErrorHandler(WrongPasswordException e) { 24 | log.warn(e.getMessage()); 25 | return Response.builder().data("错误的密码").build(); 26 | } 27 | 28 | @ExceptionHandler(value = UnauthenticException.class) 29 | public Response defaultErrorHandler(UnauthenticException e) { 30 | log.warn(e.getMessage()); 31 | return Response.builder().data("请登录后访问").build(); 32 | } 33 | 34 | @ExceptionHandler(value = Exception.class) 35 | public Response defaultErrorHandler(Exception e) { 36 | log.warn("请求发生异常: {}", e.getMessage()); 37 | return Response.builder().code(500).data(e.getMessage()).build(); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.Keeper; 5 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 6 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorToken; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @Slf4j 13 | @RestController 14 | @RequestMapping 15 | public class LoginController { 16 | 17 | @PostMapping("/login") 18 | public Response login(SimpleAuthorToken authorToken) { 19 | 20 | AuthenticInfo authenticInfo = Keeper.getSubject().login(authorToken); 21 | 22 | log.info("create token: {}", authenticInfo.payload()); 23 | return Response.builder().code(200).data(authenticInfo.payload().toString()).build(); 24 | } 25 | 26 | /** 27 | * 无需登录访问 28 | * 29 | * @return 30 | */ 31 | @RequestMapping("/guest") 32 | public Response guest() { 33 | return Response.builder().code(200).data("guest!").build(); 34 | } 35 | 36 | /** 37 | * 需登陆后访问 38 | * 39 | * @return 40 | */ 41 | @RequestMapping("/hello") 42 | public Response hello() { 43 | return Response.builder().code(200).data("i,m hello!").build(); 44 | } 45 | 46 | @RequestMapping("/logout") 47 | public Response logout() { 48 | Keeper.getSubject().logout(); 49 | return Response.builder().code(200).data("注销成功").build(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/utils/JsonUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.utils; 17 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | import lombok.experimental.UtilityClass; 20 | import lombok.extern.slf4j.Slf4j; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * JsonUtil 26 | * 27 | * @author biezhi 28 | * @date 2019-04-07 29 | */ 30 | @Slf4j 31 | @UtilityClass 32 | public class JsonUtil { 33 | 34 | private static final ObjectMapper MAPPER = new ObjectMapper(); 35 | 36 | public String toJSONString(Object value) { 37 | try { 38 | if (value == null) { 39 | return ""; 40 | } 41 | return MAPPER.writeValueAsString(value); 42 | } catch (Exception e) { 43 | log.warn("bean to json error", e); 44 | return null; 45 | } 46 | } 47 | 48 | public static T toBean(String json, Class type) { 49 | try { 50 | return MAPPER.readValue(json, type); 51 | } catch (IOException e) { 52 | log.warn("json to bean error", e); 53 | return null; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.Keeper; 5 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorToken; 6 | import io.github.biezhi.keeper.core.subject.Subject; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @Slf4j 13 | @RestController 14 | @RequestMapping 15 | public class LoginController { 16 | 17 | @PostMapping("/login") 18 | public Response login(String username, String password) { 19 | 20 | Keeper.getSubject().login( 21 | new SimpleAuthorToken(username, password, true)); 22 | 23 | return Response.builder().code(200).data("登录成功").build(); 24 | } 25 | 26 | /** 27 | * 无需登录访问 28 | * 29 | * @return 30 | */ 31 | @RequestMapping("/guest") 32 | public Response guest() { 33 | return Response.builder().code(200).data("guest!").build(); 34 | } 35 | 36 | /** 37 | * 需登陆后访问 38 | * 39 | * @return 40 | */ 41 | @RequestMapping("/hello") 42 | public Response hello() { 43 | Subject subject = Keeper.getSubject(); 44 | String username = subject.authenticInfo().username(); 45 | return Response.builder().code(200).data("i,m " + username).build(); 46 | } 47 | 48 | @RequestMapping("/logout") 49 | public Response logout() { 50 | Keeper.getSubject().logout(); 51 | return Response.builder().code(200).data("注销成功").build(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /keeper-spring-boot-web-starter/src/main/java/io/github/biezhi/keeper/starter/KeeperAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.starter; 2 | 3 | import io.github.biezhi.keeper.Keeper; 4 | import io.github.biezhi.keeper.core.aspect.KeeperAspect; 5 | import io.github.biezhi.keeper.core.authc.Authorization; 6 | import io.github.biezhi.keeper.core.jwt.JwtToken; 7 | import io.github.biezhi.keeper.core.jwt.SimpleJwtToken; 8 | import io.github.biezhi.keeper.enums.SubjectType; 9 | import io.github.biezhi.keeper.utils.SpringContextUtil; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | 15 | @Configuration 16 | @EnableConfigurationProperties(KeeperProperties.class) 17 | public class KeeperAutoConfiguration { 18 | 19 | @Autowired 20 | private KeeperProperties keeperProperties; 21 | 22 | @Bean 23 | public KeeperAspect keeperAspect() { 24 | return new KeeperAspect(); 25 | } 26 | 27 | @Bean 28 | public SpringContextUtil springContextUtil() { 29 | return new SpringContextUtil(); 30 | } 31 | 32 | @Bean 33 | public JwtToken jwtToken() { 34 | return new SimpleJwtToken(keeperProperties.getJwt()); 35 | } 36 | 37 | @Bean 38 | public Keeper keeper(@Autowired(required = false) Authorization authorization) { 39 | Keeper keeper = new Keeper(); 40 | keeper.setAuthorization(authorization); 41 | keeper.setJwtConfig(keeperProperties.getJwt()); 42 | keeper.setSessionConfig(keeperProperties.getSession()); 43 | // default subject type 44 | keeper.setSubjectType(SubjectType.SESSION); 45 | return keeper; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/config/JwtConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.config; 17 | 18 | import lombok.Data; 19 | 20 | import java.time.Duration; 21 | 22 | /** 23 | * jwt configuration 24 | * 25 | * @author biezhi 26 | * @date 2019-04-05 27 | */ 28 | @Data 29 | public class JwtConfig { 30 | 31 | /** 32 | * Read the token field from the Http Header 33 | */ 34 | private String header = "Authorization"; 35 | 36 | /** 37 | * token prefix,@link{https://jwt.io/introduction/} 38 | */ 39 | private String tokenHead = "Bearer "; 40 | 41 | /** 42 | * The secret when jwt is signed, be sure to configure, do not leak 43 | */ 44 | private String secret = "keeper"; 45 | 46 | /** 47 | * The generated token is valid. 48 | * If the refresh time is not set after expiration, you need to log in again. 49 | */ 50 | private Duration expires = Duration.ofMinutes(10); 51 | 52 | /** 53 | * The token can be renew at the latest, 54 | * in which an expired token can be refreshed to generate a new token. 55 | *

56 | * If the time is exceeded, re-authentication is required. 57 | */ 58 | private Duration renewExpires; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/cache/map/MapCache.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.cache.map; 2 | 3 | import io.github.biezhi.keeper.core.cache.Cache; 4 | 5 | import java.time.Duration; 6 | import java.util.Map; 7 | import java.util.Set; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * @author biezhi 12 | * @date 2019-04-07 13 | */ 14 | public class MapCache implements Cache { 15 | 16 | private Map cache; 17 | 18 | public MapCache() { 19 | this.cache = new ConcurrentHashMap<>(); 20 | } 21 | 22 | public MapCache(int capacity) { 23 | this.cache = new ConcurrentHashMap<>(capacity); 24 | } 25 | 26 | public MapCache(Map cache) { 27 | this.cache = cache; 28 | } 29 | 30 | @Override 31 | public void set(K key, V value) { 32 | cache.putIfAbsent(key, value); 33 | } 34 | 35 | @Deprecated 36 | @Override 37 | public void set(K key, V value, long millis) { 38 | cache.putIfAbsent(key, value); 39 | } 40 | 41 | @Override 42 | public T get(K key, Class type) { 43 | return (T) cache.get(key); 44 | } 45 | 46 | @Override 47 | public void delWith(String keyPrefix) { 48 | Set keySet = keySet(); 49 | for (K k : keySet) { 50 | if (k.toString().startsWith(keyPrefix)) { 51 | remove(k); 52 | } 53 | } 54 | } 55 | 56 | @Override 57 | public boolean exists(K key) { 58 | return cache.containsKey(key); 59 | } 60 | 61 | @Override 62 | public boolean expire(K key) { 63 | return !cache.containsKey(key); 64 | } 65 | 66 | @Override 67 | public void remove(K key) { 68 | cache.remove(key); 69 | } 70 | 71 | @Override 72 | public Set keySet() { 73 | return cache.keySet(); 74 | } 75 | 76 | @Override 77 | public void clear() { 78 | cache.clear(); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/utils/SpringContextUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.utils; 17 | 18 | import org.springframework.beans.BeansException; 19 | import org.springframework.context.ApplicationContext; 20 | import org.springframework.context.ApplicationContextAware; 21 | 22 | /** 23 | * SpringContextUtil 24 | * 25 | * @author biezhi 26 | * @date 2019-04-05 27 | */ 28 | public class SpringContextUtil implements ApplicationContextAware { 29 | 30 | private static ApplicationContext applicationContext; 31 | 32 | @Override 33 | public void setApplicationContext(ApplicationContext applicationContext) 34 | throws BeansException { 35 | SpringContextUtil.applicationContext = applicationContext; 36 | } 37 | 38 | public static ApplicationContext getApplicationContext() { 39 | return applicationContext; 40 | } 41 | 42 | @SuppressWarnings("unchecked") 43 | public static T getBean(String name) throws BeansException { 44 | return (T) applicationContext.getBean(name); 45 | } 46 | 47 | public static boolean containsBean(String beanName) { 48 | return applicationContext.containsBean(beanName); 49 | } 50 | 51 | public static T getBean(Class type) throws BeansException { 52 | return applicationContext.getBean(type); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/Authorization.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.authc; 17 | 18 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorizeInfo; 19 | import io.github.biezhi.keeper.core.cache.AuthorizeCache; 20 | import io.github.biezhi.keeper.exception.KeeperException; 21 | 22 | /** 23 | * Authorization interface 24 | *

25 | * Implement the interface when it needs to implement authorization 26 | * and host it in the Spring container. 27 | * 28 | * @author biezhi 29 | * @date 2019-04-08 30 | */ 31 | public interface Authorization { 32 | 33 | /** 34 | * Load user authorization information, including roles and permissions sets. 35 | * 36 | * @param authenticInfo the authenticated user token identifier, mainly username 37 | * @return Authorization information {@link SimpleAuthorizeInfo} 38 | * @throws KeeperException 39 | */ 40 | AuthorizeInfo doAuthorization(AuthenticInfo authenticInfo) throws KeeperException; 41 | 42 | /** 43 | * Load authorization information from the authenticCache, 44 | * loaded by default from {@link Authorization#doAuthorization(AuthenticInfo)} 45 | * 46 | * @return Authorization information {@link SimpleAuthorizeInfo} 47 | */ 48 | default AuthorizeCache loadWithCache() { 49 | return null; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.controller; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.Keeper; 5 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 6 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorToken; 7 | import io.github.biezhi.keeper.core.authc.impl.Tokens; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @Slf4j 15 | @RestController 16 | @RequestMapping 17 | public class LoginController { 18 | 19 | @PostMapping("/login") 20 | public Response login(String username, String password) { 21 | 22 | AuthenticInfo authenticInfo = Keeper.getSubject().login( 23 | new SimpleAuthorToken(username, password)); 24 | 25 | log.info("create token: {}", authenticInfo.payload()); 26 | return Response.builder().code(200).data(authenticInfo.payload().toString()).build(); 27 | } 28 | 29 | /** 30 | * 无需登录访问 31 | * 32 | * @return 33 | */ 34 | @RequestMapping("/guest") 35 | public Response guest() { 36 | return Response.builder().code(200).data("guest!").build(); 37 | } 38 | 39 | /** 40 | * 需登陆后访问 41 | * 42 | * @return 43 | */ 44 | @RequestMapping("/hello") 45 | public Response hello() { 46 | String username = Keeper.getSubject().authenticInfo().username(); 47 | return Response.builder().code(200).data("Hello, i'm " + username).build(); 48 | } 49 | 50 | @RequestMapping("/logout") 51 | public Response logout() { 52 | Keeper.getSubject().logout(); 53 | return Response.builder().code(200).data("注销成功").build(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/config/JwtAuthorizeBean.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.User; 4 | import com.example.keeper.service.UserService; 5 | import io.github.biezhi.keeper.core.authc.*; 6 | import io.github.biezhi.keeper.core.authc.cipher.Cipher; 7 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthenticInfo; 8 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorizeInfo; 9 | import io.github.biezhi.keeper.exception.KeeperException; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.Set; 15 | 16 | @Slf4j 17 | @Component 18 | public class JwtAuthorizeBean implements Authentication, Authorization { 19 | 20 | @Autowired 21 | private UserService userService; 22 | 23 | @Override 24 | public AuthenticInfo doAuthentic(AuthorToken token) throws KeeperException { 25 | log.info("doAuthentic :: {}", token.username()); 26 | 27 | User user = userService.findByUsername(token.username()); 28 | 29 | return new SimpleAuthenticInfo( 30 | user.getUsername(), 31 | user.getPassword(), 32 | user 33 | ); 34 | } 35 | 36 | @Override 37 | public Cipher cipher() { 38 | return Cipher.MD5; 39 | } 40 | 41 | @Override 42 | public AuthorizeInfo doAuthorization(AuthenticInfo token) throws KeeperException { 43 | String username = token.username(); 44 | log.info("doAuthorization :: {}", username); 45 | 46 | Set roles = userService.findRoles(username); 47 | Set permissions = userService.findPermissions(username); 48 | 49 | SimpleAuthorizeInfo simpleAuthorizeInfo = new SimpleAuthorizeInfo(); 50 | simpleAuthorizeInfo.setRoles(roles); 51 | simpleAuthorizeInfo.setPermissions(permissions); 52 | return simpleAuthorizeInfo; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/src/main/java/com/example/keeper/config/AuthorizationBean.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.User; 4 | import com.example.keeper.service.UserService; 5 | import io.github.biezhi.keeper.core.authc.*; 6 | import io.github.biezhi.keeper.core.authc.cipher.Cipher; 7 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthenticInfo; 8 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorizeInfo; 9 | import io.github.biezhi.keeper.exception.KeeperException; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.Set; 15 | 16 | @Slf4j 17 | @Component 18 | public class AuthorizationBean implements Authentication, Authorization { 19 | 20 | @Autowired 21 | private UserService userService; 22 | 23 | @Override 24 | public AuthenticInfo doAuthentic(AuthorToken token) throws KeeperException { 25 | log.info("doAuthentic :: {}", token.username()); 26 | 27 | User user = userService.findByUsername(token.username()); 28 | 29 | return new SimpleAuthenticInfo( 30 | user.getUsername(), 31 | user.getPassword(), 32 | user 33 | ); 34 | } 35 | 36 | @Override 37 | public Cipher cipher() { 38 | return Cipher.EQUALS; 39 | } 40 | 41 | @Override 42 | public AuthorizeInfo doAuthorization(AuthenticInfo token) throws KeeperException { 43 | String username = token.username(); 44 | 45 | log.info("doAuthorization :: {}", username); 46 | 47 | Set roles = userService.findRoles(username); 48 | Set permissions = userService.findPermissions(username); 49 | 50 | SimpleAuthorizeInfo simpleAuthorizeInfo = new SimpleAuthorizeInfo(); 51 | simpleAuthorizeInfo.setRoles(roles); 52 | simpleAuthorizeInfo.setPermissions(permissions); 53 | return simpleAuthorizeInfo; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | keeper-examples 7 | io.github.biezhi 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | keeper-spring-boot-session 13 | 14 | 15 | 16 | io.github.biezhi 17 | keeper-spring-boot-web-starter 18 | ${keeper-spring-boot-web.version} 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-aop 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-autoconfigure 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-configuration-processor 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-dependencies 43 | ${spring-boot.version} 44 | pom 45 | import 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/src/main/java/com/example/keeper/config/KeeperBeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import io.github.biezhi.keeper.Keeper; 5 | import io.github.biezhi.keeper.core.web.filter.AuthenticFilter; 6 | import io.github.biezhi.keeper.enums.SubjectType; 7 | import io.github.biezhi.keeper.utils.WebUtil; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Primary; 11 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 12 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | 17 | @Configuration 18 | public class KeeperBeanConfig extends WebMvcConfigurationSupport { 19 | 20 | @Override 21 | public void addCorsMappings(CorsRegistry registry) { 22 | registry.addMapping("/**") 23 | .allowedHeaders("*") 24 | .allowedMethods("*") 25 | .allowedOrigins("*"); 26 | } 27 | 28 | @Bean 29 | public AuthenticFilter authenticFilter() { 30 | return new AuthenticFilter() { 31 | 32 | @Override 33 | protected void initFilterBean() { 34 | this.addPathPatterns("/**") 35 | .excludePathPatterns("/guest", "/login"); 36 | } 37 | 38 | @Override 39 | protected void unAuthentic(HttpServletRequest request, HttpServletResponse response) { 40 | WebUtil.writeJSON(response, 41 | Response.builder().code(500) 42 | .msg("请登录后访问") 43 | .build()); 44 | } 45 | }; 46 | } 47 | 48 | @Bean 49 | @Primary 50 | public Keeper initKeeper(Keeper keeper) { 51 | keeper.setSubjectType(SubjectType.JWT); 52 | return keeper; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/subject/Subject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.subject; 17 | 18 | import io.github.biezhi.keeper.annotation.Permissions; 19 | import io.github.biezhi.keeper.annotation.Roles; 20 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 21 | import io.github.biezhi.keeper.core.authc.AuthorToken; 22 | 23 | /** 24 | * @author biezhi 25 | * @date 2019-04-05 26 | */ 27 | public interface Subject { 28 | 29 | /** 30 | * @return returns the current login information 31 | */ 32 | AuthenticInfo authenticInfo(); 33 | 34 | AuthenticInfo login(AuthorToken token); 35 | 36 | void logout(); 37 | 38 | /** 39 | * Refresh user authorization information 40 | *

41 | * the authorization information is reloaded after this operation 42 | */ 43 | void refreshAuthorize(); 44 | 45 | /** 46 | * @return return the login status of the current user 47 | */ 48 | boolean isLogin(); 49 | 50 | /** 51 | * Token renewal 52 | * 53 | * @return 54 | */ 55 | boolean renew(); 56 | 57 | /** 58 | * Determine if the user has execute permission 59 | * 60 | * @param roles roles currently allowed to execute 61 | * @param permissions permissions currently allowed to access 62 | * @return return whether the current user has permission to execute 63 | */ 64 | boolean hasPermissions(Roles roles, Permissions permissions); 65 | 66 | } 67 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/cache/AuthorizeCache.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.cache; 17 | 18 | import io.github.biezhi.keeper.core.authc.AuthorizeInfo; 19 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorizeInfo; 20 | 21 | /** 22 | * AuthorizeCache 23 | *

24 | * Define read and write of authorization information 25 | * 26 | * @author biezhi 27 | * @date 2019-04-07 28 | */ 29 | public interface AuthorizeCache { 30 | 31 | /** 32 | * Obtain the authorization information of the user from the authenticCache according to the username 33 | * 34 | * @param username user unique identifier 35 | * @return {@link SimpleAuthorizeInfo} 36 | */ 37 | AuthorizeInfo getAuthorizeInfo(String username); 38 | 39 | /** 40 | * Whether the user has cached authorization information 41 | * 42 | * @param username user unique identifier 43 | * @return 44 | */ 45 | boolean cached(String username); 46 | 47 | /** 48 | * Remove the user authorization authenticCache information 49 | * 50 | * @param username user unique identifier 51 | */ 52 | void remove(String username); 53 | 54 | /** 55 | * Empty the authenticCache 56 | */ 57 | void clear(); 58 | 59 | /** 60 | * Write an authorization authenticCache 61 | * 62 | * @param username user unique identifier 63 | * @param authorizeInfo AuthorizeInfo 64 | */ 65 | void set(String username, AuthorizeInfo authorizeInfo); 66 | 67 | } 68 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | keeper-examples 7 | io.github.biezhi 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | keeper-spring-boot-jwt 13 | 14 | 15 | 16 | io.github.biezhi 17 | keeper-spring-boot-web-starter 18 | ${keeper-spring-boot-web.version} 19 | 20 | 21 | com.auth0 22 | java-jwt 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-aop 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-autoconfigure 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-configuration-processor 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-dependencies 47 | ${spring-boot.version} 48 | pom 49 | import 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | keeper-examples 7 | io.github.biezhi 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | keeper-spring-boot-jwt-redis 13 | 14 | 15 | 16 | io.github.biezhi 17 | keeper-spring-boot-web-starter 18 | ${keeper-spring-boot-web.version} 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-aop 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-redis 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-autoconfigure 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-configuration-processor 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-dependencies 47 | ${spring-boot.version} 48 | pom 49 | import 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | keeper-examples 7 | io.github.biezhi 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | keeper-spring-boot-session-redis 13 | 14 | 15 | 16 | io.github.biezhi 17 | keeper-spring-boot-web-starter 18 | ${keeper-spring-boot-web.version} 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-aop 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-redis 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-autoconfigure 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-configuration-processor 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-dependencies 47 | ${spring-boot.version} 48 | pom 49 | import 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /keeper-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | keeper 7 | io.github.biezhi 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | keeper-core 13 | ${keeper-core.version} 14 | 15 | 16 | 17 | 18 | com.auth0 19 | java-jwt 20 | 21 | 22 | 23 | org.slf4j 24 | slf4j-api 25 | provided 26 | 27 | 28 | 29 | javax.servlet 30 | javax.servlet-api 31 | provided 32 | 33 | 34 | 35 | org.springframework 36 | spring-context 37 | provided 38 | 39 | 40 | org.springframework 41 | spring-web 42 | provided 43 | 44 | 45 | org.springframework 46 | spring-aop 47 | provided 48 | 49 | 50 | org.springframework 51 | spring-aspects 52 | provided 53 | 54 | 55 | 56 | org.projectlombok 57 | lombok 58 | provided 59 | 60 | 61 | 62 | org.springframework.data 63 | spring-data-redis 64 | 2.0.11.RELEASE 65 | provided 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/authc/impl/Tokens.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.authc.impl; 17 | 18 | import io.github.biezhi.keeper.core.authc.AuthorToken; 19 | import lombok.experimental.UtilityClass; 20 | 21 | /** 22 | * Tokens 23 | *

24 | * Builder for creating {@link AuthorToken} 25 | * 26 | * @author biezhi 27 | * @date 2019-04-07 28 | */ 29 | @UtilityClass 30 | public class Tokens { 31 | 32 | public static AuthorTokenBuilder create(String username) { 33 | AuthorTokenBuilder builder = new AuthorTokenBuilder(); 34 | builder.username = username; 35 | return builder; 36 | } 37 | 38 | public static SimpleAuthorToken build(AuthorToken authorToken) { 39 | SimpleAuthorToken simpleAuthorToken = new SimpleAuthorToken(); 40 | simpleAuthorToken.setUsername(authorToken.username()); 41 | simpleAuthorToken.setPassword((authorToken.password())); 42 | return simpleAuthorToken; 43 | } 44 | 45 | public static class AuthorTokenBuilder { 46 | String username; 47 | String password; 48 | boolean remember; 49 | 50 | public AuthorTokenBuilder remember(boolean remember) { 51 | this.remember = remember; 52 | return this; 53 | } 54 | 55 | public AuthorTokenBuilder password(String password) { 56 | this.password = password; 57 | return this; 58 | } 59 | 60 | public AuthorToken build() { 61 | SimpleAuthorToken simpleAuthorToken = new SimpleAuthorToken(); 62 | simpleAuthorToken.setUsername(username); 63 | simpleAuthorToken.setPassword(password); 64 | simpleAuthorToken.setRemember(remember); 65 | return simpleAuthorToken; 66 | } 67 | 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/config/KeeperAuthorizeBean.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.User; 4 | import com.example.keeper.service.UserService; 5 | import io.github.biezhi.keeper.core.authc.*; 6 | import io.github.biezhi.keeper.core.authc.cipher.Cipher; 7 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthenticInfo; 8 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorizeInfo; 9 | import io.github.biezhi.keeper.core.cache.AuthorizeCache; 10 | import io.github.biezhi.keeper.exception.KeeperException; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.data.redis.core.StringRedisTemplate; 13 | 14 | import java.time.Duration; 15 | import java.util.Set; 16 | 17 | @Slf4j 18 | public class KeeperAuthorizeBean implements Authentication, Authorization { 19 | 20 | private AuthorizeCache authorizeCache; 21 | private UserService userService; 22 | 23 | public KeeperAuthorizeBean(UserService userService, StringRedisTemplate stringRedisTemplate) { 24 | this.userService = userService; 25 | // authorizeCache = new AuthorizeRedisCache(stringRedisTemplate, Duration.ofMinutes(10)); 26 | } 27 | 28 | @Override 29 | public AuthenticInfo doAuthentic(AuthorToken token) throws KeeperException { 30 | 31 | log.info("doAuthentic :: {}", token.username()); 32 | 33 | User user = userService.findByUsername(token.username()); 34 | 35 | return new SimpleAuthenticInfo( 36 | user.getUsername(), 37 | user.getPassword(), 38 | user 39 | ); 40 | } 41 | 42 | @Override 43 | public Cipher cipher() { 44 | return Cipher.MD5; 45 | } 46 | 47 | @Override 48 | public AuthorizeInfo doAuthorization(AuthenticInfo token) throws KeeperException { 49 | String username = token.username(); 50 | 51 | log.info("doAuthorization :: {}", username); 52 | 53 | Set roles = userService.findRoles(username); 54 | Set permissions = userService.findPermissions(username); 55 | 56 | SimpleAuthorizeInfo simpleAuthorizeInfo = new SimpleAuthorizeInfo(); 57 | simpleAuthorizeInfo.setRoles(roles); 58 | simpleAuthorizeInfo.setPermissions(permissions); 59 | return simpleAuthorizeInfo; 60 | } 61 | 62 | @Override 63 | public AuthorizeCache loadWithCache() { 64 | return authorizeCache; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/config/KepperAuthorizeBean.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.User; 4 | import com.example.keeper.service.UserService; 5 | import io.github.biezhi.keeper.core.authc.*; 6 | import io.github.biezhi.keeper.core.authc.cipher.Cipher; 7 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthenticInfo; 8 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthorizeInfo; 9 | import io.github.biezhi.keeper.core.cache.AuthorizeCache; 10 | import io.github.biezhi.keeper.exception.KeeperException; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.data.redis.core.StringRedisTemplate; 13 | 14 | import java.time.Duration; 15 | import java.util.Set; 16 | 17 | @Slf4j 18 | public class KepperAuthorizeBean implements Authentication, Authorization { 19 | 20 | private UserService userService; 21 | private AuthorizeCache authorizeCache; 22 | 23 | public KepperAuthorizeBean(UserService userService, StringRedisTemplate stringRedisTemplate) { 24 | this.userService = userService; 25 | // this.authorizeCache = new AuthorizeRedisCache(stringRedisTemplate, Duration.ofMinutes(10)); 26 | } 27 | 28 | @Override 29 | public AuthenticInfo doAuthentic(AuthorToken token) throws KeeperException { 30 | log.info("doAuthentic :: {}", token.username()); 31 | 32 | User user = userService.findByUsername(token.username()); 33 | 34 | return new SimpleAuthenticInfo( 35 | user.getUsername(), 36 | user.getPassword(), 37 | user 38 | ); 39 | } 40 | 41 | @Override 42 | public Cipher cipher() { 43 | return Cipher.EQUALS; 44 | } 45 | 46 | @Override 47 | public AuthorizeInfo doAuthorization(AuthenticInfo token) throws KeeperException { 48 | String username = token.username(); 49 | 50 | log.info("doAuthorization :: {}", token.username()); 51 | 52 | Set roles = userService.findRoles(username); 53 | Set permissions = userService.findPermissions(username); 54 | 55 | SimpleAuthorizeInfo simpleAuthorizeInfo = new SimpleAuthorizeInfo(); 56 | simpleAuthorizeInfo.setRoles(roles); 57 | simpleAuthorizeInfo.setPermissions(permissions); 58 | return simpleAuthorizeInfo; 59 | } 60 | 61 | @Override 62 | public AuthorizeCache loadWithCache() { 63 | return authorizeCache; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-session-redis/src/main/java/com/example/keeper/config/KeeperBeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import com.example.keeper.service.UserService; 5 | import io.github.biezhi.keeper.Keeper; 6 | import io.github.biezhi.keeper.core.cache.redis.RedisCache; 7 | import io.github.biezhi.keeper.core.web.filter.AuthenticFilter; 8 | import io.github.biezhi.keeper.enums.SubjectType; 9 | import io.github.biezhi.keeper.utils.WebUtil; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.context.annotation.Primary; 13 | import org.springframework.data.redis.core.StringRedisTemplate; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.time.Duration; 18 | 19 | /** 20 | * @author biezhi 21 | * @date 2019-04-07 22 | */ 23 | @Configuration 24 | public class KeeperBeanConfig { 25 | 26 | // @Bean 27 | // public AuthorizeRedisCache authorizeRedisCache(StringRedisTemplate stringRedisTemplate) { 28 | // return new AuthorizeRedisCache(stringRedisTemplate, Duration.ofMinutes(10)); 29 | // } 30 | 31 | @Bean 32 | public AuthenticFilter authenticFilter() { 33 | return new AuthenticFilter() { 34 | 35 | @Override 36 | protected void initFilterBean() { 37 | this.addPathPatterns("/**") 38 | .excludePathPatterns("/guest", "/login"); 39 | } 40 | 41 | @Override 42 | protected void unAuthentic(HttpServletRequest request, HttpServletResponse response) { 43 | WebUtil.writeJSON(response, 44 | Response.builder().code(500) 45 | .msg("请登录后访问") 46 | .build()); 47 | } 48 | }; 49 | } 50 | 51 | @Bean 52 | public KepperAuthorizeBean kepperAuthorizeBean(UserService userService, StringRedisTemplate stringRedisTemplate) { 53 | return new KepperAuthorizeBean(userService, stringRedisTemplate); 54 | } 55 | 56 | @Bean 57 | @Primary 58 | public Keeper initKeeper(Keeper keeper, StringRedisTemplate stringRedisTemplate) { 59 | keeper.setSubjectType(SubjectType.SESSION); 60 | // keeper.setAuthenticInfoCache(new AuthenticRedisCache(stringRedisTemplate)); 61 | keeper.setKeeperCache(new RedisCache<>(stringRedisTemplate)); 62 | return keeper; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/utils/WebUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.utils; 17 | 18 | import lombok.experimental.UtilityClass; 19 | import lombok.extern.slf4j.Slf4j; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | import javax.servlet.http.HttpSession; 24 | import java.io.IOException; 25 | import java.util.Objects; 26 | 27 | /** 28 | * WebUtil 29 | * 30 | * @author biezhi 31 | * @date 2019-04-04 32 | */ 33 | @Slf4j 34 | @UtilityClass 35 | public class WebUtil { 36 | 37 | private static final ThreadLocal REQUEST_THREAD_LOCAL = new ThreadLocal<>(); 38 | private static final ThreadLocal RESPONSE_THREAD_LOCAL = new ThreadLocal<>(); 39 | 40 | public static void initContext(HttpServletRequest request, HttpServletResponse response) { 41 | REQUEST_THREAD_LOCAL.set(request); 42 | RESPONSE_THREAD_LOCAL.set(response); 43 | } 44 | 45 | public static void removeRequest() { 46 | REQUEST_THREAD_LOCAL.remove(); 47 | RESPONSE_THREAD_LOCAL.remove(); 48 | } 49 | 50 | public HttpServletResponse currentResponse() { 51 | return RESPONSE_THREAD_LOCAL.get(); 52 | } 53 | 54 | public HttpServletRequest currentRequest() { 55 | return REQUEST_THREAD_LOCAL.get(); 56 | } 57 | 58 | public HttpSession currentSession() { 59 | return currentSession(false); 60 | } 61 | 62 | public HttpSession currentSession(boolean create) { 63 | HttpServletRequest request = currentRequest(); 64 | return null != request ? request.getSession(create) : null; 65 | } 66 | 67 | public void writeJSON(HttpServletResponse response, Object data) { 68 | try { 69 | response.setContentType("application/json; charset=UTF-8"); 70 | response.getWriter().write(Objects.requireNonNull(JsonUtil.toJSONString(data))); 71 | response.getWriter().flush(); 72 | } catch (IOException e) { 73 | log.error("Write to response error", e); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/jwt/JwtToken.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.jwt; 17 | 18 | import java.time.Duration; 19 | import java.util.Map; 20 | 21 | public interface JwtToken { 22 | 23 | /** 24 | * Create a token that sets the username to subject 25 | * 26 | * @param username unique identity of the user currently logged in 27 | * @param claims user additional information, please do not store password type information 28 | * @return return a new JWT token 29 | */ 30 | String create(String username, Map claims); 31 | 32 | /** 33 | * Parse the username based on the incoming JWT token 34 | * 35 | * @param token jwt token 36 | * @return username 37 | */ 38 | String getUsername(String token); 39 | 40 | /** 41 | * get the create time in seconds for token 42 | * 43 | * @param token 44 | * @return 45 | */ 46 | long getCreateTime(String token); 47 | 48 | /** 49 | * get the timeout in seconds for token 50 | * 51 | * @param token 52 | * @return 53 | */ 54 | long getExpireTime(String token); 55 | 56 | /** 57 | * get the latest timeout in seconds for a renewal 58 | * 59 | * @param token 60 | * @return 61 | */ 62 | long getRenewExpireTime(String token); 63 | 64 | /** 65 | * Verify that the incoming token has expired 66 | * 67 | * @param token jwt token 68 | */ 69 | boolean isExpired(String token); 70 | 71 | /** 72 | * Verify that the incoming token can be refreshed if the claim to refresh does not expire 73 | * 74 | * @param token jwt token 75 | */ 76 | boolean canRenew(String token); 77 | 78 | /** 79 | * @return returns token of the current request context, obtained in the header 80 | */ 81 | String getAuthToken(); 82 | 83 | 84 | /** 85 | * Refreshing the current user's JWT token writes a new token to the header of the response. 86 | * the client should replace this token with the latest accessible JWT token 87 | * 88 | * @param username unique identity of the user currently logged in 89 | * @param claims user additional information, please do not store password type information 90 | */ 91 | String refresh(String username, Map claims); 92 | 93 | } 94 | -------------------------------------------------------------------------------- /keeper-examples/keeper-spring-boot-jwt-redis/src/main/java/com/example/keeper/config/KeeperBeanConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.keeper.config; 2 | 3 | import com.example.keeper.model.Response; 4 | import com.example.keeper.service.UserService; 5 | import io.github.biezhi.keeper.Keeper; 6 | import io.github.biezhi.keeper.core.cache.redis.RedisCache; 7 | import io.github.biezhi.keeper.core.web.filter.AuthenticFilter; 8 | import io.github.biezhi.keeper.enums.SubjectType; 9 | import io.github.biezhi.keeper.utils.WebUtil; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.context.annotation.Primary; 13 | import org.springframework.data.redis.core.StringRedisTemplate; 14 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 15 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.time.Duration; 20 | 21 | @Configuration 22 | public class KeeperBeanConfig extends WebMvcConfigurationSupport { 23 | 24 | @Override 25 | public void addCorsMappings(CorsRegistry registry) { 26 | registry.addMapping("/**") 27 | .allowedHeaders("*") 28 | .allowedMethods("*") 29 | .allowedOrigins("*"); 30 | } 31 | 32 | // @Bean 33 | // public AuthorizeRedisCache authorizeRedisCache(StringRedisTemplate stringRedisTemplate) { 34 | //// return new AuthorizeRedisCache(stringRedisTemplate, Duration.ofMinutes(10)); 35 | // return new AuthorizeRedisCache(stringRedisTemplate, Duration.ofSeconds(10)); 36 | // } 37 | 38 | @Bean 39 | public AuthenticFilter authenticFilter() { 40 | return new AuthenticFilter() { 41 | 42 | @Override 43 | protected void initFilterBean() { 44 | this.addPathPatterns("/**") 45 | .excludePathPatterns("/guest", "/login"); 46 | } 47 | 48 | @Override 49 | protected void unAuthentic(HttpServletRequest request, HttpServletResponse response) { 50 | WebUtil.writeJSON(response, 51 | Response.builder().code(500) 52 | .msg("请登录后访问") 53 | .build()); 54 | } 55 | }; 56 | } 57 | 58 | @Bean 59 | public KeeperAuthorizeBean keeperAuthorizeBean(UserService userService, StringRedisTemplate stringRedisTemplate) { 60 | return new KeeperAuthorizeBean(userService, stringRedisTemplate); 61 | } 62 | 63 | @Bean 64 | @Primary 65 | public Keeper initKeeper(Keeper keeper, StringRedisTemplate stringRedisTemplate) { 66 | keeper.setSubjectType(SubjectType.JWT); 67 | // keeper.setAuthenticInfoCache(new AuthenticRedisCache(stringRedisTemplate)); 68 | keeper.setKeeperCache(new RedisCache<>(stringRedisTemplate)); 69 | return keeper; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | build.sh 24 | 25 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 26 | hs_err_pid* 27 | ### Maven template 28 | target/ 29 | pom.xml.tag 30 | pom.xml.releaseBackup 31 | pom.xml.versionsBackup 32 | pom.xml.next 33 | release.properties 34 | dependency-reduced-pom.xml 35 | buildNumber.properties 36 | .mvn/timing.properties 37 | 38 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 39 | !/.mvn/wrapper/maven-wrapper.jar 40 | ### Eclipse template 41 | 42 | .metadata 43 | bin/ 44 | tmp/ 45 | *.tmp 46 | *.bak 47 | *.swp 48 | *~.nib 49 | local.properties 50 | .settings/ 51 | .loadpath 52 | .recommenders 53 | 54 | # External tool builders 55 | .externalToolBuilders/ 56 | 57 | # Locally stored "Eclipse launch configurations" 58 | *.launch 59 | 60 | # PyDev specific (Python IDE for Eclipse) 61 | *.pydevproject 62 | 63 | # CDT-specific (C/C++ Development Tooling) 64 | .cproject 65 | 66 | # CDT- autotools 67 | .autotools 68 | 69 | # Java annotation processor (APT) 70 | .factorypath 71 | 72 | # PDT-specific (PHP Development Tools) 73 | .buildpath 74 | 75 | # sbteclipse plugin 76 | .target 77 | 78 | # Tern plugin 79 | .tern-project 80 | 81 | # TeXlipse plugin 82 | .texlipse 83 | 84 | # STS (Spring Tool Suite) 85 | .springBeans 86 | 87 | # Code Recommenders 88 | .recommenders/ 89 | 90 | # Scala IDE specific (Scala & Java development for Eclipse) 91 | .cache-main 92 | .scala_dependencies 93 | .worksheet 94 | ### JetBrains template 95 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 96 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 97 | 98 | # User-specific stuff 99 | .idea/**/workspace.xml 100 | .idea/**/tasks.xml 101 | .idea/**/dictionaries 102 | .idea/**/shelf 103 | 104 | # Sensitive or high-churn files 105 | .idea/**/dataSources/ 106 | .idea/**/dataSources.ids 107 | .idea/**/dataSources.local.xml 108 | .idea/**/sqlDataSources.xml 109 | .idea/**/dynamic.xml 110 | .idea/**/uiDesigner.xml 111 | .idea/**/dbnavigator.xml 112 | 113 | # Gradle 114 | .idea/**/gradle.xml 115 | .idea/**/libraries 116 | 117 | # CMake 118 | cmake-build-debug/ 119 | cmake-build-release/ 120 | 121 | # Mongo Explorer plugin 122 | .idea/**/mongoSettings.xml 123 | 124 | # File-based project format 125 | *.iws 126 | 127 | # IntelliJ 128 | out/ 129 | 130 | # mpeltonen/sbt-idea plugin 131 | .idea_modules/ 132 | 133 | # JIRA plugin 134 | atlassian-ide-plugin.xml 135 | 136 | # Cursive Clojure plugin 137 | .idea/replstate.xml 138 | 139 | # Crashlytics plugin (for Android Studio and IntelliJ) 140 | com_crashlytics_export_strings.xml 141 | crashlytics.properties 142 | crashlytics-build.properties 143 | fabric.properties 144 | 145 | # Editor-based Rest Client 146 | .idea/httpRequests 147 | 148 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/Keeper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper; 17 | 18 | import io.github.biezhi.keeper.core.authc.Authorization; 19 | import io.github.biezhi.keeper.core.cache.Cache; 20 | import io.github.biezhi.keeper.core.cache.map.MapCache; 21 | import io.github.biezhi.keeper.core.config.JwtConfig; 22 | import io.github.biezhi.keeper.core.config.SessionConfig; 23 | import io.github.biezhi.keeper.core.subject.JwtSubject; 24 | import io.github.biezhi.keeper.core.subject.SessionSubject; 25 | import io.github.biezhi.keeper.core.subject.Subject; 26 | import io.github.biezhi.keeper.enums.SubjectType; 27 | import io.github.biezhi.keeper.utils.SpringContextUtil; 28 | import lombok.Setter; 29 | 30 | /** 31 | * @author biezhi 32 | * @date 2019-04-04 33 | */ 34 | public class Keeper { 35 | 36 | private boolean enableURIAuthorizeCache; 37 | 38 | @Setter 39 | private SubjectType subjectType = SubjectType.SESSION; 40 | 41 | private Authorization authorization; 42 | 43 | private JwtConfig jwtConfig; 44 | 45 | private SessionConfig sessionConfig; 46 | 47 | private Cache keeperCache = new MapCache<>(); 48 | 49 | public static Subject getSubject() { 50 | Keeper keeper = SpringContextUtil.getBean(Keeper.class); 51 | if (SubjectType.SESSION.equals(keeper.subjectType)) { 52 | return new SessionSubject(); 53 | } else if (SubjectType.JWT.equals(keeper.subjectType)) { 54 | return new JwtSubject(); 55 | } else { 56 | return new SessionSubject(); 57 | } 58 | } 59 | 60 | public boolean enableURIAuthorizeCache() { 61 | return enableURIAuthorizeCache; 62 | } 63 | 64 | public void enableURIAuthorizeCache(boolean enableURIAuthorizeCache) { 65 | this.enableURIAuthorizeCache = enableURIAuthorizeCache; 66 | } 67 | 68 | public Authorization getAuthorization() { 69 | return authorization; 70 | } 71 | 72 | public void setAuthorization(Authorization authorization) { 73 | this.authorization = authorization; 74 | } 75 | 76 | public JwtConfig getJwtConfig() { 77 | return jwtConfig; 78 | } 79 | 80 | public void setJwtConfig(JwtConfig jwtConfig) { 81 | this.jwtConfig = jwtConfig; 82 | } 83 | 84 | public SessionConfig getSessionConfig() { 85 | return sessionConfig; 86 | } 87 | 88 | public void setSessionConfig(SessionConfig sessionConfig) { 89 | this.sessionConfig = sessionConfig; 90 | } 91 | 92 | public Cache getKeeperCache() { 93 | return keeperCache; 94 | } 95 | 96 | public void setKeeperCache(Cache keeperCache) { 97 | this.keeperCache = keeperCache; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/cache/redis/RedisCache.java: -------------------------------------------------------------------------------- 1 | package io.github.biezhi.keeper.core.cache.redis; 2 | 3 | import io.github.biezhi.keeper.core.cache.Cache; 4 | import io.github.biezhi.keeper.utils.JsonUtil; 5 | import org.springframework.data.redis.core.StringRedisTemplate; 6 | 7 | import java.util.Set; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * @author biezhi 12 | * @date 2019-04-07 13 | */ 14 | public class RedisCache implements Cache { 15 | 16 | protected final StringRedisTemplate stringRedisTemplate; 17 | 18 | protected final String prefix; 19 | 20 | public RedisCache(StringRedisTemplate stringRedisTemplate) { 21 | this(stringRedisTemplate, ""); 22 | } 23 | 24 | public RedisCache(StringRedisTemplate stringRedisTemplate, String prefix) { 25 | this.stringRedisTemplate = stringRedisTemplate; 26 | this.prefix = prefix; 27 | } 28 | 29 | @Override 30 | public void set(String key, V value) { 31 | if (value instanceof String) { 32 | stringRedisTemplate.opsForValue().set(prefix + key, value.toString()); 33 | } else { 34 | String json = JsonUtil.toJSONString(value); 35 | stringRedisTemplate.opsForValue().set(prefix + key, json); 36 | } 37 | } 38 | 39 | @Override 40 | public void set(String key, V value, long seconds) { 41 | if (value instanceof String) { 42 | stringRedisTemplate.opsForValue().set(prefix + key, value.toString()); 43 | } else { 44 | String json = JsonUtil.toJSONString(value); 45 | stringRedisTemplate.opsForValue().set(prefix + key, json); 46 | } 47 | if (seconds > 0) { 48 | stringRedisTemplate.expire(prefix + key, seconds, TimeUnit.SECONDS); 49 | } 50 | } 51 | 52 | @Override 53 | public T get(String key, Class type) { 54 | String json = stringRedisTemplate.opsForValue().get(key); 55 | if (String.class.equals(type)) { 56 | return (T) json; 57 | } 58 | if (Integer.class.equals(type)) { 59 | return (T) Integer.valueOf(json); 60 | } 61 | if (Long.class.equals(type)) { 62 | return (T) Long.valueOf(json); 63 | } 64 | return JsonUtil.toBean(json, type); 65 | } 66 | 67 | @Override 68 | public void delWith(String keyPrefix) { 69 | Set keys = stringRedisTemplate.keys(keyPrefix + "*"); 70 | stringRedisTemplate.delete(keys); 71 | } 72 | 73 | @Override 74 | public boolean exists(String key) { 75 | if (null == key) { 76 | return false; 77 | } 78 | Boolean hasKey = stringRedisTemplate.hasKey(prefix + key); 79 | return null == hasKey ? false : hasKey; 80 | } 81 | 82 | @Override 83 | public boolean expire(String key) { 84 | if (null == key) { 85 | return false; 86 | } 87 | Long expire = stringRedisTemplate.getExpire(prefix + key); 88 | return null == expire || expire < 2; 89 | } 90 | 91 | @Override 92 | public void remove(String key) { 93 | String delKey = prefix + key; 94 | stringRedisTemplate.delete(delKey); 95 | stringRedisTemplate.hasKey(delKey); 96 | } 97 | 98 | @Override 99 | public Set keySet() { 100 | return stringRedisTemplate.keys(prefix); 101 | } 102 | 103 | @Override 104 | public void clear() { 105 | stringRedisTemplate.delete(keySet()); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/aspect/KeeperAspect.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.aspect; 17 | 18 | import io.github.biezhi.keeper.Keeper; 19 | import io.github.biezhi.keeper.annotation.Permissions; 20 | import io.github.biezhi.keeper.annotation.Roles; 21 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 22 | import io.github.biezhi.keeper.core.authc.Authorization; 23 | import io.github.biezhi.keeper.core.subject.Subject; 24 | import io.github.biezhi.keeper.exception.UnauthenticException; 25 | import io.github.biezhi.keeper.exception.UnauthorizedException; 26 | import io.github.biezhi.keeper.utils.WebUtil; 27 | import lombok.extern.slf4j.Slf4j; 28 | import org.aspectj.lang.JoinPoint; 29 | import org.aspectj.lang.annotation.Aspect; 30 | import org.aspectj.lang.annotation.Before; 31 | import org.aspectj.lang.annotation.Pointcut; 32 | import org.aspectj.lang.reflect.MethodSignature; 33 | import org.springframework.beans.factory.annotation.Autowired; 34 | 35 | import javax.servlet.http.HttpServletRequest; 36 | import java.lang.reflect.Method; 37 | import java.util.Map; 38 | import java.util.Objects; 39 | import java.util.concurrent.ConcurrentHashMap; 40 | 41 | @Slf4j 42 | @Aspect 43 | public class KeeperAspect { 44 | 45 | private static final Map SUBJECT_PERMISSION_CACHE = new ConcurrentHashMap<>(); 46 | 47 | @Autowired 48 | private Keeper keeper; 49 | 50 | @Pointcut("@annotation(io.github.biezhi.keeper.annotation.Permissions) || @annotation(io.github.biezhi.keeper.annotation.Roles)") 51 | public void permBefore() { 52 | } 53 | 54 | @Before("permBefore()") 55 | public void beforePermissions(JoinPoint joinPoint) { 56 | Authorization authorization = keeper.getAuthorization(); 57 | if (null == authorization) { 58 | throw new UnauthorizedException("Unauthorized"); 59 | } 60 | 61 | Subject subject = Keeper.getSubject(); 62 | if (null == subject || !subject.isLogin()) { 63 | throw UnauthenticException.build(); 64 | } 65 | 66 | int hash = 0; 67 | if (keeper.enableURIAuthorizeCache()) { 68 | HttpServletRequest request = WebUtil.currentRequest(); 69 | 70 | AuthenticInfo authenticInfo = subject.authenticInfo(); 71 | 72 | hash = Objects.hash( 73 | authenticInfo.username(), 74 | request.getMethod(), 75 | request.getRequestURI()); 76 | 77 | if (SUBJECT_PERMISSION_CACHE.containsKey(subject) && 78 | SUBJECT_PERMISSION_CACHE.get(subject).equals(hash)) { 79 | return; 80 | } 81 | } 82 | 83 | Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); 84 | 85 | Roles roles = method.getAnnotation(Roles.class); 86 | 87 | Permissions permissions = method.getAnnotation(Permissions.class); 88 | 89 | if (!subject.hasPermissions(roles, permissions)) { 90 | throw UnauthorizedException.build(); 91 | } 92 | if (keeper.enableURIAuthorizeCache()) { 93 | SUBJECT_PERMISSION_CACHE.put(subject, hash); 94 | } 95 | 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/subject/JwtSubject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.subject; 17 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; 19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 20 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 21 | import io.github.biezhi.keeper.core.authc.AuthorToken; 22 | import io.github.biezhi.keeper.core.authc.impl.SimpleAuthenticInfo; 23 | import io.github.biezhi.keeper.exception.ExpiredException; 24 | import io.github.biezhi.keeper.utils.JsonUtil; 25 | import io.github.biezhi.keeper.utils.StringUtil; 26 | import lombok.Data; 27 | import lombok.EqualsAndHashCode; 28 | import lombok.extern.slf4j.Slf4j; 29 | 30 | import static io.github.biezhi.keeper.keeperConst.KEEPER_AUTHENTIC_KEY; 31 | 32 | /** 33 | * JwtSubject 34 | * 35 | * @author biezhi 36 | * @date 2019-04-05 37 | */ 38 | @Data 39 | @Slf4j 40 | @JsonIgnoreProperties 41 | @EqualsAndHashCode(callSuper = true) 42 | public class JwtSubject extends SimpleSubject { 43 | 44 | @Override 45 | public AuthenticInfo authenticInfo() { 46 | if (!isLogin()) { 47 | return null; 48 | } 49 | String token = jwtToken().getAuthToken(); 50 | String username = jwtToken().getUsername(token); 51 | 52 | String authenticInfoKey = String.format(KEEPER_AUTHENTIC_KEY, username); 53 | 54 | if (keeperCache().exists(authenticInfoKey)) { 55 | return keeperCache().get(authenticInfoKey, SimpleAuthenticInfo.class); 56 | } 57 | AuthenticInfo authenticInfo = authentication().doAuthentic(() -> username); 58 | if (null == authenticInfo) { 59 | return null; 60 | } 61 | 62 | keeperCache().set(authenticInfoKey, JsonUtil.toJSONString(authenticInfo)); 63 | return authenticInfo; 64 | } 65 | 66 | @JsonIgnore 67 | @Override 68 | public AuthenticInfo login(AuthorToken token) { 69 | SimpleAuthenticInfo authenticInfo = (SimpleAuthenticInfo) super.login(token); 70 | 71 | String jwtToken = jwtToken().create(token.username(), authenticInfo.claims()); 72 | 73 | // 存储登录状态,处理注销 74 | this.recordLogin(authenticInfo, jwtToken); 75 | authenticInfo.setPayload(jwtToken); 76 | 77 | return authenticInfo; 78 | } 79 | 80 | @JsonIgnore 81 | @Override 82 | public void logout() { 83 | String token = jwtToken().getAuthToken(); 84 | String username = jwtToken().getUsername(token); 85 | if (StringUtil.isEmpty(username)) { 86 | return; 87 | } 88 | this.logoutResetCache(token, username); 89 | } 90 | 91 | 92 | @JsonIgnore 93 | @Override 94 | public boolean isLogin() { 95 | String token = jwtToken().getAuthToken(); 96 | if (StringUtil.isEmpty(token)) { 97 | return false; 98 | } 99 | 100 | String username = jwtToken().getUsername(token); 101 | if (StringUtil.isEmpty(username)) { 102 | return false; 103 | } 104 | 105 | // token 被撤销,如注销 106 | if (this.tokenBeRevoked(token, username)) { 107 | return false; 108 | } 109 | 110 | boolean expired = jwtToken().isExpired(token); 111 | if (expired) { 112 | this.removeLoginToken(username, token); 113 | throw ExpiredException.build(); 114 | } 115 | return true; 116 | } 117 | 118 | @Override 119 | public boolean renew() { 120 | String token = jwtToken().getAuthToken(); 121 | if (StringUtil.isEmpty(token)) { 122 | return false; 123 | } 124 | String username = jwtToken().getUsername(token); 125 | boolean canRefresh = jwtToken().canRenew(token); 126 | if (!canRefresh) { 127 | return false; 128 | } 129 | 130 | AuthenticInfo authenticInfo = authentication().doAuthentic(() -> username); 131 | 132 | String newToken = jwtToken().refresh(username, authenticInfo.claims()); 133 | log.debug("renew success, token: {}", newToken); 134 | 135 | // 存储登录状态,处理注销 136 | this.recordLogin(authenticInfo, newToken); 137 | return true; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/web/filter/AuthenticFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.web.filter; 17 | 18 | import io.github.biezhi.keeper.Keeper; 19 | import io.github.biezhi.keeper.core.subject.Subject; 20 | import io.github.biezhi.keeper.exception.ExpiredException; 21 | import io.github.biezhi.keeper.exception.UnauthorizedException; 22 | import io.github.biezhi.keeper.utils.WebUtil; 23 | import lombok.Setter; 24 | import lombok.extern.slf4j.Slf4j; 25 | import org.springframework.util.AntPathMatcher; 26 | import org.springframework.util.ObjectUtils; 27 | import org.springframework.util.PathMatcher; 28 | import org.springframework.web.filter.OncePerRequestFilter; 29 | 30 | import javax.servlet.FilterChain; 31 | import javax.servlet.ServletException; 32 | import javax.servlet.http.HttpServletRequest; 33 | import javax.servlet.http.HttpServletResponse; 34 | import java.io.IOException; 35 | import java.util.ArrayList; 36 | import java.util.Arrays; 37 | import java.util.List; 38 | 39 | /** 40 | * AuthenticFilter 41 | * 42 | * @author biezhi 43 | * @date 2019-04-07 44 | */ 45 | @Slf4j 46 | public class AuthenticFilter extends OncePerRequestFilter { 47 | 48 | private final List includePatterns = new ArrayList<>(); 49 | private final List excludePatterns = new ArrayList<>(); 50 | 51 | @Setter 52 | private PathMatcher pathMatcher = new AntPathMatcher(); 53 | 54 | public AuthenticFilter addPathPatterns(String... urls) { 55 | return addPathPatterns(Arrays.asList(urls)); 56 | } 57 | 58 | public AuthenticFilter addPathPatterns(List patterns) { 59 | this.includePatterns.addAll(patterns); 60 | return this; 61 | } 62 | 63 | public AuthenticFilter excludePathPatterns(String... urls) { 64 | return excludePathPatterns(Arrays.asList(urls)); 65 | } 66 | 67 | public AuthenticFilter excludePathPatterns(List patterns) { 68 | this.excludePatterns.addAll(patterns); 69 | return this; 70 | } 71 | 72 | /** 73 | * Determine a match for the given lookup path. 74 | * 75 | * @param lookupPath the current request path 76 | * @param pathMatcher a path matcher for path pattern matching 77 | * @return {@code true} if the interceptor applies to the given request path 78 | */ 79 | protected boolean matches(String lookupPath, PathMatcher pathMatcher) { 80 | PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher); 81 | if (!ObjectUtils.isEmpty(this.excludePatterns)) { 82 | for (String pattern : this.excludePatterns) { 83 | if (pathMatcherToUse.match(pattern, lookupPath)) { 84 | return false; 85 | } 86 | } 87 | } 88 | if (ObjectUtils.isEmpty(this.includePatterns)) { 89 | return true; 90 | } 91 | for (String pattern : this.includePatterns) { 92 | if (pathMatcherToUse.match(pattern, lookupPath)) { 93 | return true; 94 | } 95 | } 96 | return false; 97 | } 98 | 99 | @Override 100 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 101 | String lookupPath = request.getRequestURI(); 102 | // init web context 103 | WebUtil.initContext(request, response); 104 | 105 | // whether to skip the URI 106 | if (!this.matches(lookupPath, pathMatcher)) { 107 | this.doFilter(request, response, filterChain); 108 | WebUtil.removeRequest(); 109 | return; 110 | } 111 | boolean authentic; 112 | try { 113 | authentic = isAuthentic(request, response); 114 | } catch (Exception e) { 115 | this.authenticError(e, request, response, filterChain); 116 | WebUtil.removeRequest(); 117 | return; 118 | } 119 | if (authentic) { 120 | this.doFilter(request, response, filterChain); 121 | } else { 122 | this.unAuthentic(request, response); 123 | } 124 | WebUtil.removeRequest(); 125 | } 126 | 127 | /** 128 | * Whether the authentication is passed, 129 | *

130 | * return true when the user is logged in, continue the following process 131 | * 132 | * @return 133 | */ 134 | protected boolean isAuthentic(HttpServletRequest request, HttpServletResponse response) { 135 | Subject subject = Keeper.getSubject(); 136 | return subject.isLogin(); 137 | } 138 | 139 | /** 140 | * Processing logic when an authentication failure occurs abnormally 141 | */ 142 | protected void authenticError(Exception e, HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 143 | if (e instanceof ExpiredException) { 144 | Subject subject = Keeper.getSubject(); 145 | if (!subject.renew()) { 146 | this.unAuthentic(request, response); 147 | } else { 148 | this.doFilter(request, response, filterChain); 149 | } 150 | } else { 151 | log.error("authentic error", e); 152 | this.unAuthentic(request, response); 153 | } 154 | } 155 | 156 | /** 157 | * Processing logic when not logged in or authentication failed 158 | */ 159 | protected void unAuthentic(HttpServletRequest request, HttpServletResponse response) { 160 | throw UnauthorizedException.build(); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/subject/SessionSubject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.subject; 17 | 18 | import com.auth0.jwt.JWT; 19 | import com.auth0.jwt.JWTCreator; 20 | import com.auth0.jwt.JWTVerifier; 21 | import com.auth0.jwt.algorithms.Algorithm; 22 | import com.auth0.jwt.interfaces.DecodedJWT; 23 | import io.github.biezhi.keeper.Keeper; 24 | import io.github.biezhi.keeper.core.authc.AuthenticInfo; 25 | import io.github.biezhi.keeper.core.authc.AuthorToken; 26 | import io.github.biezhi.keeper.core.config.SessionConfig; 27 | import io.github.biezhi.keeper.exception.ExpiredException; 28 | import io.github.biezhi.keeper.utils.DateUtil; 29 | import io.github.biezhi.keeper.utils.SpringContextUtil; 30 | import io.github.biezhi.keeper.utils.StringUtil; 31 | import io.github.biezhi.keeper.utils.WebUtil; 32 | import lombok.Data; 33 | import lombok.EqualsAndHashCode; 34 | 35 | import javax.servlet.http.Cookie; 36 | import javax.servlet.http.HttpServletRequest; 37 | import javax.servlet.http.HttpServletResponse; 38 | import javax.servlet.http.HttpSession; 39 | import java.util.Date; 40 | 41 | import static io.github.biezhi.keeper.keeperConst.KEEPER_LOGIN_KEY; 42 | import static io.github.biezhi.keeper.keeperConst.KEEPER_SESSION_KEY; 43 | 44 | /** 45 | * SessionSubject 46 | * 47 | * @author biezhi 48 | * @date 2019-04-05 49 | */ 50 | @Data 51 | @EqualsAndHashCode(callSuper = true) 52 | public class SessionSubject extends SimpleSubject { 53 | 54 | @Override 55 | public AuthenticInfo authenticInfo() { 56 | if (!isLogin()) { 57 | return null; 58 | } 59 | HttpSession session = WebUtil.currentSession(); 60 | if (null == session) { 61 | return null; 62 | } 63 | return (AuthenticInfo) session.getAttribute(KEEPER_SESSION_KEY); 64 | } 65 | 66 | @Override 67 | public AuthenticInfo login(AuthorToken token) { 68 | HttpSession session = WebUtil.currentSession(true); 69 | if (null == session) { 70 | return null; 71 | } 72 | 73 | AuthenticInfo authenticInfo = super.login(token); 74 | 75 | session.setAttribute(KEEPER_SESSION_KEY, authenticInfo); 76 | 77 | // remember me 78 | SessionConfig config = sessionConfig(); 79 | if (!token.remember() || null == config.getRenewExpires()) { 80 | return authenticInfo; 81 | } 82 | 83 | HttpServletResponse response = WebUtil.currentResponse(); 84 | 85 | String cookieToken = generateToken(token.username()); 86 | 87 | Cookie cookie = new Cookie(config.getCookieName(), cookieToken); 88 | cookie.setPath(config.getPath()); 89 | cookie.setSecure(config.isSecure()); 90 | cookie.setHttpOnly(config.isHttpOnly()); 91 | cookie.setMaxAge((int) config.getRenewExpires().toMillis() / 1000); 92 | if (StringUtil.isNotEmpty(config.getDomain())) { 93 | cookie.setDomain(config.getDomain()); 94 | } 95 | response.addCookie(cookie); 96 | 97 | // 存储登录状态,处理注销 98 | this.recordLogin(token.username(), cookieToken); 99 | 100 | return authenticInfo; 101 | } 102 | 103 | @Override 104 | public boolean isLogin() { 105 | SessionConfig config = sessionConfig(); 106 | HttpSession session = WebUtil.currentSession(true); 107 | if (null == session) { 108 | return false; 109 | } 110 | Object attribute = session.getAttribute(KEEPER_SESSION_KEY); 111 | if (null != attribute) { 112 | return true; 113 | } 114 | 115 | if (config.getRenewExpires() != null) { 116 | throw ExpiredException.build(); 117 | } 118 | return false; 119 | } 120 | 121 | @Override 122 | public boolean renew() { 123 | HttpSession session = WebUtil.currentSession(); 124 | 125 | String token = ""; 126 | Cookie cookie = getRenewCookie(); 127 | if (null != cookie) { 128 | token = cookie.getValue(); 129 | } 130 | 131 | if (StringUtil.isEmpty(token)) { 132 | return false; 133 | } 134 | String username = getUsername(token); 135 | if (StringUtil.isEmpty(username)) { 136 | return false; 137 | } 138 | 139 | // token 被撤销,如注销 140 | if (this.tokenBeRevoked(token, username)) { 141 | return false; 142 | } 143 | 144 | AuthenticInfo authenticInfo = authentication().doAuthentic(() -> username); 145 | session.setAttribute(KEEPER_SESSION_KEY, authenticInfo); 146 | return true; 147 | } 148 | 149 | @Override 150 | public void logout() { 151 | HttpSession session = WebUtil.currentSession(); 152 | if (null == session) { 153 | return; 154 | } 155 | session.removeAttribute(KEEPER_SESSION_KEY); 156 | 157 | HttpServletResponse response = WebUtil.currentResponse(); 158 | 159 | Cookie cookie = getRenewCookie(); 160 | if (null == cookie) { 161 | return; 162 | } 163 | String token = cookie.getValue(); 164 | cookie.setValue(""); 165 | cookie.setMaxAge(-1); 166 | response.addCookie(cookie); 167 | 168 | String username = getUsername(token); 169 | if (StringUtil.isEmpty(username)) { 170 | return; 171 | } 172 | 173 | // 修改当前 token 的过期时间 174 | String loginTokenKey = String.format(KEEPER_LOGIN_KEY, username, token.substring(token.lastIndexOf(".") + 1)); 175 | keeperCache().set(loginTokenKey, System.currentTimeMillis() / 1000 + ""); 176 | 177 | } 178 | 179 | private SessionConfig sessionConfig() { 180 | return SpringContextUtil.getBean(Keeper.class).getSessionConfig(); 181 | } 182 | 183 | private Cookie getRenewCookie() { 184 | HttpServletRequest request = WebUtil.currentRequest(); 185 | Cookie[] cookies = request.getCookies(); 186 | for (Cookie cookie : cookies) { 187 | if (cookie.getName().equals(sessionConfig().getCookieName())) { 188 | return cookie; 189 | } 190 | } 191 | return null; 192 | } 193 | 194 | private String generateToken(String username) { 195 | SessionConfig config = sessionConfig(); 196 | JWTCreator.Builder builder = JWT.create() 197 | .withSubject(username) 198 | .withIssuedAt(new Date()) 199 | .withExpiresAt(DateUtil.plus(config.getRenewExpires().toMillis())); 200 | 201 | return builder.sign(Algorithm.HMAC256(config.getSecret())); 202 | } 203 | 204 | private String getUsername(String token) { 205 | if (StringUtil.isEmpty(token)) { 206 | return null; 207 | } 208 | SessionConfig config = sessionConfig(); 209 | try { 210 | JWTVerifier verifier = JWT.require( 211 | Algorithm.HMAC256(config.getSecret())) 212 | .build(); 213 | 214 | DecodedJWT jwt = verifier.verify(token); 215 | return jwt.getSubject(); 216 | } catch (Exception e) { 217 | return null; 218 | } 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/jwt/SimpleJwtToken.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.jwt; 17 | 18 | import com.auth0.jwt.JWT; 19 | import com.auth0.jwt.JWTCreator; 20 | import com.auth0.jwt.algorithms.Algorithm; 21 | import com.auth0.jwt.interfaces.Claim; 22 | import com.auth0.jwt.interfaces.DecodedJWT; 23 | import io.github.biezhi.keeper.Keeper; 24 | import io.github.biezhi.keeper.core.cache.Cache; 25 | import io.github.biezhi.keeper.core.config.JwtConfig; 26 | import io.github.biezhi.keeper.exception.KeeperException; 27 | import io.github.biezhi.keeper.utils.DateUtil; 28 | import io.github.biezhi.keeper.utils.SpringContextUtil; 29 | import io.github.biezhi.keeper.utils.StringUtil; 30 | import io.github.biezhi.keeper.utils.WebUtil; 31 | 32 | import javax.servlet.http.HttpServletRequest; 33 | import javax.servlet.http.HttpServletResponse; 34 | import java.time.Duration; 35 | import java.time.Instant; 36 | import java.util.Date; 37 | import java.util.Map; 38 | import java.util.Optional; 39 | 40 | import static io.github.biezhi.keeper.keeperConst.LOGOUT_KEY; 41 | 42 | public class SimpleJwtToken implements JwtToken { 43 | 44 | /** 45 | * Token the latest refresh token after expiration. 46 | *

47 | * Does not refresh by default, effective when the configuration refresh expires. 48 | * 49 | * @see JwtConfig#setRenewExpires(Duration) 50 | */ 51 | private static final String REFRESH_EXPIRES_AT = "rea"; 52 | 53 | /** 54 | * When the token is renewed, the request is guaranteed to be executed correctly, 55 | * and the new token is cached in the request attribute 56 | */ 57 | private static final String NEW_TOKEN = "auth:new_token"; 58 | 59 | /** 60 | * JWT configuration, including key, expiration time, headers, and so on 61 | * 62 | * @see JwtConfig 63 | */ 64 | private final JwtConfig config; 65 | 66 | public SimpleJwtToken(JwtConfig config) { 67 | this.config = config; 68 | } 69 | 70 | @Override 71 | public String create(String username, Map claims) { 72 | JWTCreator.Builder builder = JWT.create() 73 | .withSubject(username) 74 | .withIssuedAt(new Date()) 75 | .withExpiresAt(DateUtil.plus(config.getExpires().toMillis())); 76 | 77 | if (null != config.getRenewExpires() && 78 | config.getRenewExpires().toMillis() > 0) { 79 | 80 | builder.withClaim(REFRESH_EXPIRES_AT, 81 | DateUtil.plus(config.getRenewExpires().toMillis())); 82 | } 83 | 84 | if (null != claims && !claims.isEmpty()) { 85 | claims.forEach((key, value) -> { 86 | addClaim(builder, key, value); 87 | }); 88 | } 89 | 90 | return builder.sign(Algorithm.HMAC256(config.getSecret())); 91 | } 92 | 93 | @Override 94 | public String getUsername(String token) { 95 | if (StringUtil.isEmpty(token)) { 96 | return null; 97 | } 98 | return this.parseToken(token) 99 | .map(DecodedJWT::getSubject) 100 | .orElse(null); 101 | } 102 | 103 | @Override 104 | public long getCreateTime(String token) { 105 | if (StringUtil.isEmpty(token)) { 106 | return 0L; 107 | } 108 | return this.parseToken(token) 109 | .map(DecodedJWT::getIssuedAt) 110 | .map(Date::getTime) 111 | .map(time -> time / 1000) 112 | .orElse(0L); 113 | } 114 | 115 | @Override 116 | public long getExpireTime(String token) { 117 | if (StringUtil.isEmpty(token)) { 118 | return 0L; 119 | } 120 | return this.parseToken(token) 121 | .map(DecodedJWT::getExpiresAt) 122 | .map(Date::getTime) 123 | .map(time -> time / 1000) 124 | .orElse(0L); 125 | } 126 | 127 | @Override 128 | public long getRenewExpireTime(String token) { 129 | if (StringUtil.isEmpty(token)) { 130 | return 0L; 131 | } 132 | return this.parseToken(token) 133 | .map(decode -> decode.getClaim(REFRESH_EXPIRES_AT)) 134 | .map(Claim::asLong) 135 | .orElse(0L); 136 | } 137 | 138 | @Override 139 | public boolean isExpired(String token) { 140 | if (StringUtil.isEmpty(token)) { 141 | return true; 142 | } 143 | 144 | Date expiresAt = this.parseToken(token) 145 | .map(DecodedJWT::getExpiresAt) 146 | .orElseThrow(() -> 147 | new KeeperException("Invalid token type, missing expires_at claim") 148 | ); 149 | 150 | return expiresAt.before(new Date()); 151 | } 152 | 153 | @Override 154 | public boolean canRenew(String token) { 155 | if (StringUtil.isEmpty(token)) { 156 | return false; 157 | } 158 | 159 | Long expiresAt = this.parseToken(token) 160 | .map(decode -> decode.getClaim(REFRESH_EXPIRES_AT)) 161 | .map(Claim::asLong) 162 | .orElse(0L); 163 | 164 | long now = Instant.now().getEpochSecond(); 165 | return expiresAt > now; 166 | } 167 | 168 | @Override 169 | public String getAuthToken() { 170 | HttpServletRequest request = WebUtil.currentRequest(); 171 | if (null == request) { 172 | return null; 173 | } 174 | if (null != request.getAttribute(NEW_TOKEN)) { 175 | return (String) request.getAttribute(NEW_TOKEN); 176 | } 177 | String authorization = request.getHeader(config.getHeader()); 178 | if (StringUtil.isEmpty(authorization) || 179 | !authorization.contains(config.getTokenHead())) { 180 | return null; 181 | } 182 | return authorization.replace(config.getTokenHead(), ""); 183 | } 184 | 185 | @Override 186 | public String refresh(String username, Map claims) { 187 | HttpServletRequest request = WebUtil.currentRequest(); 188 | HttpServletResponse response = WebUtil.currentResponse(); 189 | 190 | if (null == request || null == response) { 191 | return null; 192 | } 193 | 194 | String token = create(username, claims); 195 | request.setAttribute(NEW_TOKEN, token); 196 | response.setHeader(config.getHeader(), token); 197 | return token; 198 | } 199 | 200 | private void addClaim(JWTCreator.Builder builder, String key, Object value) { 201 | if (null == key || null == value) { 202 | return; 203 | } 204 | if (value instanceof String) { 205 | builder.withClaim(key, (String) value); 206 | } 207 | if (value instanceof Boolean) { 208 | builder.withClaim(key, (Boolean) value); 209 | } 210 | if (value instanceof Long) { 211 | builder.withClaim(key, (Long) value); 212 | } 213 | if (value instanceof Integer) { 214 | builder.withClaim(key, (Integer) value); 215 | } 216 | if (value instanceof Double) { 217 | builder.withClaim(key, (Double) value); 218 | } 219 | if (value instanceof Date) { 220 | builder.withClaim(key, (Date) value); 221 | } 222 | } 223 | 224 | public Cache logoutCache() { 225 | return SpringContextUtil.getBean(Keeper.class).getKeeperCache(); 226 | } 227 | 228 | private Optional parseToken(String token) { 229 | try { 230 | return Optional.of(JWT.decode(token)); 231 | } catch (Exception e) { 232 | return Optional.empty(); 233 | } 234 | } 235 | 236 | } 237 | -------------------------------------------------------------------------------- /keeper-core/src/main/java/io/github/biezhi/keeper/core/subject/SimpleSubject.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, biezhi (biezhi.me@gmail.com) 3 | *

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

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.github.biezhi.keeper.core.subject; 17 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; 19 | import io.github.biezhi.keeper.Keeper; 20 | import io.github.biezhi.keeper.annotation.Permissions; 21 | import io.github.biezhi.keeper.annotation.Roles; 22 | import io.github.biezhi.keeper.core.authc.*; 23 | import io.github.biezhi.keeper.core.authc.cipher.Cipher; 24 | import io.github.biezhi.keeper.core.cache.AuthorizeCache; 25 | import io.github.biezhi.keeper.core.cache.Cache; 26 | import io.github.biezhi.keeper.core.jwt.JwtToken; 27 | import io.github.biezhi.keeper.enums.Logical; 28 | import io.github.biezhi.keeper.exception.UnauthenticException; 29 | import io.github.biezhi.keeper.exception.WrongPasswordException; 30 | import io.github.biezhi.keeper.utils.JsonUtil; 31 | import io.github.biezhi.keeper.utils.SpringContextUtil; 32 | import lombok.Data; 33 | 34 | import java.util.Arrays; 35 | import java.util.List; 36 | import java.util.Set; 37 | 38 | import static io.github.biezhi.keeper.keeperConst.KEEPER_AUTHENTIC_KEY; 39 | import static io.github.biezhi.keeper.keeperConst.KEEPER_LOGIN_KEY; 40 | 41 | /** 42 | * @author biezhi 43 | * @date 2019-04-05 44 | */ 45 | @Data 46 | public abstract class SimpleSubject implements Subject { 47 | 48 | protected Authentication authentication() { 49 | return SpringContextUtil.getBean(Authentication.class); 50 | } 51 | 52 | protected JwtToken jwtToken() { 53 | return SpringContextUtil.getBean(JwtToken.class); 54 | } 55 | 56 | protected Cache keeperCache() { 57 | return keeper().getKeeperCache(); 58 | } 59 | 60 | protected Keeper keeper() { 61 | return SpringContextUtil.getBean(Keeper.class); 62 | } 63 | 64 | @JsonIgnore 65 | @Override 66 | public AuthenticInfo login(AuthorToken token) { 67 | AuthenticInfo authenticInfo = authentication().doAuthentic(token); 68 | 69 | if (null == authenticInfo) { 70 | throw UnauthenticException.build("AuthenticInfo can not be null."); 71 | } 72 | 73 | Cipher cipher = authentication().cipher(); 74 | 75 | if (null != cipher && !cipher.verify(token, authenticInfo)) { 76 | throw WrongPasswordException.build(); 77 | } 78 | return authenticInfo; 79 | } 80 | 81 | protected void recordLogin(String username, String token) { 82 | String loginTokenKey = String.format(KEEPER_LOGIN_KEY, username, token.substring(token.lastIndexOf(".") + 1)); 83 | long createTime = jwtToken().getCreateTime(token); 84 | long expireTime = jwtToken().getExpireTime(token); 85 | 86 | long seconds = expireTime - (System.currentTimeMillis() / 1000); 87 | keeperCache().set(loginTokenKey, String.valueOf(createTime), seconds); 88 | } 89 | 90 | protected void recordLogin(AuthenticInfo authenticInfo, String token) { 91 | String loginTokenKey = String.format(KEEPER_LOGIN_KEY, authenticInfo.username(), token.substring(token.lastIndexOf(".") + 1)); 92 | long createTime = jwtToken().getCreateTime(token); 93 | long expireTime = jwtToken().getExpireTime(token); 94 | 95 | long seconds = expireTime - (System.currentTimeMillis() / 1000); 96 | keeperCache().set(loginTokenKey, String.valueOf(createTime), seconds); 97 | 98 | String authenticInfoKey = String.format(KEEPER_AUTHENTIC_KEY, authenticInfo.username()); 99 | keeperCache().set(authenticInfoKey, JsonUtil.toJSONString(authenticInfo), seconds); 100 | } 101 | 102 | protected boolean tokenBeRevoked(String token, String username) { 103 | String loginTokenKey = String.format(KEEPER_LOGIN_KEY, username, token.substring(token.lastIndexOf(".") + 1)); 104 | if (!keeperCache().exists(loginTokenKey)) { 105 | return false; 106 | } 107 | long tokenCreateTime = jwtToken().getCreateTime(token); 108 | 109 | Long time = keeperCache().get(loginTokenKey, Long.class); 110 | if (tokenCreateTime == time) { 111 | return false; 112 | } 113 | return true; 114 | } 115 | 116 | protected void logoutResetCache(String token, String username) { 117 | long renewExpireTime = jwtToken().getRenewExpireTime(token); 118 | 119 | long seconds = renewExpireTime - System.currentTimeMillis() / 1000; 120 | 121 | // 重置 token 的登录时间,不能删除,因为 token 可能未过期 122 | String loginTokenKey = String.format(KEEPER_LOGIN_KEY, username, token.substring(token.lastIndexOf(".") + 1)); 123 | keeperCache().set(loginTokenKey, System.currentTimeMillis() / 1000 + "", seconds); 124 | 125 | String authenticInfoKey = String.format(KEEPER_AUTHENTIC_KEY, username); 126 | keeperCache().remove(authenticInfoKey); 127 | } 128 | 129 | protected void removeLoginToken(String username, String token) { 130 | String loginTokenKey = String.format(KEEPER_LOGIN_KEY, username, token.substring(token.lastIndexOf(".") + 1)); 131 | keeperCache().remove(loginTokenKey); 132 | } 133 | 134 | @JsonIgnore 135 | @Override 136 | public boolean hasPermissions(Roles roleAnnotation, Permissions permAnnotation) { 137 | AuthorizeInfo authorizeInfo = this.authorize(false); 138 | if (null == authorizeInfo) { 139 | return false; 140 | } 141 | if (null != roleAnnotation && null != authorizeInfo.getRoles()) { 142 | if (Logical.AND.equals(roleAnnotation.logical())) { 143 | return rolesAnd(authorizeInfo.getRoles(), roleAnnotation.value()); 144 | } 145 | if (Logical.OR.equals(roleAnnotation.logical())) { 146 | String[] value = roleAnnotation.value(); 147 | for (String s : value) { 148 | return authorizeInfo.getRoles().contains(s); 149 | } 150 | } 151 | } 152 | if (null != permAnnotation && null != authorizeInfo.getPermissions()) { 153 | if (Logical.AND.equals(permAnnotation.logical())) { 154 | return permissionsAnd(authorizeInfo.getPermissions(), permAnnotation.value()); 155 | } 156 | if (Logical.OR.equals(permAnnotation.logical())) { 157 | String[] value = permAnnotation.value(); 158 | for (String s : value) { 159 | return authorizeInfo.getPermissions().contains(s); 160 | } 161 | } 162 | } 163 | return false; 164 | } 165 | 166 | @JsonIgnore 167 | @Override 168 | public void refreshAuthorize() { 169 | this.authorize(true); 170 | } 171 | 172 | protected AuthorizeInfo authorize(boolean reload) { 173 | AuthenticInfo authenticInfo = this.authenticInfo(); 174 | Authorization authorization = SpringContextUtil.getBean(Keeper.class).getAuthorization(); 175 | String username = authenticInfo.username(); 176 | AuthorizeCache cache = authorization.loadWithCache(); 177 | if (cache == null) { 178 | return authorization.doAuthorization(authenticInfo); 179 | } 180 | 181 | if (reload) { 182 | cache.remove(username); 183 | } 184 | if (!cache.cached(username)) { 185 | AuthorizeInfo authorizeInfo = authorization.doAuthorization(authenticInfo); 186 | if (null != authorizeInfo) { 187 | cache.set(username, authorizeInfo); 188 | } 189 | return authorizeInfo; 190 | } else { 191 | return cache.getAuthorizeInfo(username); 192 | } 193 | } 194 | 195 | private boolean rolesAnd(Set roles, String[] value) { 196 | List roleList = Arrays.asList(value); 197 | return roles.containsAll(roleList); 198 | } 199 | 200 | private boolean permissionsAnd(Set permissions, String[] value) { 201 | List roleList = Arrays.asList(value); 202 | return permissions.containsAll(roleList); 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.biezhi 8 | keeper 9 | pom 10 | 1.0-SNAPSHOT 11 | keeper 12 | https://biezhi.github.io/keeper 13 | keeper 14 | 15 | 16 | 17 | The Apache Software License, Version 2.0 18 | http://www.apache.org/licenses/LICENSE-2.0.txt 19 | 20 | 21 | 22 | 23 | biezhi 24 | biezhi.me@gmail.com 25 | 26 | 27 | 28 | scm:git@github.com:keeper/biezhi.git 29 | scm:git@github.com:keeper/biezhi.git 30 | git@github.com:keeper/biezhi.git 31 | 32 | 33 | 34 | keeper-core 35 | keeper-spring-boot-web-starter 36 | keeper-examples 37 | 38 | 39 | 40 | 1.8 41 | 1.7.25 42 | 2.1.3.RELEASE 43 | 5.1.5.RELEASE 44 | 3.1.0 45 | 3.4.0 46 | 1.18.6 47 | 0.0.1-beta 48 | 0.0.1-beta 49 | 50 | 51 | 52 | 53 | 54 | org.slf4j 55 | slf4j-api 56 | ${slf4j-api.version} 57 | 58 | 59 | 60 | javax.servlet 61 | javax.servlet-api 62 | ${servlet-api.version} 63 | 64 | 65 | 66 | org.springframework 67 | spring-context 68 | ${spring.version} 69 | 70 | 71 | org.springframework 72 | spring-web 73 | ${spring.version} 74 | 75 | 76 | org.springframework 77 | spring-aop 78 | ${spring.version} 79 | 80 | 81 | org.springframework 82 | spring-aspects 83 | ${spring.version} 84 | 85 | 86 | 87 | org.projectlombok 88 | lombok 89 | ${lombok.version} 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-autoconfigure 95 | ${spring-boot.version} 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-configuration-processor 100 | ${spring-boot.version} 101 | 102 | 103 | 104 | org.springframework.boot 105 | spring-boot-starter-web 106 | ${spring-boot.version} 107 | 108 | 109 | 110 | com.auth0 111 | java-jwt 112 | ${jwt.version} 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-compiler-plugin 123 | 124 | ${java.version} 125 | ${java.version} 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | release 134 | 135 | 136 | oss 137 | 138 | https://oss.sonatype.org/content/repositories/snapshots/ 139 | 140 | 141 | 142 | oss 143 | 144 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | org.apache.maven.plugins 153 | maven-source-plugin 154 | 2.4 155 | 156 | 157 | package 158 | 159 | jar-no-fork 160 | 161 | 162 | 163 | 164 | 165 | 166 | org.apache.maven.plugins 167 | maven-javadoc-plugin 168 | 2.10.2 169 | 170 | UTF-8 171 | UTF-8 172 | 173 | 174 | 175 | package 176 | 177 | jar 178 | 179 | 180 | -Xdoclint:none 181 | 182 | 183 | 184 | 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-gpg-plugin 189 | 1.6 190 | 191 | 192 | sign-artifacts 193 | verify 194 | 195 | sign 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | snapshots 205 | 206 | 207 | oss 208 | 209 | https://oss.sonatype.org/content/repositories/snapshots/ 210 | 211 | 212 | 213 | oss 214 | 215 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | org.apache.maven.plugins 224 | maven-source-plugin 225 | 2.4 226 | 227 | 228 | package 229 | 230 | jar-no-fork 231 | 232 | 233 | 234 | 235 | 236 | 237 | org.apache.maven.plugins 238 | maven-surefire-plugin 239 | 2.17 240 | 241 | true 242 | 243 | 244 | 245 | 246 | org.apache.maven.plugins 247 | maven-gpg-plugin 248 | 1.6 249 | 250 | 251 | sign-artifacts 252 | verify 253 | 254 | sign 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 biezhi.me@gmail.com 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. --------------------------------------------------------------------------------