【问题标题】:Spring Security stateleless JWT authentication - How to get other jwt fields?Spring Security 无状态 JWT 身份验证 - 如何获取其他 jwt 字段?
【发布时间】:2016-04-22 21:25:35
【问题描述】:

我们有一个使用 JWT 在服务之间进行身份验证的微服务架构。我希望轻松地从 JWT 中获取更多字段。目前,Spring Security 直接暴露了当局。

我们的边缘服务/API 网关创建以传递给下游服务的示例 JWT(某些字段是我们的应用自定义的):

{
  "projectId": "project1",
  "group": "client",
  "iss": "login.company.com",
  "aud": "company.com",
  "sub": "testguy",
  "exp": 1461074284992,
  "projectRoleId": "ADMINPAG",
  "contentAccessGroupId": "CAG1",
  "authorities": [
    "client",
    "PROJECT_ADD_USER",
    "PROJECT_ADD_CAG",
    "PROJECT_DOCUMENT_VIEW",
    "PROJECT_EDIT_CAG",
    "PROJECT_LIST_USERS",
    "PROJECT_SEARCH"
  ],
  "user_name": "test@company.com"
}

使用 Spring Boot 实现这一点非常容易:

compile("org.springframework.boot:spring-boot-starter-security:1.3.1")
compile('org.springframework.security.oauth:spring-security-oauth2:2.0.8.RELEASE')
compile('org.springframework.security:spring-security-jwt:1.0.3.RELEASE')

1) 注释应用程序类

@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyApp {

2) 向 application.yml 添加属性以配置忽略的路径和 JWT 密钥:

security:
  basic:
    enabled: false
  oauth2:
    resource.jwt.keyValue: itsasecret
  ignored:
    - /
    - /swagger-ui.html
    - /webjars/**
    - /swagger-resources
    - /swagger/**

这会导致用户在检测到 JWT 时自动登录。我可以用@PreAuthorize("hasAuthority('PROJECT_SEARCH')") 断言他们的权限就好了。

唯一剩下的就是我希望能够从 JWT 中获取其他字段(例如“sub” - 用户 ID)。此信息似乎存储在 OAuth2AccessToken#getAdditionalInformation() 字段中。自定义 UserDetails 实现在这里可以正常工作,但我希望能够直接从 JWT 创建 UserDetails,而不是通过 UserDetailsS​​ervice(根据用户名创建一个)。

我的主要目标是能够从 PreAuthorize 注释中引用其中一些 JWT 字段,例如,断言当前用户的 ID 与“userId”参数中传递的 ID 相同。如果可能的话,如果可以将为此所需的配置更改隔离到共享模块中的 AutoConfiguration 类,我会很高兴,这样我们就不必触及我们所有的服务。 JWT 本身的结构是 100% 灵活的。

【问题讨论】:

    标签: jwt spring-security-oauth2


    【解决方案1】:

    在 Spring Cloud Docs 上找到了解决方案。

    我想要的是一个 JwtAccessTokenConverterConfigurer。

    下面的类是自动检测到的,一旦读取 JWT,就会为你创建一个自定义的 OAuth2Authentication 对象:

    @Component
    public class JwtConverter extends DefaultAccessTokenConverter implements
            JwtAccessTokenConverterConfigurer {
    
        @Override
        public void configure(JwtAccessTokenConverter converter) {
            converter.setAccessTokenConverter(this);
        }
    
        @Override
        public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
            OAuth2Authentication auth = super.extractAuthentication(map);
            MyAppCustomUserDetails details = new MyAppCustomUserDetails();
            //populate details from the provided map, which contains the whole JWT.
            auth.setDetails(details);
            return auth;
        }
    }
    

    这里唯一需要注意的是,我的自定义用户详细信息对象需要额外的一跳才能到达。它实际上是作为默认用户详细信息对象上的一个字段创建的,称为 decodedDetails。我刚刚制作了一个自定义方法参数解析器来帮助检索:

    @Configuration
    @ConditionalOnBean(JwtConverter.class)
    public class WebConfigurer extends WebMvcConfigurerAdapter {
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
            argumentResolvers.add(currentUserHandlerMethodArgumentResolver());
            super.addArgumentResolvers(argumentResolvers);
        }
    
        @Bean
        public HandlerMethodArgumentResolver currentUserHandlerMethodArgumentResolver() {
            return new HandlerMethodArgumentResolver() {
                @Override
                public boolean supportsParameter(MethodParameter parameter) {
                    return parameter.getParameterType().equals(MyAppCustomUserDetails.class);
                }
    
                @Override
                public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
                    return ((OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails()).getDecodedDetails();
                }
            };
        }
    
    }
    

    这仅允许您将自定义用户详细信息作为参数传递给您的控制器,并使其从 JWT 自动填充:

    @RequestMapping("/currentUser")
    public UserInfoResponse getCurrentUser(MyAppCustomUserDetails user){
    //do stuff
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-15
      • 2022-08-16
      • 2019-02-07
      • 2021-08-22
      • 2018-02-27
      • 2016-11-22
      • 2021-01-27
      • 2022-10-05
      • 2019-04-18
      相关资源
      最近更新 更多