6

我已经使用 Spring Security OAuth 2.0 和 JWT 为我的身份验证服务器实现了密码授予。我通过扩展 AuthorizationServerConfigurerAdapter 创建服务器。我可以给服务器一个用户名/密码并取回一个 JWT 令牌。我使用 ResourceConfiguration 来扩展其他服务上的 ResourceServerConfigurerAdapter 类,以在任何时候进行 Web 服务调用时验证 JWT。下面粘贴的是我的代码。

我想让我的 Native Mobile 应用程序能够使用 Facebook、Gmail、Linked 等登录……我想按照所附文章中的步骤进行操作:

https://ole.michelsen.dk/blog/social-signin-spa-jwt-server.html

  1. 用户在移动端执行 OAuth 舞蹈并向我发送他们正在使用的社交服务的访问令牌。
  2. 我收到访问令牌,并使用它来调用相应的社交服务。
  3. 如果 Token 有效,则用户无法登录,会抛出错误。
  4. 如果令牌有效,我会从社交服务中获取用户详细信息,并使用它在我的数据存储中创建一个“社交用户”,该“社交用户”将与现有或新的系统用户相关联。
  5. 一旦使用社交用户创建系统用户,或者将社交用户绑定到现有系统用户,JWT 令牌就会被发送回移动应用程序。
  6. 这个 JWT 令牌应该类似于 Spring Security OAuth 2.0 密码授予流程创建的 JWT 令牌,并且在授权用户时应该被我的 ResourceServerConfiguration 接受。

我在网上搜索了符合我标准的解决方案,但我找不到任何解决方案。我的要求合理吗?有没有更简单的方法来做到这一点,允许用户通过用户名/密码和社交媒体身份验证登录,同时取回 JWT 令牌。我发现的一个示例使用 OAuth2ClientAuthenticationProcessingFilter 来执行我上面提到的逻辑,但我不知道 OAuth2ClientAuthenticationProcessingFilter 是如何工作的,也找不到任何文档。如果有人不得不使用类似的技术堆栈来实现类似的要求,请告诉我您使用什么技术来实现此解决方案。

在身份验证服务器上:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;

    @Value("${clientId}")
    private String clientId;

    @Value("${clientSecret}")
    private String clientSecret;

    @Value("${jwtSigningKey}")
    private String jwtSigningKey;

    @Value("${accessTokenValiditySeconds}")
    private String accessTokenValiditySeconds;

    @Value("${refreshTokenValiditySeconds}")
    private String refreshTokenValiditySeconds;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey(jwtSigningKey);
        return accessTokenConverter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new JWTTokenEnhancer();
    }

    // Added for refresh token capability
    @Bean
    @Primary
    public DefaultTokenServices tokenServices(){
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient(clientId)
                    .secret(clientSecret)
                    .authorizedGrantTypes("password", "refresh_token")
                    .scopes("read","write")
                    .accessTokenValiditySeconds(Integer.valueOf(accessTokenValiditySeconds)) // 1 hour
                    .refreshTokenValiditySeconds(Integer.valueOf(refreshTokenValiditySeconds));// 30 days
    }

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {

        // Add the JWT token enhancer to the token enhancer chain then add to endpoints
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));

        endpoints.tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancerChain)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST)
                .accessTokenConverter(accessTokenConverter());
    }

    @Override
    public void configure(final AuthorizationServerSecurityConfigurer securityConfigurer) throws Exception {
        securityConfigurer.checkTokenAccess("permitAll()");
        super.configure(securityConfigurer);
    }
}

public class JWTTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(final OAuth2AccessToken accessToken,
                                     final OAuth2Authentication authentication) {

        Map<String, Object> additionalInfo = new HashMap<>();

        // Get the user detail implementation
        UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();

        // add userId and roles to the JWT token
        additionalInfo.put("user_id", userDetails.getUserId());
        additionalInfo.put("email", userDetails.getEmail());
        additionalInfo.put("user_name", userDetails.getUsername());
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

        return accessToken;
    }
}

在每个微服务上:

@Configuration
@EnableResourceServer
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@ComponentScan("com.test.security")
@Profile({"prod", "qa", "dev"})
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

  @Value("${jwtSigningKey}")
  private String jwtSigningKey;

  // http security concerns
  @Override
  public void configure(final HttpSecurity http) throws Exception {

    http.authorizeRequests()
        .antMatchers("/swagger-ui.html").permitAll()
        .antMatchers("/hystrix/**").permitAll()
        .antMatchers("/admin/hystrix.stream/**").permitAll()
        .antMatchers("/admin/health/**").permitAll()
        .antMatchers("/admin/info/**").permitAll()
        .antMatchers("/admin/**").authenticated()
        .antMatchers("/greetings/**").authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().csrf().disable();
  }

  @Override
  public void configure(final ResourceServerSecurityConfigurer config) {
    config.tokenServices(tokenServices());
  }

  @Bean
  public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
    accessTokenConverter.setSigningKey(jwtSigningKey);
    return accessTokenConverter;
  }

  @Bean
  public TokenStore tokenStore() {
    return new JwtTokenStore(accessTokenConverter());
  }

  // Added for refresh token capability
  @Bean
  @Primary
  public DefaultTokenServices tokenServices() {
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
    defaultTokenServices.setTokenStore(tokenStore());
    defaultTokenServices.setSupportRefreshToken(true);
    return defaultTokenServices;
  }
}
4

0 回答 0