首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在微服务体系结构中实现基于角色的安全

如何在微服务体系结构中实现基于角色的安全
EN

Stack Overflow用户
提问于 2020-01-17 19:18:21
回答 4查看 4.2K关注 0票数 7

我有一个spring-boot应用程序,它有4个微服务、eureka服务器和一个集中的API网关。

所有外部流量都通过我的API网关到达我的微服务。

我的API网关(Zuul)正在验证和验证JWT token。

JWT令牌是在用户登录后由我的一个微服务(users微服务)生成的,该令牌包含用户Id及其角色/权限。

现在,我想在微服务而不是网关中的方法上实现基于角色的安全性。

我尝试过使用@PreAuthorize,但它不能在网关之外工作(显然,为了让它工作,我必须在我的微服务的SecurityContextHolder中设置一个Spring Security身份验证对象,并用权限填充它)。

那么,有什么解决方案可以实现这种类型的安全性吗?

在微服务架构中设置安全性的最佳设计是什么?

API网关级的身份验证和微服务级的授权?

在API网关级验证JWT之后,我是否需要在微服务中使用spring安全性,或者只传递角色(将它们附加到请求中),例如,创建我自己的注释并使用Spring AOP处理授权?

EN

回答 4

Stack Overflow用户

发布于 2020-07-29 02:37:30

Spring5 microservices中,您将能够找到开发微服务体系结构的基础,这些微服务体系结构具有您正在寻找的几个必要条件:

关于安全性,我开发了两种不同的微服务:

  • Spring Oauth2 with Jwt
  • Spring Jwt多应用程序安全服务,用于访问和刷新Jwt令牌,具有多种自定义功能,如:定义每个令牌的内容,使用JWS或JWE等

最重要的是使用Swagger很好地记录了,正如您可以看到的here,并且所有记录的API都可以使用唯一的网关Url进行访问。

对于每个微服务的所有类,都开发了J单元测试。

安全性

在这一点上,我做出了几个决定:

1.不是验证安全性的微服务的网关。

因为使用网关作为“防火墙”是一种不太灵活的方法。我想决定哪些微服务需要安全性,每个微服务都应该在内部管理可以访问每个端点的角色。总之,每个微服务都必须使用授权/身份验证,但它不需要知道该功能是如何完成的。

2.用于处理安全性的特定微服务

正如我告诉你的,我开发了两个不同的方法,因为我想“玩”不同的选项/方法。最重要的优点是封装,如果“明天”我决定通过任何其他选项更改Jwt,我只需要修改那些选项,使用它们的微服务将保持相同的代码(我将很快向您解释集成是如何完成的)。

安全集成示例

我将解释安全功能是如何在以下组件之间集成的:

作为architecture.

的一部分开发的

1.管理用户和角色的每个应用程序都将在安全微服务中包括一个类似于next one的文件夹,以定义其模型、存储库以获取所需信息等

2.安全微服务的全局端点定义为here。如您所见,它们基本上使用2个Dtos:

主要优点是,只有安全微服务知道有关该功能是如何完成的详细信息,使用它的其他微服务将收到包含所需信息的众所周知的Dtos。

3.在披萨服务中,安全集成主要定义在以下3个类中:

  • SecurityContextRepository从标头中获取授权令牌,并将其发送到带有所提供的“授权令牌”的security-jwt-serviceSecurityManager.
  • SecurityManager调用(它不知道它是Jwt还是其他任何东西),并接收众所周知的UsernameAuthoritiesDto (将其转换为Spring类UsernamePasswordAuthenticationToken)
  • WebSecurityConfiguration全局安全configuration.

的对象

现在,您可以在端点中包含所需的基于角色的安全性:

最后的注意事项

  1. pizza-service是使用Webflux开发的,您可以在order-service here中看到基于MVC微服务的等效集成(在本例中,我使用了“其他安全服务”,但很容易适应它)。

  1. 为了提高安全性并遵循"Oauth方法“,对security-jwt-service的请求也需要包括基本身份验证。正如您在SecurityManager类中看到的:

private String buildAuthorizationHeader(String用户名,String密码){ String auth =用户名+ ":“+密码;byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());return "Basic”+新字符串(EncodedAuth);}

数据库中用于存储该信息的表与用于管理每个应用程序的安全配置的表是相同的:security.jwt_client_details

票数 4
EN

Stack Overflow用户

发布于 2020-07-28 01:18:00

目前的问题很广泛,因为您微服务的流量性质尚不清楚。

通过您的API网关将所有外部流量

  • Assuming到您的microservices.

代码语言:javascript
复制
- You don't need to validate the JWT twice once in API gateway and then again in your internal microservice. If the JWT is invalid , the request will never reach your microservice
- Then API gateway propagate the roles. In your microservice, you initialise the spring security context using the roles passed in the header. It will allow you to use `@PreAuthorize` etc

  • Assuming外部流量可以通过您的API网关,也可以直接到达您的microservices.

代码语言:javascript
复制
- Now you need to verify it in both API gateway and in your microservices

更新

  • 我对Zuul API网关一无所知。这只是为了解决以下问题:

我尝试过使用@PreAuthorize,但它不能在网关外工作(显然,为了让它工作,我必须在我的微服务的SecurityContextHolder中设置一个Spring Security身份验证对象,并用权限填充它)。

代码语言:javascript
复制
    public class PreAuthenticatedUserRoleHeaderFilter 
        extends GenericFilterBean {

    public void doFilter(ServletRequest servletRequest, 
                         ServletResponse servletResponse,
                         FilterChain chain) 
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String rolesString = //extract the roles
        String userName = // extract the username

        List<GrantedAuthority> authorities 
           = AuthorityUtils.commaSeparatedStringToAuthorityList(rolesString);


        PreAuthenticatedAuthenticationToken authentication 
                = new PreAuthenticatedAuthenticationToken(
                                    userName, null, authorities);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(servletRequest, servletResponse);
    }
}
代码语言:javascript
复制
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, 
        jsr250Enabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        PreAuthenticatedUserRoleHeaderFilter authFilter
                = new PreAuthenticatedUserRoleHeaderFilter();
        http.
                antMatcher("/**")
                .csrf()
                .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilterBefore(authFilter, 
                                 BasicAuthenticationFilter.class)
                .authorizeRequests()
                .anyRequest()
                .authenticated();
      }

    }
票数 3
EN

Stack Overflow用户

发布于 2020-07-27 22:43:52

如果您的API网关也是使用私钥创建和签名JWT令牌的网关,并使用API网关的公钥对您进行身份验证,那么您就是指定JWT令牌结构的人,您应该能够将角色编码到该JWT中(例如,它可以是作用域参数,但所有可能的作用域通常都是所有用户都可以访问的)。然后,您可以将spring boot配置为从该JWT (设置SecurityContextHolder角色的权限)自动解析组角色,并且无需任何修改即可使用@PreAuthorize注释。

如果你的API网关只在第三方授权服务器(签署和构造JWT的服务器)上使用这个服务器的公钥来验证JWT令牌,那么你必须实现一些自定义的基于角色的访问机制。我想到的一个办法是实现二级Oauth2身份验证,它只在你的微服务和应用编程接口网关之间使用某种“内部”JWT来处理请求。例如,请参见下图:

由于您通过API网关代码定义了内部JWT结构的外观,因此您可以设置自定义属性,如角色:(admin,user等)。例如,这可以从用户名、id、电子邮件中解析,您可以从第三方授权服务器的外部JWT中获得这些信息。因此,您需要在API网关代码中保留一些映射,如:

代码语言:javascript
复制
(userId: 12563) => Admin group
(userId: 45451) => User group

由于微服务使用JWT进行身份验证,因此可以使用spring boot resource server设置身份验证,并将其配置为从定制的结构化内部JWT自动解析组(您在SecurityContextHolder中提到的对象)。这样你就可以简单地在你的微服务中使用@PreAuthorize注解,因此你不需要创建自定义的注解。注意,这只能解决我在第一种情况中指定的第二种情况,你已经控制了JWT token。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59786338

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档