使用Spring Security OAuth 2.0和JWT进行移动原生社交注册

时间:2018-02-01 02:57:46

标签: spring-boot single-sign-on jwt spring-security-oauth2 spring-security-rest

我已使用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. 如果令牌有效,则用户无法登录,则会引发错误。
  4. 如果令牌有效,我会从社交服务获取用户详细信息,并使用它在我的数据存储中创建一个“社交用户”,该用户将绑定到现有或新的系统用户。
  5. 使用社交用户创建系统用户后,或社交用户与现有系统用户绑定后,JWT令牌将被发送回移动应用程序。
  6. 此JWT令牌应类似于Spring Security OAuth 2.0密码授予流创建的JWT令牌,并且在授权用户时应由我的ResourceServerConfiguration接受。
  7. 我在网上搜索了符合我标准的解决方案,但我找不到任何解决方案。我的要求合理吗?有没有更简单的方法,允许用户通过用户名/密码和社交媒体身份验证登录,同时取回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;
      }
    }
    

0 个答案:

没有答案