This is an automated email from the ASF dual-hosted git repository. liubao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/servicecomb-fence.git
commit 46de410996ac63012ed1237acf21e37eae3b321f Author: liubao <[email protected]> AuthorDate: Fri May 24 17:19:49 2019 +0800 [SCB-1292]Add developers guide for servicecomb-fence and fix authSignatureVerifier --- README.md | 108 +++------- .../server/RefreshTokenTokenGranter.java | 6 +- .../authentication/edge/AuthHandler.java | 2 +- .../resource/ResourceAuthHandler.java | 2 +- docs/en_US/developersGuide.md | 1 + docs/zh_CN/developersGuide.md | 219 +++++++++++++++++++++ .../AuthenticationConfiguration.java | 9 +- samples/EdgeService/pom.xml | 4 - .../gateway/AuthenticationConfiguration.java | 4 +- .../resource/AuthenticationConfiguration.java | 6 +- 10 files changed, 258 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index c91d67e..8befb72 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,54 @@ -This project demonstrates authentications and authorizations based on JWT/OAuth2. Projct names follow OAuth2 architecture. +# English -## Implementations +This project is servicecomb-java-chassis security support. The main architecture is based on [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749) and [OpenID Connect](https://openid.net/connect/). And provides APIs for developers based on [Spring Security](https://spring.io/projects/spring-security). Please see [developers guide](docs/en_US/developersGuide.md) for details. -This project uses spring security API and mainly designed for ServiceComb architecture. +## Project description -* User Management +This project contains two folders api and samples. Api folder contains components used in Authentication Server, Edge Service and Resource Server. And samples folder gives a working example showing how to use these apis. - 1. UserDetailsService: load users information - 2. UserDetails: User information - 3. GrantedAuthority: authorities - 4. PasswordEncoder: encode or verify user password +* Build and run -## Project - -* AuthenticationServer - -Authentication server implementation. Provides APIs to login, and query roles, etc. - - -* Gateway - -Check if users are authenticated and dispatch HTTP request. - -* Client - -Demonstrates how client uses this project. Integration tests are provided. - - -* Api -Reusable part. +``` +cd samples +mvn clean install +``` -* For testing +After build, the Authentication Server, Resource Server, Edge Service and Testing Client runnable jar are generated, start and run the four services. -Run AuthenticationServer、Gateway、Client、ResourceServer and call +* Run tests +After services are started, try ``` http://localhost:9093/v1/test/start ``` -see AuthenticationTestCase for details. - - -本项目提供认证鉴权服务的实现,主要提供了基于角色的权限管理,和基于JWT的微服务授权模式。微服务的命名参考了OAuth2协议里面的命名方式。可以参考[OAuth2.0原理和验证流程分析](https://www.jianshu.com/p/d74ce6ca0c33)对于OAuth2认证过程的介绍,本项目的认证过程非常类似OAuth2的密码模式。 +see AuthenticationTestCase for testing details. -项目的目标是提供一个商业可用的鉴权实现,对于项目代码实现的问题可以提交issue,本项目也接纳PR,共同完善。 +# 中文 +本项目为servicecomb-java-chassis提供认证鉴权支持。鉴权实现的主要框架参考了[The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749) 和 [OpenID Connect](https://openid.net/connect/)。项目参考[Spring Security](https://spring.io/projects/spring-security)给开发者提供了接口。请参考[开发指南](docs/zh_CN/developersGuide.md)获取详细信息。 +## 项目说明 -## 实现说明 +项目包含了api和samples两个目录。其中api目录主要提供给Authentication Server, Edge Service and Resource Server使用的api。 samples目录是基于上诉api提供的一个开发示例。 -* 用户管理 -用户管理采用了org.springframework.security.core.userdetails的模型,包括: - 1. UserDetailsService:加载用户信息。 - 2. UserDetails:用户信息。 - 3. GrantedAuthority:角色信息。 - 4. PasswordEncoder:用户密码加密和匹配。 - - -## 项目结构介绍 +* 编译和运行 -* AuthenticationServer -认证鉴权服务。提供用户管理、角色管理。并提供登录认证、权限查询等接口。鉴权服务及相关API是核心交付件,也是能够被重用的部分。开发者可以基于这个项目开发认证鉴权服务。 - -* Gateway -提供请求拦截,校验用户是否已经经过认证。一方面演示网关如何和配套鉴权服务完成开发,本项目也是自动化测试的组成部分。 - -* Client -Client模拟的是使用使用者。一方面演示客户端如何获取Token,本项目也是自动化测试的组成部分。 - -* ResourceServer -ResourceServer模拟的是业务服务。一方面演示业务服务如何进行权限配置,本项目也是自动化测试的组成部分。 - -* Api -认证鉴权提取的公共功能,作为复用单元。目前项目处于初始阶段,很多复用代码分散在其他项目中。 +``` +cd samples +mvn clean install +``` +编译完成后,会生成Authentication Server, Resource Server, Edge Service and Testing Client可执行jar包,运行这四个服务。 -* 测试介绍 +* 运行测试用例 -本项目实现了微服务架构的自动化测试。启动AuthenticationServer、Gateway、Client、ResourceServer后,可以提供 +当四个服务都运行起来后,访问: ``` http://localhost:9093/v1/test/start ``` -触发测试用例的执行。 所有的测试用例放到Client微服务里面, 这个微服务实现了简单的测试框架帮助书写测试用例,对测试结果进行检查等功能。 - -测试项目同时展示了这个项目的功能,比如: AuthenticationTestCase 的测试逻辑展示了基本的认证功能,从登陆,到接口的权限检查。 - -# TODO LIST -1. provide TLS for authentication server & edge service -2. grant scope for INTERNAL access & EXTERNAL access -3. access token support: a. use access token to get optional scope or roles token. 这个可以解决角色过多的时候, token过大的一些问题 -4. 实现注销逻辑(会话管理) -5. 支持分层的角色机制 - - ROLE_LEVEL1 - / \ - ROLE_LEVEL2 ROLE_LEVEL2 - - TOKEN里面只返回ROLE_LEVEL1,设置为ROLE_LEVEL2访问的操作,也可以访问。 - -6. REFRESH_TOKEN可以用来实现申请不同SCOPE的TOKEN。 -7. 设计目标:无状态。认证服务器和资源服务器均可以多实例部署,每个实例之间不共享状态。在实现很多功能的时候,都遵循这个约束。包括通过refresh token获取新的access token的时候。遵循这个约束,意味着请求需要同时传递refresh token和access token。 -8, 重新设计TOKEN(代码重构、支持会话管理),支持OpenID Connect。 - -OAUTH的不好的地方:TOKEN在有效期内,容易被利用,无法注销;TOKEN过期后,必须重新认证,和用户是否在一直操作无关,体验不好,虽然可以通过refresh_token获取新的token提升体验,但是refresh_token有效期如果设置的太长,会降低安全性。Token在有效期内,如果修改了权限等信息,无法及时感知,需要重新登录。 -OAUTH的好的地方:TOKEN签发、认证都可以由微服务实例独自完成,不需要共用的数据存储,比如数据库、Redis等,效率更高,弹性扩容。 +可以通过查看AuthenticationTestCase了解测试用例的详情。 diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java index ffbf9c5..7a41e90 100644 --- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java +++ b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java @@ -25,8 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.jwt.Jwt; import org.springframework.security.jwt.JwtHelper; +import org.springframework.security.jwt.crypto.sign.SignatureVerifier; import org.springframework.security.jwt.crypto.sign.Signer; -import org.springframework.security.jwt.crypto.sign.SignerVerifier; import org.springframework.stereotype.Component; import com.netflix.config.DynamicPropertyFactory; @@ -34,8 +34,8 @@ import com.netflix.config.DynamicPropertyFactory; @Component(value = "fefreshTokenTokenGranter") public class RefreshTokenTokenGranter implements TokenGranter { @Autowired - @Qualifier("authSignerVerifier") - private SignerVerifier signerVerifier; + @Qualifier("authSignatureVerifier") + private SignatureVerifier signerVerifier; @Autowired @Qualifier("authSigner") diff --git a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java b/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java index 40ef32d..abf4587 100644 --- a/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java +++ b/api/edge-service/service/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java @@ -38,7 +38,7 @@ public class AuthHandler implements Handler { } Jwt jwt = JwtHelper.decode(token); try { - jwt.verifySignature(BeanUtils.getBean("authSignerVerifier")); + jwt.verifySignature(BeanUtils.getBean("authSignatureVerifier")); } catch (InvalidSignatureException e) { asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated")); return; diff --git a/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java b/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java index 1d8e32b..68c6960 100644 --- a/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java +++ b/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java @@ -59,7 +59,7 @@ public class ResourceAuthHandler implements Handler { Jwt jwt = JwtHelper.decode(token); JWTClaims claims; try { - jwt.verifySignature(BeanUtils.getBean("authSignerVerifier")); + jwt.verifySignature(BeanUtils.getBean("authSignatureVerifier")); claims = JsonParser.parse(jwt.getClaims(), JWTClaims.class); // TODO: verify claims. } catch (Exception e) { diff --git a/docs/en_US/developersGuide.md b/docs/en_US/developersGuide.md new file mode 100644 index 0000000..f87f5c1 --- /dev/null +++ b/docs/en_US/developersGuide.md @@ -0,0 +1 @@ +# TODO \ No newline at end of file diff --git a/docs/zh_CN/developersGuide.md b/docs/zh_CN/developersGuide.md new file mode 100644 index 0000000..013f98f --- /dev/null +++ b/docs/zh_CN/developersGuide.md @@ -0,0 +1,219 @@ +# servicecomb-fence 开发指南 + +开发者可以使用 servicecomb-fence 给 servicecomb-java-chassis 微服务项目增加基于 OpenID Connect 的认证鉴权能力。 + +## 认证鉴权流程介绍 + +### 密码模式 + + * Client 输入用户名密码向 Authentication Server 请求 Token。 + +``` +** HTTP Request ** + +POST http://localhost:9090/api/authentication-server/v1/oauth/token HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +grant_type=password&username=admin&password=changeMyPassword +``` + + * Authentication Server 发送 Token 给 Client 。 + + * Client 携带 Token 请求 Resource Server 。 + +``` +** HTTP Request ** + +POST http://localhost:9090/api/resource-server/v1/auth/handler/adminSayHello?name=Hi HTTP/1.1 +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW +``` + + * Resource Server 返回对应的资源给 Client 。 + +## 开发 Authentication Server + +Authentication Server 主要提供认证和授权等接口。 + +* 配置依赖 + +项目中引入 + +``` + <dependency> + <groupId>org.apache.servicecomb.authentication</groupId> + <artifactId>authentication-server-api-endpoint</artifactId> + </dependency> +``` + +* 配置 + +Authentication Server 需要配置 PasswordEncoder、Signer、SignerVerifier、UserDetailsService 等。这些对象和 Spring Security的概念一样。 +``` +@Configuration +public class AuthenticationConfiguration { + @Autowired + @Qualifier("authPasswordEncoder") + private PasswordEncoder passwordEncoder; + + @Bean(name = "authPasswordEncoder") + public PasswordEncoder authPasswordEncoder() { + return new Pbkdf2PasswordEncoder(); + } + + @Bean(name = "authSigner") + public Signer authSigner() { + return authSignerVerifier(); + } + + @Bean(name = "authSignerVerifier") + public SignerVerifier authSignerVerifier() { + return new MacSigner("Please change this key."); + } + + @Bean(name = "authUserDetailsService") + public UserDetailsService authUserDetailsService() { + InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); + UserDetails uAdmin = new User("admin", passwordEncoder.encode("changeMyPassword"), + Arrays.asList(new SimpleGrantedAuthority("ADMIN"))); + UserDetails uGuest = new User("guest", passwordEncoder.encode("changeMyPassword"), + Arrays.asList(new SimpleGrantedAuthority("GUEST"))); + manager.createUser(uAdmin); + manager.createUser(uGuest); + return manager; + } +} +``` + +* UserDetailsService + +获取用户详情,包括用户名称、角色等信息。包括 InMemoryUserDetailsManager 、 JdbcUserDetailsManager 等实现。示例项目使用了InMemoryUserDetailsManager , 它不会持久化, 正式项目需要使用 JdbcUserDetailsManager 等。 + +* Signer 和 SignatureVerifier + +生成 Token 和对 Token 进行校验。Singer 和 SignatureVerifier 是配套使用的, 在 Authentication Server , 生成 Token 的时候,需要使用 Singer 。 验证 Token 的有效性 (比如查询 userDetails 等场景), 需要使用 SignatureVerifier 。 通常有两种方式进行签名和校验, 一种是基于对称秘钥的机制,比如MacSigner,即是 Singer, 也是 SignatureVerifier (SignerVerifier); 一种是基于非对称秘钥的机制, 比如 RsaSigner 和 RsaVerifier , 生成 Token 和校验 Token 的秘钥是不同的。 + +* PasswordEncoder + +从 UserDetailsService 校验用户密码的时候需要使用。 开发者需要在加密性能和保密程度方面选择合适的算法。 常用的有 Pbkdf2PasswordEncoder , 它可以设置迭代次数,能够更好的保护密码不被暴力破解。 + +## 开发 Resource Server + +Resource Server 对 Client 的访问进行认证, 并进行权限控制。 + +* 配置依赖 + +项目中引入 + +``` + <dependency> + <groupId>org.apache.servicecomb.authentication</groupId> + <artifactId>authentication-resource-api-endpoint</artifactId> + </dependency> +``` + +* 配置 + +Resource Server 需要配置 SignatureVerifier 等, 对用户会话进行认证。 +``` +@Configuration +public class AuthenticationConfiguration { + @Bean(name = "authSignatureVerifier") + public SignerVerifier authSignatureVerifier() { + return new MacSigner("Please change this key."); + } +} +``` + +* 权限配置 + +fence 提供了两种方式进行权限配置。 一种是基于配置文件的,一种是基于 Annotation 。 + +基于文件的配置, 在 microservice.yaml 中可以配置每个方法的访问权限。 + +``` +servicecomb: + authencation: + access: + needAuth: true + roles: + HandlerAuthEndpoint: + adminSayHello: ADMIN + guestSayHello: GUEST + guestOrAdminSayHello: ADMIN,GUEST + # everyoneSayHello: all can +``` + +还可以统一配置Schema + +``` +servicecomb: + authencation: + access: + needAuth: true + roles: + HandlerAuthEndpoint: ADMIN +``` + +或者所有 + +``` +servicecomb: + authencation: + access: + needAuth: true + roles: ADMIN +``` + +基于 Annotation , 可以使用 PreAuthorize 标签 + +``` +@PreAuthorize("hasRole('ADMIN')") +``` + +Annotation 支持默认没有启用, 可以通过 EnableGlobalMethodSecurity 标签启用。 + +``` +@Configuration +@EnableGlobalMethodSecurity( + prePostEnabled = true) +public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration { + @Override + protected MethodSecurityExpressionHandler createExpressionHandler() { + DefaultMethodSecurityExpressionHandler h = (DefaultMethodSecurityExpressionHandler) super.createExpressionHandler(); + h.setDefaultRolePrefix(""); + return h; + } +} +``` + + +## 开发 Edge Service + +Edge Service 是微服务接入层。 在[单体应用微服务改造](https://bbs.huaweicloud.com/blogs/17ad483f325f11e9bd5a7ca23e93a891)中介绍了基于网关的弹性架构, 这种架构对于微服务持续演进具有重要意义, 因此建议开发者都按照这个[基础架构](https://bbs.huaweicloud.com/blogs/8bb2c3b8366c11e9bd5a7ca23e93a891)搭建微服务。接入层在认证鉴权的功能包括透传 Authentication Server的请求, 将HTTP消息转化为ServiceComb-java-chassis友好的消息格式,对Token进行认证,控制内部接口和外部接口的隔离等功能。 + +* 配置依赖 + +项目中引入 + +``` + <dependency> + <groupId>org.apache.servicecomb.authentication</groupId> + <artifactId>authentication-edge-api-endpoint</artifactId> + </dependency> +``` + +* 配置 + +Edge Service 需要配置 SignatureVerifier 等, 对用户会话进行认证。 +``` + +@Configuration +public class AuthenticationConfiguration { + @Bean(name = "authSignatureVerifier") + public SignerVerifier authSignatureVerifier() { + return new MacSigner("Please change this key."); + } +} + +``` diff --git a/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java b/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java index 2fdbd4c..b70273f 100644 --- a/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java +++ b/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java @@ -30,7 +30,6 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; import org.springframework.security.jwt.crypto.sign.MacSigner; -import org.springframework.security.jwt.crypto.sign.Signer; import org.springframework.security.jwt.crypto.sign.SignerVerifier; import org.springframework.security.provisioning.InMemoryUserDetailsManager; @@ -45,12 +44,8 @@ public class AuthenticationConfiguration { return new Pbkdf2PasswordEncoder(); } - @Bean(name = "authSigner") - public Signer authSigner() { - return authSignerVerifier(); - } - - @Bean(name = "authSignerVerifier") + // If using RSA, need to configure authSigner and authSignatureVerifier separately. + @Bean(name = {"authSigner", "authSignatureVerifier" }) public SignerVerifier authSignerVerifier() { return new MacSigner("Please change this key."); } diff --git a/samples/EdgeService/pom.xml b/samples/EdgeService/pom.xml index cb50f95..e0ecf5c 100644 --- a/samples/EdgeService/pom.xml +++ b/samples/EdgeService/pom.xml @@ -56,10 +56,6 @@ <artifactId>authentication-edge-api-endpoint</artifactId> </dependency> <dependency> - <groupId>org.apache.servicecomb.authentication</groupId> - <artifactId>authentication-server-api-service</artifactId> - </dependency> - <dependency> <groupId>org.apache.servicecomb</groupId> <artifactId>solution-basic</artifactId> </dependency> diff --git a/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java b/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java index 1e913fb..510d821 100644 --- a/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java +++ b/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java @@ -24,8 +24,8 @@ import org.springframework.security.jwt.crypto.sign.SignerVerifier; @Configuration public class AuthenticationConfiguration { - @Bean(name = "authSignerVerifier") - public SignerVerifier authSignerVerifier() { + @Bean(name = "authSignatureVerifier") + public SignerVerifier authSignatureVerifier() { return new MacSigner("Please change this key."); } } diff --git a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java b/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java index 712030d..2074321 100644 --- a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java +++ b/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java @@ -20,12 +20,12 @@ package org.apache.servicecomb.authentication.resource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.jwt.crypto.sign.MacSigner; -import org.springframework.security.jwt.crypto.sign.SignerVerifier; +import org.springframework.security.jwt.crypto.sign.SignatureVerifier; @Configuration public class AuthenticationConfiguration { - @Bean(name = "authSignerVerifier") - public SignerVerifier authSignerVerifier() { + @Bean(name = "authSignatureVerifier") + public SignatureVerifier authSignatureVerifier() { return new MacSigner("Please change this key."); } }
