diff --git a/README.md b/README.md index ac6ca293..a602bc25 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Ever since sureness was born, we hope to solve these, provide a **REST API**, ** > [Sureness](https://github.com/usthe/sureness) is a simple and efficient open-source security framework that focus on the protection of REST API. > Provide authentication and authorization, based on RBAC. -> No specific framework dependency (supports Javalin, Spring Boot, Quarkus, Ktor, and more). +> No specific framework dependency (supports Javalin, Spring Boot, Quarkus, Ktor, Micronaut and more). > Supports dynamic modification of permissions. > Supports WebSockets and mainstream HTTP containers (Servlet and JAX-RS). > Supports JWT, Basic Auth, Digest Auth, and can be extended to support custom authentication methods. @@ -71,7 +71,8 @@ Detail see [Benchmark Test](https://github.com/tomsun28/sureness-shiro-spring-se - [x] sureness integration quarkus sample [sample-quarkus](samples/quarkus-sureness) - [x] sureness integration javalin sample [sample-javalin](samples/javalin-sureness) - [x] sureness integration ktor sample [sample-ktor](samples/ktor-sureness) -- [x] sureness integration spring webflux sample [sample-spring-webflux](samples/spring-webflux-sureness) +- [x] sureness integration spring webflux sample [sample-spring-webflux](samples/spring-webflux-sureness) +- [x] sureness integration micronaut sample [sample-micronaut](samples/micronaut-sureness) - [x] sureness integration session sample [sureness-session](samples/sureness-session) - [x] sureness integration redis cache session sample [sureness-redis-session](samples/sureness-redis-session) - [x] more samples todo @@ -95,11 +96,11 @@ When use maven or gradle build project, add coordinate com.usthe.sureness sureness-core - 1.0.2 + 1.0.3 ``` ``` -compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.2' +compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.3' ``` #### 🐵 Use the Default Configuration to Configure Sureness diff --git a/README_CN.md b/README_CN.md index f2576cf0..5020bd3a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -31,7 +31,7 @@ > `sureness` 是我们在深度使用权限框架 `apache shiro` 之后,吸取其优点全新设计开发的一个认证鉴权框架 > 面向 `REST API` 的认证鉴权,基于 `RBAC` (用户-角色-资源)主要关注于对 `API` 的安全保护 -> 无特定框架依赖(本质就是过滤器处拦截判断,已有 `Springboot,Quarkus,Javalin,Ktor` 等集成样例) +> 无特定框架依赖(本质就是过滤器处拦截判断,已有 `Springboot,Quarkus,Javalin,Ktor,Micronaut` 等集成样例) > 支持动态修改权限配置(动态修改配置每个 `API` 谁有权访问) > 支持 `Websocket` ,主流 `HTTP` 容器 `Servlet` 和 `JAX-RS` > 支持多种认证策略, `JWT, Basic auth, Digest auth` ... 可扩展自定义支持的认证方式 @@ -72,6 +72,7 @@ - [x] sureness集成javalin样例 [sample-javalin](samples/javalin-sureness) - [x] sureness集成ktor样例 [sample-ktor](samples/ktor-sureness) - [x] sureness集成spring webflux样例 [sample-spring-webflux](samples/spring-webflux-sureness) +- [x] sureness集成micronaut样例 [sample-micronaut](samples/micronaut-sureness) - [x] sureness使用session样例 [sureness-session](samples/sureness-session) - [x] sureness分布式缓存session样例 [sureness-redis-session](samples/sureness-redis-session) - [x] more samples todo @@ -95,11 +96,11 @@ com.usthe.sureness sureness-core - 1.0.2 + 1.0.3 ``` ``` -compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.2' +compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.3' ``` #### 🐵 使用默认配置来配置sureness diff --git a/core/pom.xml b/core/pom.xml index 620b6e8f..7bbff6bb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.usthe.sureness sureness-core - 1.0.2 + 1.0.3 jar sureness diff --git a/docs/README.md b/docs/README.md index 5dc4a6be..27387d96 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,7 +9,7 @@ > A simple and efficient open-source jvm security framework that focus on the protection of restful api. [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) -[![Maven](https://img.shields.io/badge/Maven%20Central-1.0.2-blue.svg)](https://search.maven.org/artifact/com.usthe.sureness/sureness-core) +[![Maven](https://img.shields.io/badge/Maven%20Central-1.0.3-blue.svg)](https://search.maven.org/artifact/com.usthe.sureness/sureness-core) ![GitHub pull request check contexts](https://img.shields.io/github/status/contexts/pulls/tomsun28/sureness/8?label=pull%20checks) [![Gitter](https://img.shields.io/gitter/room/usthe/sureness?label=sureness&color=orange&logo=gitter&logoColor=red)](https://gitter.im/usthe/sureness) ![GitHub Release Date](https://img.shields.io/github/release-date/tomsun28/sureness?color=blue&logo=figshare&logoColor=red) @@ -66,6 +66,7 @@ Detail see [Benchmark Test](https://github.com/tomsun28/sureness-shiro-spring-se - [x] sureness integration javalin sample [sample-javalin](sample-javalin.md) - [x] sureness integration ktor sample [sample-ktor](sample-ktor.md) - [x] sureness integration spring webflux sample [spring-webflux-sureness](sample-spring-webflux.md) +- [x] sureness integration micronaut sample [sample-micronaut](sample-micronaut.md) - [x] sureness integration session sample [sureness-session](https://github.com/usthe/sureness/tree/master/samples/sureness-session) - [x] sureness integration redis cache session sample [sureness-redis-session](https://github.com/usthe/sureness/tree/master/samples/sureness-redis-session) - [x] more samples todo diff --git a/docs/_coverpage.md b/docs/_coverpage.md index a4bf3625..aa12d82c 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,6 +1,6 @@ ![logo](_media/icon128.svg) -# sureness 1.0.2 for jvm +# sureness 1.0.3 for jvm > Focusing on Protection of REST API. diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 77f86c65..eca429ab 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -21,6 +21,7 @@ - [Spring-Webflux-Sureness Sample](sample-spring-webflux.md) - [Javalin-Sureness Sample](sample-javalin.md) - [Ktor-Sureness Sample](sample-ktor.md) + - [Micronaut-Sureness Sample](sample-micronaut.md) - Others - [Design](design.md) diff --git a/docs/cn/README.md b/docs/cn/README.md index 5aee9bd2..08c24b21 100644 --- a/docs/cn/README.md +++ b/docs/cn/README.md @@ -9,7 +9,7 @@ > 面向`REST API`的高性能认证鉴权框架 [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) -[![Maven](https://img.shields.io/badge/Maven%20Central-1.0.2-blue.svg)](https://search.maven.org/artifact/com.usthe.sureness/sureness-core) +[![Maven](https://img.shields.io/badge/Maven%20Central-1.0.3-blue.svg)](https://search.maven.org/artifact/com.usthe.sureness/sureness-core) ![GitHub pull request check contexts](https://img.shields.io/github/status/contexts/pulls/tomsun28/sureness/8?label=pull%20checks) [![Gitter](https://img.shields.io/gitter/room/usthe/sureness?label=sureness&color=orange&logo=gitter&logoColor=red)](https://gitter.im/usthe/sureness) ![GitHub Release Date](https://img.shields.io/github/release-date/tomsun28/sureness?color=blue&logo=figshare&logoColor=red) @@ -67,6 +67,7 @@ - [x] sureness集成javalin样例 [sample-javalin](cn/sample-javalin.md) - [x] sureness集成ktor样例 [sample-ktor](cn/sample-ktor.md) - [x] sureness集成spring webflux样例 [spring-webflux-sureness](cn/sample-spring-webflux.md) +- [x] sureness集成micronaut样例 [sample-micronaut](cn/sample-micronaut.md) - [x] sureness使用session样例 [sureness-session](https://github.com/usthe/sureness/tree/master/samples/sureness-session) - [x] sureness分布式缓存session样例 [sureness-redis-session](https://github.com/usthe/sureness/tree/master/samples/sureness-redis-session) - [x] more samples todo diff --git a/docs/cn/_coverpage.md b/docs/cn/_coverpage.md index 400fd894..fde14fe6 100644 --- a/docs/cn/_coverpage.md +++ b/docs/cn/_coverpage.md @@ -1,6 +1,6 @@ ![logo](../_media/icon128.svg) -# sureness 1.0.2 for jvm +# sureness 1.0.3 for jvm > 面向`REST API`的高性能认证鉴权框架 diff --git a/docs/cn/_sidebar.md b/docs/cn/_sidebar.md index 1ca9e4df..95895a2c 100644 --- a/docs/cn/_sidebar.md +++ b/docs/cn/_sidebar.md @@ -21,6 +21,7 @@ - [Spring-Webflux项目集成](cn/sample-spring-webflux.md) - [Javalin项目集成](cn/sample-javalin.md) - [Ktor项目集成](cn/sample-ktor.md) + - [Micronaut项目集成](cn/sample-micronaut.md) - 其它 - [设计文档](cn/design.md) diff --git a/docs/cn/quickstart.md b/docs/cn/quickstart.md index b91e7c0d..c0fc8693 100644 --- a/docs/cn/quickstart.md +++ b/docs/cn/quickstart.md @@ -16,11 +16,11 @@ com.usthe.sureness sureness-core - 1.0.2 + 1.0.3 ``` ``` -compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.2' +compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.3' ``` #### 🐵 使用默认配置来配置sureness diff --git a/docs/cn/sample-micronaut.md b/docs/cn/sample-micronaut.md new file mode 100644 index 00000000..fce58972 --- /dev/null +++ b/docs/cn/sample-micronaut.md @@ -0,0 +1,419 @@ +# Using Sureness to protect the security of micronaut REST API + +Using Sureness to secure micronaut REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC) + + +## What You Will Learn + +* Creating a simple REST API using micronaut +* Learn how to integrate Sureness into a micronaut application +* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API +* Test API authorization - use different users to verify that they can access the REST API + + +The tutorial assumes that you know what JWT, Basic Auth, Digest Auth, RBAC are. If you +do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction. + +## Setting Up Dependencies + +First, you will need to create a maven project and add micronautn, Sureness dependencies coordinate + +```` + + + 8 + 1.8 + 1.8 + jar + 1.8 + 2.4.3 + 1.1.8 + 3.8.1 + com.usthe.sureness.micronaut.Application + netty + + + + + + io.micronaut + micronaut-bom + ${micronaut.version} + pom + import + + + + + + + org.slf4j + slf4j-simple + 1.7.30 + + + com.usthe.sureness + sureness-core + + + io.micronaut + micronaut-inject + compile + + + io.micronaut + micronaut-validation + compile + + + ch.qos.logback + logback-classic + runtime + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + io.micronaut.test + micronaut-test-junit5 + test + + + io.micronaut + micronaut-http-client + compile + + + io.micronaut + micronaut-http-server-netty + compile + + + io.micronaut + micronaut-runtime + compile + + + + + + + + io.micronaut.build + micronaut-maven-plugin + ${micronaut-maven-plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + io.micronaut + micronaut-inject-java + ${micronaut.version} + + + io.micronaut + micronaut-validation + ${micronaut.version} + + + + -Amicronaut.processing.group=com.usthe.sureness + -Amicronaut.processing.module=micronaut-sureness + + + + + + + + +```` + + +- [User Guide](https://docs.micronaut.io/2.4.3/guide/index.html) +- [API Reference](https://docs.micronaut.io/2.4.3/api/index.html) +- [Configuration Reference](https://docs.micronaut.io/2.4.3/guide/configurationreference.html) +- [Micronaut Guides](https://guides.micronaut.io/index.html) + +We need to create a simple micronautn app and provide some REST API for test. + + + +## Setting Up Sureness + +#### 1.Run Micronaut Application + +``` + +import io.micronaut.runtime.Micronaut; + + +public class Application{ + + public static void main(String[] args) { + Micronaut.run(Application.class, args); + + } +} + +``` + +#### 2. Config Document Datasource - `sureness.yml` + +Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`. + +Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`. eg: + +````yaml +## -- sureness.yml document dataSource-- ## + +# load api resource which need be protected, config role who can access these resource. +# resources that are not configured are also authenticated and protected by default, but not authorized +# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3 +# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role +resourceRole: + - /api/v1/source1===get===[role2] + - /api/v1/source1===post===[role1] + - /api/v1/source1===delete===[role3] + - /api/v1/source1===put===[role1,role2] + - /api/v1/source2===get===[] + - /api/v2/host===post===[role2,role3] + - /api/v2/host===get===[role2,role3] + - /api/v2/host===delete===[role2,role3] + - /api/v2/host===put===[role2,role3] + - /api/v3/*===*===[role1,role2,role3] + +# load api resource which do not need be protected, means them need be excluded. +# these api resource can be access by everyone +excludedResource: + - /api/v3/host===get + - /**/*.html===get + - /**/*.js===get + - /**/*.css===get + - /**/*.ico===get + +# account info +# there are three account: admin, root, tom +# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500 +# eg: root has role1, unencrypted password is 23456 +# eg: tom has role3, unencrypted password is 32113 +account: + - appId: admin + # if add salt, the password is encrypted password - the result: MD5(password+salt) + # digest auth not support encrypted password + # if no salt, the password is unencrypted password + credential: 0192023A7BBD73250516F069DF18B500 + salt: 123 + role: [role1,role2] + - appId: root + credential: 23456 + role: [role1,role2] + - appId: tom + credential: 32113 + role: [role3] + +```` + + + +#### 3. Add an Interceptor Intercepting All Requests + +The essence of sureness is to intercept all rest requests for authenticating and authorizing. The interceptor can be a filter or interceptor, it intercepts all request to check them. In Micronaut, we use Filter +```java +@Filter("/**") +public class MicronautSurenessFilterExample implements HttpServerFilter { + + private static final Logger logger = LoggerFactory.getLogger(MicronautSurenessFilterExample.class); + + @Inject + private SurenessSecurityManager securityManager ; + + + @Override + public Publisher> doFilter(HttpRequest request, + ServerFilterChain chain) { + Integer statusCode = null; + String errorMsg = null; + try { + SubjectSum subject =securityManager.checkIn(request); + if (subject != null) { + SurenessContextHolder.bindSubject(subject); + } + } catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) { + logger.debug("this request is illegal"); + statusCode = HttpStatus.BAD_REQUEST.getCode(); + errorMsg = e4.getMessage(); + } catch (DisabledAccountException | ExcessiveAttemptsException e2 ) { + logger.debug("the account is disabled"); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e2.getMessage(); + } catch (IncorrectCredentialsException | ExpiredCredentialsException e3) { + logger.debug("this account credential is incorrect or expired"); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e3.getMessage(); + } catch (UnauthorizedException e5) { + logger.debug("this account can not access this resource"); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e5.getMessage(); + } catch (RuntimeException e) { + logger.error("other exception happen: ", e); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e.getMessage(); + } + if (statusCode != null && errorMsg != null) { + String finalErrorMsg = errorMsg; + Integer finalStatusCode = statusCode; + logger.info(statusCode+"--->"+errorMsg); + try { + URI location = new URI("/auth/error"); + request = request.mutate().headers(httpHeaders -> { + httpHeaders.add("statusCode", String.valueOf(finalStatusCode)); + httpHeaders.add("errorMsg", finalErrorMsg); + }).uri(location); + }catch (URISyntaxException e){ + logger.error("uri error"); + } + } + return chain.proceed(request); + } + + + + @Override + public int getOrder() { + return ServerFilterPhase.SECURITY.order(); + } + + +} +``` +SurenessSecurityManager configuration + +```java +import io.micronaut.context.annotation.Factory; + +@Factory +public class SurenessConfiguration { + private static final Logger logger = LoggerFactory.getLogger(SurenessConfiguration.class); + + @Factory + public SurenessSecurityManager init() { + SurenessAccountProvider accountProvider = new DocumentAccountProvider(); + List processorList = new LinkedList<>(); + NoneProcessor noneProcessor = new NoneProcessor(); + processorList.add(noneProcessor); + PasswordProcessor passwordProcessor = new PasswordProcessor(); + passwordProcessor.setAccountProvider(accountProvider); + processorList.add(passwordProcessor); + DefaultProcessorManager processorManager = new DefaultProcessorManager(processorList); + if (logger.isDebugEnabled()) { + logger.debug("DefaultProcessorManager init"); + } + PathTreeProvider pathTreeProvider = new DocumentPathTreeProvider(); + DefaultPathRoleMatcher pathRoleMatcher = new DefaultPathRoleMatcher(); + pathRoleMatcher.setPathTreeProvider(pathTreeProvider); + pathRoleMatcher.buildTree(); + if (logger.isDebugEnabled()) { + logger.debug("DefaultPathRoleMatcher init"); + } + + // SubjectFactory init + SubjectFactory subjectFactory = new SurenessSubjectFactory(); + List subjectCreates = Arrays.asList( + new NoneSubjectReactiveCreator(), + new BasicSubjectReactiveCreator()); + subjectFactory.registerSubjectCreator(subjectCreates); + if (logger.isDebugEnabled()) { + logger.debug("SurenessSubjectFactory init"); + } + + // surenessSecurityManager init + SurenessSecurityManager securityManager = SurenessSecurityManager.getInstance(); + securityManager.setPathRoleMatcher(pathRoleMatcher); + securityManager.setSubjectFactory(subjectFactory); + securityManager.setProcessorManager(processorManager); + if (logger.isDebugEnabled()) { + logger.debug("SurenessSecurityManager init"); + } + return securityManager; + } + +} + + +``` + +#### 4. Last, Implement Auth Exception Handling Process + +Sureness uses exception handling process: + +- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information. +- If auth failure, method - `checkIn()` will throw different types of auth exceptions. + +We need to continue the subsequent process based on these exceptions.(eg: return the request response) + +Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something: + +```` +// when auth error , add error msg to HttpRequest + if (statusCode != null && errorMsg != null) { + String finalErrorMsg = errorMsg; + Integer finalStatusCode = statusCode; + logger.info(statusCode+"--->"+errorMsg); + try { + URI location = new URI("/auth/error"); + request = request.mutate().headers(httpHeaders -> { + httpHeaders.add("statusCode", String.valueOf(finalStatusCode)); + httpHeaders.add("errorMsg", finalErrorMsg); + }).uri(location); + }catch (URISyntaxException e){ + logger.error("uri error"); + } + } + +```` + + +**All done, we can test now!** + +## Test + +Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support? +This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api. + +Let's test it. (we use postman and chrome to test.) + +### Test Authentication + +#### 1. Basic Auth Test + +Use postman Basic auth, as shown below: + +* success - input username: admin, password: admin + +![success](../_images/micronaut/success.png) + + +* fail - input username: admin, password: admin1234 + +![fail](../_images/micronaut/error.png) + + +## Conclusion + +micronaut is a framework dedicated to simplicity and ease of use, and so is Sureness. +We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc. +Finally, thank you again for reading. + +[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness) \ No newline at end of file diff --git a/docs/quickstart.md b/docs/quickstart.md index 78a5a65e..48019f8f 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -17,11 +17,11 @@ When use maven or gradle build project, add coordinate com.usthe.sureness sureness-core - 1.0.2 + 1.0.3 ``` ``` -compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.2' +compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.3' ``` #### 🐵 Use the Default Configuration to Configure Sureness diff --git a/docs/sample-micronaut.md b/docs/sample-micronaut.md new file mode 100644 index 00000000..3eb51cff --- /dev/null +++ b/docs/sample-micronaut.md @@ -0,0 +1,419 @@ +# Using Sureness to protect the security of micronaut REST API + +Using Sureness to secure micronaut REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC) + + +## What You Will Learn + +* Creating a simple REST API using micronaut +* Learn how to integrate Sureness into a micronaut application +* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API +* Test API authorization - use different users to verify that they can access the REST API + + +The tutorial assumes that you know what JWT, Basic Auth, Digest Auth, RBAC are. If you +do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction. + +## Setting Up Dependencies + +First, you will need to create a maven project and add micronautn, Sureness dependencies coordinate + +```` + + + 8 + 1.8 + 1.8 + jar + 1.8 + 2.4.3 + 1.1.8 + 3.8.1 + com.usthe.sureness.micronaut.Application + netty + + + + + + io.micronaut + micronaut-bom + ${micronaut.version} + pom + import + + + + + + + org.slf4j + slf4j-simple + 1.7.30 + + + com.usthe.sureness + sureness-core + + + io.micronaut + micronaut-inject + compile + + + io.micronaut + micronaut-validation + compile + + + ch.qos.logback + logback-classic + runtime + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + io.micronaut.test + micronaut-test-junit5 + test + + + io.micronaut + micronaut-http-client + compile + + + io.micronaut + micronaut-http-server-netty + compile + + + io.micronaut + micronaut-runtime + compile + + + + + + + + io.micronaut.build + micronaut-maven-plugin + ${micronaut-maven-plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + io.micronaut + micronaut-inject-java + ${micronaut.version} + + + io.micronaut + micronaut-validation + ${micronaut.version} + + + + -Amicronaut.processing.group=com.usthe.sureness + -Amicronaut.processing.module=micronaut-sureness + + + + + + + + +```` + + +- [User Guide](https://docs.micronaut.io/2.4.3/guide/index.html) +- [API Reference](https://docs.micronaut.io/2.4.3/api/index.html) +- [Configuration Reference](https://docs.micronaut.io/2.4.3/guide/configurationreference.html) +- [Micronaut Guides](https://guides.micronaut.io/index.html) + +We need to create a simple micronautn app and provide some REST API for test. + + + +## Setting Up Sureness + +#### 1.Run Micronaut Application + +``` + +import io.micronaut.runtime.Micronaut; + + +public class Application{ + + public static void main(String[] args) { + Micronaut.run(Application.class, args); + + } +} + +``` + +#### 2. Config Document Datasource - `sureness.yml` + +Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`. + +Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`. eg: + +````yaml +## -- sureness.yml document dataSource-- ## + +# load api resource which need be protected, config role who can access these resource. +# resources that are not configured are also authenticated and protected by default, but not authorized +# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3 +# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role +resourceRole: + - /api/v1/source1===get===[role2] + - /api/v1/source1===post===[role1] + - /api/v1/source1===delete===[role3] + - /api/v1/source1===put===[role1,role2] + - /api/v1/source2===get===[] + - /api/v2/host===post===[role2,role3] + - /api/v2/host===get===[role2,role3] + - /api/v2/host===delete===[role2,role3] + - /api/v2/host===put===[role2,role3] + - /api/v3/*===*===[role1,role2,role3] + +# load api resource which do not need be protected, means them need be excluded. +# these api resource can be access by everyone +excludedResource: + - /api/v3/host===get + - /**/*.html===get + - /**/*.js===get + - /**/*.css===get + - /**/*.ico===get + +# account info +# there are three account: admin, root, tom +# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500 +# eg: root has role1, unencrypted password is 23456 +# eg: tom has role3, unencrypted password is 32113 +account: + - appId: admin + # if add salt, the password is encrypted password - the result: MD5(password+salt) + # digest auth not support encrypted password + # if no salt, the password is unencrypted password + credential: 0192023A7BBD73250516F069DF18B500 + salt: 123 + role: [role1,role2] + - appId: root + credential: 23456 + role: [role1,role2] + - appId: tom + credential: 32113 + role: [role3] + +```` + + + +#### 3. Add an Interceptor Intercepting All Requests + +The essence of sureness is to intercept all rest requests for authenticating and authorizing. The interceptor can be a filter or interceptor, it intercepts all request to check them. In Micronaut, we use Filter +```java +@Filter("/**") +public class MicronautSurenessFilterExample implements HttpServerFilter { + + private static final Logger logger = LoggerFactory.getLogger(MicronautSurenessFilterExample.class); + + @Inject + private SurenessSecurityManager securityManager ; + + + @Override + public Publisher> doFilter(HttpRequest request, + ServerFilterChain chain) { + Integer statusCode = null; + String errorMsg = null; + try { + SubjectSum subject =securityManager.checkIn(request); + if (subject != null) { + SurenessContextHolder.bindSubject(subject); + } + } catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) { + logger.debug("this request is illegal"); + statusCode = HttpStatus.BAD_REQUEST.getCode(); + errorMsg = e4.getMessage(); + } catch (DisabledAccountException | ExcessiveAttemptsException e2 ) { + logger.debug("the account is disabled"); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e2.getMessage(); + } catch (IncorrectCredentialsException | ExpiredCredentialsException e3) { + logger.debug("this account credential is incorrect or expired"); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e3.getMessage(); + } catch (UnauthorizedException e5) { + logger.debug("this account can not access this resource"); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e5.getMessage(); + } catch (RuntimeException e) { + logger.error("other exception happen: ", e); + statusCode = HttpStatus.FORBIDDEN.getCode(); + errorMsg = e.getMessage(); + } + if (statusCode != null && errorMsg != null) { + String finalErrorMsg = errorMsg; + Integer finalStatusCode = statusCode; + logger.info(statusCode+"--->"+errorMsg); + try { + URI location = new URI("/auth/error"); + request = request.mutate().headers(httpHeaders -> { + httpHeaders.add("statusCode", String.valueOf(finalStatusCode)); + httpHeaders.add("errorMsg", finalErrorMsg); + }).uri(location); + }catch (URISyntaxException e){ + logger.error("uri error"); + } + } + return chain.proceed(request); + } + + + + @Override + public int getOrder() { + return ServerFilterPhase.SECURITY.order(); + } + + +} +``` +SurenessSecurityManager configuration + +```java +import io.micronaut.context.annotation.Factory; + +@Factory +public class SurenessConfiguration { + private static final Logger logger = LoggerFactory.getLogger(SurenessConfiguration.class); + + @Factory + public SurenessSecurityManager init() { + SurenessAccountProvider accountProvider = new DocumentAccountProvider(); + List processorList = new LinkedList<>(); + NoneProcessor noneProcessor = new NoneProcessor(); + processorList.add(noneProcessor); + PasswordProcessor passwordProcessor = new PasswordProcessor(); + passwordProcessor.setAccountProvider(accountProvider); + processorList.add(passwordProcessor); + DefaultProcessorManager processorManager = new DefaultProcessorManager(processorList); + if (logger.isDebugEnabled()) { + logger.debug("DefaultProcessorManager init"); + } + PathTreeProvider pathTreeProvider = new DocumentPathTreeProvider(); + DefaultPathRoleMatcher pathRoleMatcher = new DefaultPathRoleMatcher(); + pathRoleMatcher.setPathTreeProvider(pathTreeProvider); + pathRoleMatcher.buildTree(); + if (logger.isDebugEnabled()) { + logger.debug("DefaultPathRoleMatcher init"); + } + + // SubjectFactory init + SubjectFactory subjectFactory = new SurenessSubjectFactory(); + List subjectCreates = Arrays.asList( + new NoneSubjectReactiveCreator(), + new BasicSubjectReactiveCreator()); + subjectFactory.registerSubjectCreator(subjectCreates); + if (logger.isDebugEnabled()) { + logger.debug("SurenessSubjectFactory init"); + } + + // surenessSecurityManager init + SurenessSecurityManager securityManager = SurenessSecurityManager.getInstance(); + securityManager.setPathRoleMatcher(pathRoleMatcher); + securityManager.setSubjectFactory(subjectFactory); + securityManager.setProcessorManager(processorManager); + if (logger.isDebugEnabled()) { + logger.debug("SurenessSecurityManager init"); + } + return securityManager; + } + +} + + +``` + +#### 4. Last, Implement Auth Exception Handling Process + +Sureness uses exception handling process: + +- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information. +- If auth failure, method - `checkIn()` will throw different types of auth exceptions. + +We need to continue the subsequent process based on these exceptions.(eg: return the request response) + +Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something: + +```` +// when auth error , add error msg to HttpRequest + if (statusCode != null && errorMsg != null) { + String finalErrorMsg = errorMsg; + Integer finalStatusCode = statusCode; + logger.info(statusCode+"--->"+errorMsg); + try { + URI location = new URI("/auth/error"); + request = request.mutate().headers(httpHeaders -> { + httpHeaders.add("statusCode", String.valueOf(finalStatusCode)); + httpHeaders.add("errorMsg", finalErrorMsg); + }).uri(location); + }catch (URISyntaxException e){ + logger.error("uri error"); + } + } + +```` + + +**All done, we can test now!** + +## Test + +Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support? +This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api. + +Let's test it. (we use postman and chrome to test.) + +### Test Authentication + +#### 1. Basic Auth Test + +Use postman Basic auth, as shown below: + +* success - input username: admin, password: admin + +![success](_images/micronaut/success.png) + + +* fail - input username: admin, password: admin1234 + +![fail](_images/micronaut/error.png) + + +## Conclusion + +micronaut is a framework dedicated to simplicity and ease of use, and so is Sureness. +We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc. +Finally, thank you again for reading. + +[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness) \ No newline at end of file diff --git a/pom.xml b/pom.xml index a63f189d..19b93b91 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ UTF-8 - 1.0.2 + 1.0.3 1.8 1.8 1.8